zoukankan      html  css  js  c++  java
  • 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:ProjectCMakehelloworldwidget.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_autogeninclude_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下安装与使用


  • 相关阅读:
    8.驱动与硬件通信
    11.ok6410之led驱动程序编写
    10.字符设备控制学习
    9.应用程序如何访问驱动
    linux内核打印数据到串口控制台,printk数据不打印问题
    nginx配置-location
    spring-cloud config配置中心
    idea中git stash--解决pull冲突或切换分支问题
    idea中git回退本地仓库版本
    idea中git回退远程仓库版本
  • 原文地址:https://www.cnblogs.com/linuxAndMcu/p/14458427.html
Copyright © 2011-2022 走看看