大坑,Qt的qml在cmakelist编译中居然出现了这种事情

快捷通信:如果你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的鬼故事


目录

概括:如果你QT用qml遇到了这个问题:

1,序章:qml实例文档

2:qml在cmakelist中不听话

3:最终解决方法

cmake修改方法

方式一(qt6_add_resources)

方式二 (qt_add_qml_module )

4:我的demo


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 区别一览

项目setContextPropertyqmlRegisterType
是否在 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 {
    // ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值