Windows下QtCreator使用CMake編譯GUI程序


一、前言

為什么要用 CMake 來構建 Qt 的項目呢?Qt 不是有 qmake 嗎?這樣,豈不是多此一舉?
其實,應用 CMake 來構建項目還是非常有必要的,特別是當你的項目涉及到很多第三方庫的時候,CMake 的優勢非常突出。

Qt5.15.2 在之前選擇安裝模塊的時候,自動幫我們勾選了 CMake_64 模塊,你也可以另外選擇勾選 CMake_32 模塊,所以 QtCreator 是支持 CMake 編譯方式的,而不僅僅只能使用 QMake 編譯。

本人之前也手動安裝了 CMake3.20.0 的版本,這些可以在 QtCreator 中的套件配置的 cmake 項看到:

CMake_qtCMake_D.png


下面我們介紹一下 Qt 使用 CMake 編譯的兩種方式。


二、依賴QtCreator自動生成CMakeLists.txt文件

QtCreator 新建工程時,選擇 cmake 而不是默認的 qmake 編譯方式,如下所示:

CMake_qtCMake_A.png


然后選擇 CMake 要編譯成的哪種編譯套件,是 MingW 還是 MSVC,這里選擇的是 Qt5.15.2 MinGW 64,創建運行成功后:

CMake_qtCMake_B.png


可以看到 QtCreator 自動幫忙生成了 CMakeLists.txt 文件,直接可以使用,其內容如下:

cmake_minimum_required(VERSION 3.5)

project(HelloWorld LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check https://doc.qt.io/qt/deployment-android.html for more information.
# They need to be set before the find_package( ...) calls below.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED)

set(PROJECT_SOURCES
        main.cpp
        widget.cpp
        widget.h
        widget.ui
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(HelloWorld
        ${PROJECT_SOURCES}
    )
else()
    if(ANDROID)
        add_library(HelloWorld SHARED
            ${PROJECT_SOURCES}
        )
    else()
        add_executable(HelloWorld
            ${PROJECT_SOURCES}
        )
    endif()
endif()

target_link_libraries(HelloWorld PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)

QtCreator 也幫忙添加了 CMake Modules,來支持對 Qt5Core、Qt5Gui 等模塊的支持。

就是后續在工程中添加其它的類,比如 Form 類,不會自動添加到 CMakeLists.txt 文件中,需要手動添加:

set(PROJECT_SOURCES
        main.cpp
        widget.cpp
        widget.h
        widget.ui
        #下面為手動添加內容
        form.cpp
        form.h
        form.ui
)

需要添加額外的模塊,同理。

當然自動生成的 CMakeLists.txt 有些累贅的內容,比如對 ANDROID 的配置,下面會介紹一下如何自己編寫 CMakeLists.txt 來創建工程。


三、手動配置CMakeLists.txt

你也可以自己手動配置 CMakeLists.txt。

首先,我們還是要使用 QtCreator 創建項目:helloworld。項目中的文件列表如下:項目雖小,五臟俱全,該有的文件都有了(.h .cpp .qrc .ui .pro .png)。我把 .png 文件和 .qrc 文件放在了一個新建的 resources 資源文件夾中。

├── helloworld.pro
├── helloworld.pro.user
├── main.cpp
├── resources
│   ├── ico.png
│   └── resources.qrc
├── widget.cpp
├── widget.h
└── widget.ui

該項目中唯一需要添加代碼的地方是 widget.cpp 文件,因為我們需要添加一個圖標:

#include "widget.h"
#include "ui_widget.h"
#include <QIcon>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 窗體標題
    this->setWindowTitle("Qt5.1 窗體應用");
    // 窗體 ICO 圖片,如圖不起別名,后綴直接寫圖片全名。
    this->setWindowIcon(QIcon(":/new/prefix1/ico.png"));
}

Widget::~Widget()
{
    delete ui;
}

接着在項目文件夾中手動創建一個 CMakeLists.txt 文件,添加到工程中,其內容如下:

#設置cmake版本號
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)

#設置工程名稱(看情況修改)
project(helloworld)

# 添加C++11(非必須)
set(CMAKE_CXX_STANDARD 11)

#打開全局moc
set(CMAKE_AUTOMOC ON)
#打開全局uic
set(CMAKE_AUTOUIC ON)
#打開全局rcc,如果沒有使用qrc,此句可以去掉
set(CMAKE_AUTORCC ON)

#設置工程包含當前目錄,使用*.ui文件時,需要加上這句,否則找不到頭文件
set(CMAKE_INCLUDE_CURRENT_DIR ON)

#查找需要的Qt庫文件,最好每一個庫都要寫,Qt也會根據依賴關系自動添加
find_package(Qt5 REQUIRED Widgets)

#創建工程文件
add_executable(helloworld main.cpp widget.cpp widget.h widget.ui resources/resources.qrc)

#添加Qt5依賴項
target_link_libraries(helloworld Qt5::Widgets)

這是 CMakeLists.txt 編寫的第一種方法,全局控制:從 CMake 全局入手控制文件生成,非常簡便!后面會介紹另外兩種方法。

最后的項目組成如下:

├── CMakeLists.txt
├── CMakeLists.txt.user
├── helloworld.pro
├── helloworld.pro.user
├── main.cpp
├── resources
│   ├── ico.png
│   └── resources.qrc
├── widget.cpp
├── widget.h
└── widget.ui

關閉之前創建的 helloworld 項目,然后用 QtCreator 直接打開 CMakeLists.txt 文件,一開始會彈出一個 configure 窗口,直接 configure 就可以實現項目配置,然后 cmake 該工程,就會添加 main.cpp、widget.h 等源文件到工程中。最后構建運行該項目報錯:

F:\Project\CMake\helloworld\widget.cpp:-1: error: undefined reference to `vtable for Widget'

在網上搜索發現,跟 Qt 的 moc 機制有關,也就是“元對象編譯器”,與之相關需要定義的宏 Q_OBJECT,與 CMakeLists.txt 中的set(CMAKE_AUTOMOC ON)沖突,解決辦法就是注釋宏 Q_OBJECT。

moc 全稱是 Meta-Object Compiler,也就是“元對象編譯器”。Qt 程序在交由標准編譯器編譯之前,先要使用 moc 分析 C++ 源文件。如果它發現在一個頭文件中包含了宏 Q_OBJECT,則會生成另外一個 C++ 源文件。這個源文件中包含了 Q_OBJECT 宏的實現代碼

修改后運行成功,如下圖所示:

CMake_qtCMake_C.png


四、擴展:CMakeLists.txt 編寫的另外兩種方法

上面介紹了 CMakeLists.txt 編寫的第一種方法,全局控制,下面介紹另兩種方法。


方法二、目標控制

#設置cmake版本號
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)

#設置工程名稱(看情況修改)
set(project_name helloworld)
project(${project_name})

# 添加C++11(非必須)
set(CMAKE_CXX_STANDARD 11)

#查找需要的Qt庫文件,最好每一個庫都要寫,Qt也會根據依賴關系自動添加
find_package(Qt5 REQUIRED Widgets)

#設置目標名稱
set(target_name ${project_name})
#創建工程文件
add_executable(${target_name} main.cpp widget.cpp widget.h widget.ui resources/resources.qrc)
#添加Qt5依賴項
target_link_libraries(${target_name} Qt5::Widgets)

#設置目標關聯的*.h, *.cpp 使用 Qt moc進行編譯
set_target_properties(${target_name} PROPERTIES AUTOMOC ON)
#設置目標關聯的*.ui 使用 Qt uic進行編譯
set_target_properties(${target_name} PROPERTIES AUTOUIC ON)
#設置目標關聯的*.qrc 使用 Qt uic進行編譯
set_target_properties(${target_name} PROPERTIES AUTORCC ON)

#跳過不需要使用moc編譯的文件。如果覺得麻煩此句可以不寫,automoc能根據*.h,*.cpp代碼里面的宏(Q_OBJECT;Q_GADGET;Q_NAMESPACE)自動判斷是否需要使用moc
set_source_files_properties(main.cpp PROPERTIES SKIP_AUTOMOC ON)

使用該方法,從目標(可執行或者庫)入手控制文件生成,可控力度更細一些。

上述兩種方法:

使用 CMake 官方 Auto 方法生成的 VS 工程,主要會修改三個地方:

  • 會將一個 mocs_compilation.cpp 添加到工程里面
  • 【附件包含路徑】中會添加一句TestWindow_autogen\include_xxx
  • 【預先生成事件】中會添加預處理命令,源文件修改后會自動重新生成 moc_xxx.cpp 文件

方法三、單個文件控制

#設置cmake版本號
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)

#設置工程名稱(看情況修改)
set(project_name helloworld)
project(${project_name})

# 添加C++11(非必須)
set(CMAKE_CXX_STANDARD 11)

#查找需要的Qt庫文件,最好每一個庫都要寫,Qt也會根據依賴關系自動添加
find_package(Qt5 REQUIRED Widgets)

#包含當前路徑,使用*.ui文件時,需要加上這句,否則找不到頭文件
set(CMAKE_INCLUDE_CURRENT_DIR ON)

#需要生成的moc文件,輸出文件名稱放在變量 mocfiles中,必須在find QT5 package才能調用
qt5_wrap_cpp(mocfiles widget.h)
source_group("moc" FILES ${mocfiles})

#需要生成的ui文件,必須在find QT5 package才能調用
qt5_wrap_ui(uifiles widget.ui)

#打開全局rcc,如果沒有使用qrc,此句可以去掉
set(CMAKE_AUTORCC ON)

#設置目標名稱
set(target_name ${project_name})
#添加生成的moc文件到目標中
add_executable(${target_name} main.cpp widget.cpp widget.h widget.ui resources/resources.qrc ${mocfiles})
target_link_libraries(${target_name} Qt5::Widgets)

該方法從 源文件 入手控制文件生成,要稍微麻煩一些。

CMake 生成 VS 工程后,將 mainwindow.ui 的預處理命令放在 mainwindow.ui 屬性配置中,而 mainwindow.h 的預處理命令放在CMake Rules/moc_mainwindow.cpp.rule的屬性配置中。源文件修改后,目標文件會自動生成。


參考:

用cmake構建基於qt5.8.0的qt5項目

Qt+CMake解決方案及問題匯總

使用CMake構建Qt5工程

原創]Qt Creator構建CMake項目

CMake教程——QT項目使用CMake

CMake學習(1)-Windows下安裝與使用



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM