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();
}
工程特点
- 模块解耦:各模块职责清晰,通过接口层解耦
- 可扩展性:新业务模块可轻松接入
- 跨平台:基于 Qt6,支持 Windows、Linux、macOS
- 热更新友好:DLL 模块可独立更新
- MVVM 模式:QML 作为 View,Worker 作为 ViewModel
- 设置debug编译时输出文件后缀加d: set(CMAKE_DEBUG_POSIFIX "d")
长风破浪会有时,直挂云帆济沧海!
可通过下方链接找到博主
https://www.cnblogs.com/judes/p/10875138.html