快捷通信:如果你QT用qml遇到了这个问题:
QML debugging is enabled. Only use this in a safe environment.
QQmlApplicationEngine failed to load component
<Unknown File>: No module named "demo12" found
参考知乎:
C++ cmake工程引入qt6和Quick 教程 - 知乎
这篇博客主要是从Qt的qwidget转到了quick,也就是UI转到了qml的一篇博客,因为要学习mvvm框架,中间遇到了很多困难。尤其是这个qml在cmkaelist文件中编译出错,处处是bug的鬼故事
目录
1,序章:qml实例文档
首先在qt中新建的qml示例文档是这样的
Qt qeidget到Qt qml的转变:
在qt中初始创建的目录形式是这样的:Qt quick工程区别在qml视为前端,即需要在主函数中添加:(注意主函数demo2还有Main在cmakelist中所提到的别名):
又需要在cmakelist中添加:
简单来说就是:
CMake 配置:负责告诉构建系统“模块是什么”,生成模块元数据,让你的 QML 模块成为一个完整的模块包。“我要创建一个 QML 模块,模块名是 demo2,版本是 1.0,包含这些 QML 文件。”
运行时代码:告诉你的程序“从哪个模块加载哪个组件”,实际上使用这个模块。:多个 QML 文件时,通常会在一个主 QML 文件(比如 main.qml)里设计页面切换或视图切换的逻辑,这样管理更清晰、维护更方便。
2:qml在cmakelist中不听话
然而在我的代码框架中经常会报错:
QML debugging is enabled. Only use this in a safe environment.
QQmlApplicationEngine failed to load component
<Unknown File>: No module named "demo12" found
因为我的很多源文件(即生成多个可执行程序),同时需要将qml的相关编译程序连接到多个可执行程序中,它居然找不到!调研发现在此类工程中qml在工程的调用有两种形式
一是借用初始框架中的这种格式:qt_add_qml_module
看着代码很简单,但是在我的多个可执行程序的框架下无法使用,一直报错误,花了一天也解决不了,g
另外一种则是让qrc来识别这个qml,但是这种方法要求qrc文件的同级/子目录下装有qml的文件资源
即:qt6_add_resources
然而在我的工程中均无法使用,最后参考了知乎大佬的博客实现的
3:最终解决方法
引用大佬的方法
方式一(qt6_add_resources)
要将现有的C++ CMake工程引入Qt6环境并使用Qt和QML,你需要执行以下步骤:
1. 安装Qt6:如果你还没有安装Qt6,请从官方网站下载并安装。确保你安装了Qt6的CMake模块。
2. 修改CMakeLists.txt:在你的C++ CMake工程中,打开CMakeLists.txt文件。首先,确保找到Qt6包,然后链接到需要的Qt6模块。例如,如果你需要使用Qt6 Core、Gui和Quick模块,可以添加以下内容:
find_package(Qt6 COMPONENTS Core Gui Quick REQUIRED)
然后,将这些模块链接到你的目标(例如,你的可执行文件):
target_link_libraries(your_target_name PRIVATE Qt6::Core Qt6::Gui Qt6::Quick)
3. 添加QML文件:在工程目录下创建一个QML文件夹,将所有QML文件放在该文件夹中。然后,在
CMakeLists.txt
中使用file(GLOB ...)
命令将这些文件添加到工程中。例如:file(GLOB QML_FILES qml/*.qml)
将QML文件添加到可执行文件的资源文件中:
qt6_add_resources(your_target_name "qml" PREFIX "/" FILES ${QML_FILES} )
这里我的代码报错,后面研究后发现:传给 qt6_add_resources()
的 QML 文件路径是 绝对路径,Qt6 的资源系统不接受绝对路径文件作为资源;它只能识别 相对于 CMakeLists.txt 的路径。最终我的修改如下:
# 查找 QML 文件(相对路径)
file(GLOB RELATIVE_QML_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} view/*.qml)
# 添加 QML 文件为资源
qt6_add_resources(${example_name} "qml"
PREFIX
"/"
FILES
${RELATIVE_QML_FILES}
)
4. 创建主入口:在你的C++工程中创建一个
main.cpp
文件(如果尚未创建),并添加以下代码以设置Qt和QML环境:#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
这里我修改成了我的真实qml的路径,还有需要注意:我们没有使用第二种利用.qrc的方式管理qml文件,即没有显式地写 .qrc
文件(传统方式),而是使用了 Qt6 提供的新式 CMake 宏:qt6_add_resources()
(在cmakelist文件中写的)。所以qml编译后在默认qrc资源管理中,目录只要填写正确即可。
5. 注册C++类:在
main.cpp
中,使用qmlRegisterType
函数注册你的C++类,使其可以在QML中使用。例如:qmlRegisterType<YourCppClass>("com.example.yourclassname", 1, 0, "YourCppClass");
然后在QML文件中,可以通过以下方式导入和使用你的C++类:
import com.example.yourclassname 1.0 YourCppClass { // ... }
6. 编译和运行:现在你的C++ CMake工程已经集成了Qt6和QML,你可以编译和运行它。如果一切正常,你应该可以看到你的QML界面。
补充
2025-7-28补充:发现代码似乎没有连接C++类以及相关的参数,倒是qml显示不出来,也就是需要补充使用上面5还有6的qmlRegisterType
方法。
qpt生成了另外一种方法(setContextProperty
),优点是不需要额外创建对象:
✅ qmlRegisterType
vs setContextProperty
区别一览
项目 | setContextProperty | qmlRegisterType |
---|---|---|
是否在 C++ 中创建对象 | ✅ 是 | ❌ 否 |
是否可以在 QML 中写 MyClass {} | ❌ 不能 | ✅ 可以 |
是否能动态创建多个对象 | ❌ 不方便 | ✅ 可以 |
是否支持属性绑定、信号 | ✅ 支持 | ✅ 支持 |
使用场景 | 数据模型或单例服务 | 动态组件、UI 控件等 |
这里给出使用相关示例:
setContextProperty
// 初始化 QML 引擎
QQmlApplicationEngine engine;
// ✅ 设置上下文属性,将 C++ 对象暴露给 QML
engine.rootContext()->setContextProperty("v名", &实例);
// 设置 QML 入口
const QUrl url(QStringLiteral("qrc:/view/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
engine.rootContext()->setContextProperty("physiologyVM", &viewModel);
代码片段 | 说明 |
---|---|
engine | 你的 QQmlApplicationEngine 实例,负责加载 QML 文件。 |
rootContext() | 获取 QML 的根上下文(上下文是 QML 和 C++ 沟通的“桥梁”)。 |
setContextProperty(...) | 向 QML 的上下文中添加一个可访问的全局变量。 |
"physiologyVM" | 这是你在 QML 中访问的变量名,比如 physiologyVM.heartRate 。 |
&viewModel | 是 C++ 中的实际对象地址(指针),例如你的 PhysiologyViewModel 实例。 |
import QtQuick 2.15
ApplicationWindow {
width: 400
height: 300
visible: true
// ❌ 不需要写 PhysiologyViewModel {},因为实例已由 C++ 提供
TextField {
text: physiologyVM.heartRate.toString()
onTextChanged: physiologyVM.heartRate = parseFloat(text)
}
}
qmlRegisterType
// 注册 ViewModel(C++ 中的类)供 QML 创建使用
// Module 名:com.example.physiology,版本:1.0,QML 中类名:PhysiologyViewModel
qmlRegisterType<PhysiologyViewModel>("com.example.physiology", 1, 0, "PhysiologyViewModel");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/view/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
qmlRegisterType<PhysiologyViewModel>("com.example.physiology", 1, 0, "PhysiologyViewModel");
参数 | 作用 | 示例值 |
---|---|---|
<PhysiologyViewModel> | 要注册的 C++ 类模板参数 | 你的 ViewModel 类 |
"com.example.physiology" | 模块名(QML 中通过 import 引用),名字很怪,但是是为了防止与类名重名 | import com.example.physiology 1.0 |
1 | 主版本号 | 1 |
0 | 子版本号 | 0 |
"PhysiologyViewModel" | QML 中使用的类名(字符串) | 可在 QML 中写 PhysiologyViewModel { } |
import QtQuick 2.15
import physiologyv 1.0 // 必须导入模块和版本
ApplicationWindow {
width: 400
height: 300
visible: true
// 🔸 QML 需要自己创建这个类的实例
PhysiologyViewModel {
id: physiologyVM
heartRate: 80
}
TextField {
text: physiologyVM.heartRate.toString()
onTextChanged: physiologyVM.heartRate = parseFloat(text)
}
}
方式二 (qt_add_qml_module )
当然,你也可以使用
qt_add_qml_module
函数在CMake项目中添加QML模块。这是一个简化了QML资源添加的便捷方法。
即利用.qrc文件来添加并管理qml的方法,但是实际尝试的时候需要qml在.qrc文件的同级或者子目录。后需因为不想破坏代码目录结构就没有继续尝试了
下面是如何使用
qt_add_qml_module
的步骤:1.安装Qt6:如果你还没有安装Qt6,请从官方网站下载并安装。确保你安装了Qt6的CMake模块。
2. 修改CMakeLists.txt:在你的C++ CMake工程中,打开CMakeLists.txt文件。首先,确保找到Qt6包,然后链接到需要的Qt6模块。例如,如果你需要使用Qt6 Core、Gui和Quick模块,可以添加以下内容:
find_package(Qt6 COMPONENTS Core Gui Quick REQUIRED)
然后,将这些模块链接到你的目标(例如,你的可执行文件):
target_link_libraries(your_target_name PRIVATE Qt6::Core Qt6::Gui Qt6::Quick)
3. 添加QML文件:在工程目录下创建一个QML文件夹,将所有QML文件放在该文件夹中。
4. 使用qt_add_qml_module:在
CMakeLists.txt
中,使用qt_add_qml_module
函数将QML文件添加到工程中。例如:qt_add_qml_module(your_target_name URI com.example.yourclassname VERSION 1.0 QML_FILES qml/main.qml )
其中
your_target_name
是你的可执行文件的目标名称,URI
指定了模块的标识符,VERSION
设置了模块的版本,QML_FILES
列出了所有的QML文件。5.创建主入口:在你的C++工程中创建一个
main.cpp
文件(如果尚未创建),并添加以下代码以设置Qt和QML环境:#include <QGuiApplication> #include <QQmlApplicationEngine> int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/qml/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
6.注册C++类:在
main.cpp
中,使用qmlRegisterType
函数注册你的C++类,使其可以在QML中使用。例如:qmlRegisterType<YourCppClass>("com.example.yourclassname", 1, 0, "YourCppClass");
然后在QML文件中,可以通过以下方式导入和使用你的C++类:
import com.example.yourclassname 1.0 YourCppClass { // ...