跨CMake子项目使用Q_GADGET导致Qt QML应用崩溃求助
跨CMake子项目Q_GADGET类型QML访问崩溃问题解决
问题背景
两个CMake子项目foo与bar:
- bar子项目定义带
Q_GADGET的结构体Bar,包含x属性 - foo子项目的
FooObjectWithBarProperty类添加Bar类型的属性 - QML中绑定
obj.foo.x正常,但绑定obj.bar.x时应用崩溃,栈追踪提示Bar元类型未正确注册 - 已尝试
qRegisterMetaType、Q_DECLARE_METATYPE、QML_FOREIGN均未解决
核心原因
- 跨库符号与元对象链接问题:
Bar作为bar子项目的库类型,其moc生成的元对象代码未被foo项目正确链接,导致Qt元类型系统无法识别完整的类型信息。 - QML层面类型注册缺失:仅注册C++元类型不足以让QML引擎解析嵌套属性访问(如
obj.bar.x),需要将Bar类型注册到QML引擎中。
可行修改方案
1. 修正bar子项目的Bar定义与CMake配置
bar/bar.h
#ifndef BAR_H #define BAR_H #include <QObject> // 跨库符号导出/导入声明 #ifdef BAR_LIBRARY #define BAR_EXPORT Q_DECL_EXPORT #else #define BAR_EXPORT Q_DECL_IMPORT #endif struct BAR_EXPORT Bar { Q_GADGET Q_PROPERTY(int x MEMBER x) public: int x = 0; }; // 声明C++元类型 Q_DECLARE_METATYPE(Bar) #endif // BAR_H
bar/CMakeLists.txt
add_library(bar SHARED bar.h bar.cpp ) target_link_libraries(bar PRIVATE Qt${QT_VERSION_MAJOR}::Core) set_target_properties(bar PROPERTIES AUTOMOC ON # 自动生成moc代码 PUBLIC_HEADER bar.h BAR_LIBRARY ON # 定义BAR_LIBRARY宏,启用符号导出 ) target_include_directories(bar PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
2. 修正foo子项目的类型注册与CMake配置
foo/main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "fooobjectwithbarproperty.h" #include "bar.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // 将Bar注册到QML引擎,标记为不可创建(Q_GADGET类型无法在QML实例化) qmlRegisterUncreatableType<Bar>("com.example", 1, 0, "Bar", "Bar是Gadget类型,无法在QML中创建"); // 注册foo的QObject类型 qmlRegisterType<FooObjectWithBarProperty>("com.example", 1, 0, "FooObjectWithBarProperty"); QQmlApplicationEngine engine; const QUrl url(u"qrc:/foo/main.qml"_qs); 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(); }
foo/CMakeLists.txt
add_executable(foo main.cpp fooobjectwithbarproperty.h fooobjectwithbarproperty.cpp qml.qrc ) target_link_libraries(foo PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Quick bar) set_target_properties(foo PROPERTIES AUTOMOC ON AUTORCC ON ) target_include_directories(foo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
3. 验证QML代码(确保导入正确模块)
import QtQuick 2.15 import QtQuick.Window 2.15 import com.example 1.0 Window { width: 640 height: 480 visible: true title: qsTr("Hello World") FooObjectWithBarProperty { id: obj bar: Bar { x: 100 } foo: 200 } Text { text: obj.bar.x // 现在可正常访问,不会崩溃 anchors.centerIn: parent } }
方案说明
- 跨库符号导出:通过
BAR_EXPORT宏确保Bar的元对象信息能被foo项目正确链接,避免元类型系统无法找到类型的元数据。 - QML类型注册:
qmlRegisterUncreatableType让QML引擎识别Bar类型,同时明确禁止在QML中实例化(符合Q_GADGET的特性),这样QML才能解析嵌套属性obj.bar.x。 - AUTOMOC启用:确保CMake自动生成
Bar的moc代码,这是Q_GADGET类型元信息的来源。
内容的提问来源于stack exchange,提问作者MarcinJ
相关产品推荐
相关产品推荐

