Qt6使用cmake实例

1.使用pro2cmake

https://blog.csdn.net/haokan123456789/article/details/148676526

https://www.qt.io/blog/introducing-qmake2cmake

2.一些对应关系

https://stackoverflow.com/questions/43763416/how-to-convert-qmake-to-cmake

https://wiki.qt.io/CMake_Port/Porting_Guide

3.示例

https://blog.csdn.net/weixin_43359674/article/details/127040308#:~:text=本文介绍如何在Qt6项目中使用CMake通过子目录管理项目文件和文件夹,以便于大型项目的组织和维护。 文章详细展示了创建子目录、配置CMakeLists.txt文件以及链接库的具体步骤。 摘要生成于 C知道 ,由 ,DeepSeek-R1%20%E6%BB%A1%E8%A1%80%E7%89%88%E6%94%AF%E6%8C%81%EF%BC%8C%20%E5%89%8D%E5%BE%80%E4%BD%93%E9%AA%8C%20%3E%20%E4%B8%BA%E4%BA%86%E4%BE%BF%E4%BA%8E%E7%AE%A1%E7%90%86%E5%A4%9A%E6%96%87%E4%BB%B6%E7%9A%84%E9%A1%B9%E7%9B%AE%EF%BC%8C%E9%9C%80%E8%A6%81%E5%88%86%E6%88%90%E4%B8%8D%E5%90%8C%E7%9A%84%E6%96%87%E4%BB%B6%E5%A4%B9%E6%9D%A5%E5%AD%98%E6%94%BE%E4%B8%8D%E5%90%8C%E5%8A%9F%E8%83%BD%E7%9A%84%E5%AE%9E%E7%8E%B0%E4%BB%A3%E7%A0%81%EF%BC%8C%E8%BF%99%E6%A0%B7%E5%8F%AF%E4%BB%A5%E6%96%B9%E4%BE%BF%E7%AE%A1%E7%90%86%EF%BC%8C%E4%B8%8D%E4%BC%9A%E5%AF%BC%E8%87%B4%E9%A1%B9%E7%9B%AE%E5%A4%A7%E4%BA%86%E4%BB%A5%E5%90%8E%E7%9A%84%E6%B7%B7%E4%B9%B1%E3%80%82

说明:

1.qt6+cmake的这种子项目工程与qt5+pri的不一样,前者是所有子项目都是静态或动态库,后者是将所有源码都编译到exe中

2.此方式在qml工程里加载了qml资源文件,需要在C++里将资源文件写入静态库,使用:Q_INIT_RESOURCE(qml)实现,qml就是资源名称;也可在cmakelist中实现,但是目前还没成功

3.CMake实现qml方式:①qt_add_resources ②qt_add_qml_module ③qrc资源文件

官方推荐前两种:

Building a QML application | Build with CMake | Qt 6.10.0

qt_add_resources | Qt Core | Qt 6.10.0

这两种都会生成other_files【单纯提供一个看文件视图】:C++ cmake工程引入qt6和Quick 教程_qt cmake qml-CSDN博客

第三种,使用资源文件的方式管理qml和img文件:

The Qt Resource System | Qt Core | Qt 6.10.0

4.Qt6的CMake入门教程

Getting started with CMake | Build with CMake | Qt 6.10.0

5.Qt6的Cmake指令集合

CMake Commands in Qt6 Core | Qt Core | Qt 6.10.0

 

例子:

Qt6 + CMake 多模块示例工程

工程概述

这是一个基于 Qt6 + CMake 的多模块(Multi-DLL)示例工程,展示了如何将一个完整的 Qt Quick 应用程序拆分为多个独立的动态链接库模块。

架构设计

┌─────────────────────────────────────────────────────────────┐
│                      MainApp (主程序)                        │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Display (UI模块) ─────── Interface (接口层) ────│    │
│  │       │                         │               │    │
│  │       ▼                         ▼               │    │
│  │  QML UI                    Worker (业务层)       │    │
│  │                              │                  │    │
│  │                              ▼                  │    │
│  │                         Algs (算法模块)           │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

模块职责

模块 类型 职责
MainApp 可执行程序 程序入口,协调各模块初始化
Display DLL QML UI 展示层,负责界面渲染
Interface DLL 接口抽象层,定义前后端交互契约
Worker DLL 业务逻辑层,实现具体业务功能
Algs DLL 算法模块,处理耗时计算任务

目录结构

QtMdllDemo/
├── MainApp/           # 主程序入口
│   ├── CMakeLists.txt
│   └── main.cpp
├── Display/           # UI展示模块
│   ├── CMakeLists.txt
│   ├── Display.cpp
│   ├── Display.h
│   ├── Display_global.h
│   ├── qml/
│   │   └── Main.qml
│   └── qml.qrc
├── Interface/         # 接口抽象层
│   ├── CMakeLists.txt
│   ├── Interface.cpp
│   ├── Interface.h
│   └── Interface_global.h
├── Worker/            # 业务逻辑层
│   ├── CMakeLists.txt
│   ├── Worker.cpp
│   ├── Worker.h
│   ├── Worker_global.h
│   ├── Work1/         # 子业务模块1
│   │   ├── Work1.cpp
│   │   └── Work1.h
│   └── Work2/         # 子业务模块2
│       ├── Work2.cpp
│       └── Work2.h
├── Algs/              # 算法模块
│   ├── CMakeLists.txt
│   ├── Algs.cpp
│   ├── Algs.h
│   └── Algs_global.h
└── CMakeLists.txt     # 根CMake配置

核心代码

1. 根 CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(QtMdllDemo LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick)
qt_standard_project_setup(REQUIRES 6.8)

add_subdirectory(MainApp)
add_subdirectory(Worker)
add_subdirectory(Interface)
add_subdirectory(Display)
add_subdirectory(Algs)

2. MainApp 模块

CMakeLists.txt

add_executable(MainApp WIN32)
target_sources(MainApp PRIVATE main.cpp)
target_include_directories(MainApp PRIVATE ${CMAKE_SOURCE_DIR})
target_link_libraries(MainApp PRIVATE
    Interface
    Worker
    Display
    Qt6::Core
    Qt6::Qml
    Qt6::Quick
)

main.cpp

#include <QGuiApplication>
#include <QThread>
#include <QDebug>
#include "Worker.h"
#include "Display.h"

int main(int argc, char* argv[])
{
    QGuiApplication app(argc, argv);

    Worker worker;
    Display display;

    display.init(&worker);
    qDebug()<<"main thread" << QThread::currentThreadId();

    return app.exec();
}

3. Interface 模块(接口抽象层)

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(Interface LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

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

add_library(Interface SHARED
  Interface_global.h
  Interface.cpp
  Interface.h
)

target_link_libraries(Interface PRIVATE Qt${QT_VERSION_MAJOR}::Core)
target_compile_definitions(Interface PRIVATE INTERFACE_LIBRARY)
target_include_directories(Interface PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

Interface_global.h

#ifndef INTERFACE_GLOBAL_H
#define INTERFACE_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(INTERFACE_LIBRARY)
#define INTERFACE_EXPORT Q_DECL_EXPORT
#else
#define INTERFACE_EXPORT Q_DECL_IMPORT
#endif

#endif // INTERFACE_GLOBAL_H

Interface.h

#ifndef INTERFACE_H
#define INTERFACE_H

#include "Interface_global.h"
#include <QObject>
#include <QVariant>

class INTERFACE_EXPORT Interface : public QObject
{
    Q_OBJECT
public:
    Interface();
    virtual ~Interface() = default;
public slots:
    virtual void login(const QString& username, const QString& password) = 0;
    virtual void workLongTime() = 0;
    virtual void updateUI() = 0;

signals:
    void loginStatus(const QVariant& value);
    void workLongTimeRst();
    void updateTrace(const QString& data);

protected:
    QVariantMap m_dataCache;
};

#endif // INTERFACE_H

Interface.cpp

#include "Interface.h"

Interface::Interface() {}

4. Display 模块(UI展示层)

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(Display LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

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

add_library(Display SHARED
  Display_global.h
  Display.cpp
  Display.h
  qml.qrc
)

target_link_libraries(Display PRIVATE Qt6::Core Qt6::Qml Qt6::Quick)
target_link_libraries(Display PUBLIC Interface)
target_compile_definitions(Display PRIVATE DISPLAY_LIBRARY)
target_include_directories(Display PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

Display_global.h

#ifndef DISPLAY_GLOBAL_H
#define DISPLAY_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(DISPLAY_LIBRARY)
#define DISPLAY_EXPORT Q_DECL_EXPORT
#else
#define DISPLAY_EXPORT Q_DECL_IMPORT
#endif

#endif // DISPLAY_GLOBAL_H

Display.h

#ifndef DISPLAY_H
#define DISPLAY_H

#include "Display_global.h"

class Interface;
class QQmlApplicationEngine;

class DISPLAY_EXPORT Display
{
public:
    Display();
    void init(Interface* interface);
    void registerContextProperty(const QString& name, QObject* object);

private:
    QQmlApplicationEngine* m_engine;
    Interface* m_interface;
};

#endif // DISPLAY_H

Display.cpp

#include "Display.h"
#include "Interface.h"
#include <QQmlApplicationEngine>
#include <QUrl>
#include <QQmlContext>

Display::Display()
    : m_engine(new QQmlApplicationEngine())
    , m_interface(nullptr)
{
}

void Display::init(Interface *interface)
{
    m_interface = interface;
    registerContextProperty("AppService", m_interface);
    m_engine->load(QUrl("qrc:/qml/Main.qml"));
}

void Display::registerContextProperty(const QString &name, QObject *object)
{
    m_engine->rootContext()->setContextProperty(name, object);
}

qml/Main.qml

import QtQuick
import QtQuick.Controls
import QtQuick.Window

Window {
    width: 600
    height: 400
    visible: true
    title: "Qt6 Multi-DLL Demo"

    Column {
        anchors.centerIn: parent
        spacing: 5

        Text {
            text: "Welcome"
            font.pixelSize: 24
            anchors.horizontalCenter: parent.horizontalCenter
        }

        TextEdit {
            id: usernameField
            width: 200
            text: "admin"
        }

        TextEdit {
            id: passwordField
            width: 200
            text: "123"
        }

        Button {
            text: "Login"
            width: 200
            onClicked: AppService.login(usernameField.text, passwordField.text)
        }

        Button {
            text: "Running"
            width: 200
            onClicked: AppService.workLongTime()
        }

        Button {
            text: "UpdateUI"
            width: 200
            onClicked: AppService.updateUI()
        }

        Text {
            id: statusText
            text: "Ready"
            color: "gray"
        }

        Connections {
            target: AppService

            function onLoginStatus(value) {
                console.log("onLoginStatus:", value)
                if(value) {
                    statusText.text = "Success"
                    statusText.color = "green"
                } else {
                    statusText.text = "Faild"
                    statusText.color = "red"
                }
            }

            function onWorkLongTimeRst() {
                statusText.text = "running end"
                statusText.color = "green"
            }

            function onUpdateTrace(value) {
                statusText.text = value
                statusText.color = "green"
            }
        }
    }
}

5. Worker 模块(业务逻辑层)

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(Worker LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

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

add_library(Worker SHARED
  Worker_global.h
  Worker.cpp
  Worker.h
  Work1/Work1.h Work1/Work1.cpp
  Work2/Work2.h Work2/Work2.cpp
)

target_link_libraries(Worker PRIVATE Qt${QT_VERSION_MAJOR}::Core)
target_link_libraries(Worker PUBLIC Interface Algs)
target_compile_definitions(Worker PRIVATE WORKER_LIBRARY)
target_include_directories(Worker PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

Worker_global.h

#ifndef WORKER_GLOBAL_H
#define WORKER_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(WORKER_LIBRARY)
#define WORKER_EXPORT Q_DECL_EXPORT
#else
#define WORKER_EXPORT Q_DECL_IMPORT
#endif

#endif // WORKER_GLOBAL_H

Worker.h

#ifndef WORKER_H
#define WORKER_H

#include "Worker_global.h"
#include "Interface.h"
#include "Work1/Work1.h"
#include "Work2/Work2.h"

class WORKER_EXPORT Worker : public Interface
{
    Q_OBJECT
public:
    Worker();
    virtual ~Worker() = default;

public slots:
    virtual void login(const QString& username, const QString& password) override;
    virtual void workLongTime() override;
    virtual void updateUI() override;

private:
    Work1 m_work1;
    Work2 m_work2;
};

#endif // WORKER_H

Worker.cpp

#include "Worker.h"
#include <QDateTime>
#include <QThread>
#include <QDebug>
#include <thread>
#include "Algs.h"

Worker::Worker()
{
    m_work2.setWorker(this);
}

void Worker::login(const QString &username, const QString &password)
{
    auto rst = m_work1.loginVerify(username, password);
    emit loginStatus(rst);
}

void Worker::workLongTime()
{
    auto f = [this](){
        Algs algs;
        if(algs.doAlg())
        {
            emit workLongTimeRst();
        }
    };

    std::thread t(f);
    t.detach();
}

void Worker::updateUI()
{
    m_work2.updateTrace();
}

Work1 子模块

Work1.h

#ifndef WORK1_H
#define WORK1_H

#include <QObject>

class Work1 : public QObject
{
    Q_OBJECT
public:
    explicit Work1(QObject *parent = nullptr);
    bool loginVerify(const QString& userName, const QString& password);
signals:
};

#endif // WORK1_H

Work1.cpp

#include "Work1.h"

Work1::Work1(QObject *parent)
    : QObject{parent}
{}

bool Work1::loginVerify(const QString &userName, const QString &password)
{
    return (userName == "admin" && password == "123");
}

Work2 子模块

Work2.h

#ifndef WORK2_H
#define WORK2_H

#include <QObject>

class Worker;
class Work2 : public QObject
{
    Q_OBJECT
public:
    explicit Work2(QObject *parent = nullptr);
    void setWorker(Worker* worker){m_worker = worker;}
    void updateTrace();

signals:

private:
    Worker* m_worker;
};

#endif // WORK2_H

Work2.cpp

#include "Work2.h"
#include <QThread>
#include <QDebug>
#include <thread>
#include "Worker.h"

Work2::Work2(QObject *parent)
    : QObject{parent}
{}

void Work2::updateTrace()
{
    auto f = [this](){
        for(int i=0; i<10; i++)
        {
            emit m_worker->updateTrace(QString("trace%1").arg(i));
            QThread::msleep(100);
        }
    };
    std::thread t(f);
    t.detach();
    qDebug()<<"finish updateTrace!!";
}

6. Algs 模块(算法模块)

CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(Algs LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

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

add_library(Algs SHARED
  Algs_global.h
  Algs.cpp
  Algs.h
)

target_link_libraries(Algs PRIVATE Qt${QT_VERSION_MAJOR}::Core)
target_compile_definitions(Algs PRIVATE ALGS_LIBRARY)
target_include_directories(Algs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

Algs.h

#ifndef ALGS_H
#define ALGS_H

#include "Algs_global.h"

class ALGS_EXPORT Algs
{
public:
    Algs();
    bool doAlg();
};

#endif // ALGS_H

Algs.cpp

#include "Algs.h"
#include <QThread>
#include <QDebug>

Algs::Algs() {}

bool Algs::doAlg()
{
    for(int i=0; i<10; i++)
    {
        qDebug()<<"start do something...." << QThread::currentThreadId();
        QThread::msleep(100);
    }
    qDebug()<<"finish do something!!";
    return true;
}

编译与运行

编译步骤

# 创建build目录
mkdir build && cd build

# 配置CMake
cmake .. -G "Visual Studio 17 2022" -A x64

# 编译
cmake --build . --config Debug

运行

编译完成后,在 build/bin 目录下找到 MainApp.exe 并运行。

测试账号:

  • 用户名:admin
  • 密码:123

关键技术点

1. 动态库导出宏

每个 DLL 模块都使用了 Qt 的导出宏机制:

#if defined(MODULE_LIBRARY)
#define MODULE_EXPORT Q_DECL_EXPORT
#else
#define MODULE_EXPORT Q_DECL_IMPORT
#endif

2. 接口抽象设计

通过 Interface 抽象类实现前后端解耦,Worker 继承并实现接口,Display 通过接口指针与业务层交互。

3. QML 上下文属性

Display 模块通过 registerContextProperty 将 C++ 对象暴露给 QML:

void Display::init(Interface *interface)
{
    m_interface = interface;
    registerContextProperty("AppService", m_interface);
    m_engine->load(QUrl("qrc:/qml/Main.qml"));
}

4. 信号与槽机制

使用 Qt 的信号槽机制实现 QML 与 C++ 的双向通信:

  • QML 调用 C++:AppService.login(username, password)
  • C++ 通知 QML:emit loginStatus(result)

5. 多线程处理

耗时操作使用 std::thread 在后台线程执行,避免阻塞 UI 线程:

void Worker::workLongTime()
{
    auto f = [this](){
        Algs algs;
        if(algs.doAlg())
        {
            emit workLongTimeRst();
        }
    };
    std::thread t(f);
    t.detach();
}

工程特点

  1. 模块解耦:各模块职责清晰,通过接口层解耦
  2. 可扩展性:新业务模块可轻松接入
  3. 跨平台:基于 Qt6,支持 Windows、Linux、macOS
  4. 热更新友好:DLL 模块可独立更新
  5. MVVM 模式:QML 作为 View,Worker 作为 ViewModel
  • 设置debug编译时输出文件后缀加d: set(CMAKE_DEBUG_POSIFIX "d")
posted @ 2025-08-10 13:08  朱小勇  阅读(273)  评论(0)    收藏  举报