vcpkg로 설치한 Qt 라이브러리와 Qt Maintenance Tool(공식 Qt 설치 도구)로 설치한 Qt 라이브러리는 기술적으로 동시에 시스템에 존재할 수 있지만, 일반적인 프로젝트에서 동시에 함께 사용하는 것은 권장되지 않으며, 여러 문제가 발생할 수 있습니다.

주요 이유와 주의점은 다음과 같습니다:

  • 경로 충돌 및 우선순위 문제
    • CMake 프로젝트의 경우, vcpkg가 설치한 Qt가 CMAKE_PREFIX_PATH 등에서 우선적으로 인식되어, QtCreator에서 선택한 공식 Qt 버전 대신 vcpkg의 Qt가 자동으로 사용될 수 있습니다
    • QtCreator의 Kit 설정이나 Maintenance Tool로 설치한 Qt가 무시될 수 있습니다
  • Visual Studio 및 Qt VS Tools와의 호환성
    • Qt VS Tools(Visual Studio용 Qt 확장)는 vcpkg로 설치한 Qt의 폴더 구조와 공식 Qt 설치의 구조가 다르기 때문에, vcpkg로 설치한 Qt와 완벽하게 호환되지 않을 수 있습니다
    • 특히 디버그/릴리즈 라이브러리 경로, qmake.exe 위치, mkspecs 등에서 문제가 발생할 수 있습니다.
  • 라이브러리 버전 충돌
    • 두 설치 방식으로 서로 다른 Qt 버전이 설치되어 있다면, 프로젝트에서 예기치 않은 라이브러리 버전이 링크되어 런타임 오류가 발생할 수 있습니다.
    • 특히 Qt의 바이너리 호환성 정책에 따라, 일부 버전 간에는 혼용이 불가능할 수 있습니다.
  • 폴더 구조 및 경로 문제
    vcpkg로 설치한 Qt는 공식 Qt 설치와 폴더 구조와 경로가 다릅니다. 예를 들어, qmake.exe가 위치한 경로가 다르고, 라이브러리와 플러그인, mkspecs 등도 분리되어 있습니다
  • 디버그/릴리즈 라이브러리 분리
    vcpkg는 디버그와 릴리즈 라이브러리를 각각 다른 폴더에 저장하는 반면, 공식 Qt 설치에서는 동일 폴더에 저장하는 경우가 많아 빌드 환경에서 혼란이 생길 수 있습니다
  • 실제 사용 방법
    • 동시에 사용하는 것은 피하고, 하나의 방식(공식 설치 또는 vcpkg 설치)만 사용하는 것이 안정적입니다.
    • 특정 프로젝트에서는 vcpkg의 Qt를, 다른 프로젝트에서는 공식 설치 Qt를 사용하는 방식으로 분리할 수 있지만, 환경 변수 및 빌드 설정에 주의해야 합니다

결론:
vcpkg로 설치한 Qt와 Qt Maintenance Tool로 설치한 Qt는 동시에 시스템에 존재할 수 있지만, 동일한 프로젝트에서 함께 사용하는 것은 권장되지 않으며, 빌드 및 실행 환경에서 예기치 않은 문제가 발생할 수 있습니다. 프로젝트별로 한 가지 설치 방식만 사용하는 것이 가장 안전합니다.

 

참고

https://forum.qt.io/topic/157554/vs-tools-in-vs2022-do-not-work-with-vcpkg-installed-qt6

https://github.com/microsoft/vcpkg/issues/2643

https://forum.qt.io/topic/96818/integrate-qt-vs-tools-with-vcpkg-installed-qt

https://www.qtcentre.org/threads/71952-CMake-vcpkg-qt-takes-precedence-over-onlineinstaller-qt

Qt의 메타-오브젝트 시스템(meta-object system)객체간 통신을 위한 시그널-슬롯 매커니즘과, 런타임 타입 정보, 동적 속성 시스템을 제공한다.

 

메타-오브젝트 시스템은 아래 세 가지를 기반으로 한다:

1. QObject 클래스 : 객체를 위한 기반 클래스를 제공하여 메타-오브젝트 시스템의 이점을 이용할 수 있게 한다.

2. Q_OBJECT 매크로 : 클래스의 private 섹션 안에 선언되며, 동적 속성과 시그널 슬롯 등의 메타-오브젝트 기능을 사용할 수 있게 한다.

3. Meta-Object Compiler(moc) : QObject의 자식 클래스들에게 필요한 메타-오브젝트 구현 코드를 제공한다.

 

moc 툴은 C++ 헤더, 소스 파일 읽어 Q_OBJECT 매크로가 포함된 클래스를 찾고,

이를 찾으면 각 클래스에 대해 메타-오브젝트 코드를 포함하는 또다른 C++ 소스 파일을 생성해낸다.

이렇게 생성된 소스파일은 인클루드, 컴파일되고 클래스 구현과 링크되어 사용된다.

 

예) 헤더파일에 Q_OBJECT 매크로가 있는 클래스가 있다면,

메타-오브젝트를 이용할 수 있는 코드들을 추가한 새로운 헤더 파일을 만들어내고,

이 헤더 파일을 include한 새로운 C++ 구현 파일이 생겨 빌드시에는 이를 컴파일하고 링크하여 사용한다.

 

 

참고

https://doc.qt.io/qt-6/metaobjects.html

https://coding-chobo.tistory.com/9

QTest로 유닛 테스트를 작성하고, CMake로 빌드파일 만들어 Visual Studio로 빌드하는데 아래와 같은 오류 메시지가 뜨는 경우가 있다.

 

오류메시지

error LNK2019: unresolved external symbol _main referenced in function "int __cdecl invoke_main(void)"

 

원인

main 함수가 없어 entry point가 제대로 설정되지 않았기 때문이다

 

해결책

테스트 케이스가 작성된 cpp 파일 하단에, 아래의 내용을 넣어주면 된다.

QTEST_MAIN(MyFirstTest)
#include "tst_myfirsttest.moc"

 

tst_myfirsttest.cpp 전체 소스

#include <QObject>
#include <QTest>
#include <qDebug>


class MyFirstTest : public QObject
{
    Q_OBJECT

private:
    bool myCondition()
    {
        return true;
    }

private slots:
    void initTestCase()
    {
        qDebug("Called before everything else.");
    }

    void myFirstTest()
    {
        QVERIFY(true);  // check that a condition is satisfied
        QCOMPARE(1, 1); // compare two values
    }

    void mySecondTest()
    {
        QVERIFY(myCondition());
        QVERIFY(1 != 2);
    }

    void cleanupTestCase()
    {
        qDebug("Called after myFirstTest and mySecondTest.");
    }
};

QTEST_MAIN(MyFirstTest)
#include "tst_myfirsttest.moc"

 

CMakeLists.txt

set(_components
    Core
    Test)

foreach(_component ${_components})
    find_package(Qt5${_component})
    list(APPEND QT_LIBRARIES ${Qt5${_component}_LIBRARIES})
    list(APPEND QT_INCLUDES ${Qt5${_component}_INCLUDE_DIRS})
    add_definitions(${Qt5${_component}_DEFINITIONS})
endforeach()

include_directories(${QT_INCLUDES})

find_program(QT_QMAKE_EXECUTABLE qmake)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

enable_testing(true)

add_executable(mytest tst_myfirsttest.cpp)
add_test(NAME mytest COMMAND mytest)
target_link_libraries(mytest PRIVATE ${QT_LIBRARIES})
qt_generate_moc(tst_myfirsttest.cpp tst_myfirsttest.moc TARGET mytest)

QSQLITE DB에서 regexp를 사용하려면,

커넥션 열 때 setConnectOptions("QSQLITE_ENABLE_REGEXP=1"); 을 설정해 주어야 한다.

bool DB::connectDatabase()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "connName");
    db.setConnectOptions("ISC_DPB_LC_CTYPE=UTF-8");
    db.setConnectOptions("QSQLITE_ENABLE_REGEXP=1");
    db.setDatabaseName(":memory:");
    if (!db.isValid())
    {
        qDebug() << "addDatabase failed.(db is not valid)";
        return false;
    }
    if (!db.isOpen())
    {
        if (!db.open())
        {
            qDebug() << QString("DB Open Failed - connName : %1, dbname : %2").arg("connName").arg(":memory:");
            QSqlDatabase::removeDatabase("connName");
            return false;
        }
    }
    return true;
}

 

데이터를 조회할 때는, 아래와 같이 사용한다.

한글로 시작하는 데이터 조회

SELECT * FROM tablename WHERE `columnName` REGEXP '^[ㄱ-ㅎ가-힣]';

영어로 시작하는 데이터 조회

SELECT * FROM tablename WHERE `columnName` REGEXP '^[a-zA-Z]';

숫자로 시작하는 데이터 조회

SELECT * FROM tablename WHERE `columnName` REGEXP '^[0-9]';

일본어로 시작하는 데이터 조회

SELECT * FROM tablename WHERE `columnName` REGEXP '^[ぁ-んァ-ヶー一-龠]' ;

중국어로 시작하는 데이터 조회

SELECT * FROM tablename WHERE `columnName` REGEXP '^[一-龥]';

샘플 코드

void DB::select() {
    QSqlDatabase DB = QSqlDatabase::database("connName");    

    QSqlQuery sql(DB);
    QString query = "SELECT * FROM tablename WHERE `subject` REGEXP '^[ㄱ-ㅎ가-힣]'";
    bool re = sql.exec(query);
    while (sql.next()) {
        qDebug() << sql.value(0).toString();
    }    
}

1. 디렉토리가 비어있는지 체크

Qt 5.9 이후부터는 QDir 클래스의 isEmpty() 함수로 체크 가능하다.

QString directoryPath = "C:\Directory";
QDir dir(directoryPath);
if (dir.isEmpty())
	qDebug() << directoryPath << " is Empty!";
else
	qDebug() << directoryPath << " is not Empty!";

 

2. 디렉토리 안 파일 개수 카운팅

QDir 클래스의 count() 함수로 카운팅 가능하다.

다만, .(현재폴더) ..(부모 폴더) 두개가 자동으로 포함되어 카운팅 되기 때문에, 이들을 빼주어야 한다.

QString directoryPath = "C:\Directory";
QDir dir(directoryPath);
dir.setFilter( QDir::AllEntries | QDir::NoDotAndDotDot ); // 현재, 부모 폴더 빼주기
qDebug() << directoryPath << " contains << dir.count() << " files.";

읽기 전용 파일은 덮어쓰거나, 삭제가 안된다. 

 

이 때, 파일 퍼미션을 설정해서 읽기 전용 속성을 제거할 수 있다.

(파일에 쓰기 권한을 주면, 덮어쓰기나 삭제가 가능해진다.)

 

읽기 전용(Read Only) 파일에 쓰기 권한 주기

// C++ code
#include <QFile>

void removeReadOnly(QString filepath)
{
    QFile file(filepath);
    file.setPermissions(file.permissions() |
                        QFileDevice::WriteOwner |
                        QFileDevice::WriteUser |
                        QFileDevice::WriteGroup |
                        QFileDevice::WriteOther);
}

 

QPushButton의 background color를 설정하고/가져오는 방법입니다.

 

1. QPushButton의 background color 설정하기

QColor color(Qt::red);
ui->pushButton->setStyleSheet(QString("background-color: %1;").arg(color.name()));

 

2. QPushButton의 background color 가져오기

// C++ code
QColor Panel::buttonColor() const
{
    return ui->pushButton->palette().background().color();
}

QMessageBox 안에 html 태그를 포함한 텍스트를 쓰고 싶은 경우가 있다.

예) <strong> 태그를 써서 특정 글자를 진하게 표현하고 싶다던가, <h4> 등의 제목 태그를 쓰고 싶다던가, <br/> 태그를 써서 줄바꿈을 하고 싶다던가..

 

이럴 때에는 setTextFormat(Qt::RichText); 함수로, 텍스트 포맷을 RichText로 설정해 주면 된다.

QMessageBox msg(this);
msg.setIcon(QMessageBox::Information);
msg.setTextFormat(Qt::RichText);
msg.setWindowTitle(tr("Title"));
msg.setText(tr("<strong>Hello!</strong><br/>World!");
msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
if (msg.exec() == QMessageBox::Yes) {
	// do something on Yes
}
else {
	// do something on No
}

 

+ Recent posts