C++与QML交互入门指南
QtQuick中C++与QML协作核心是QObject暴露、信号槽、属性绑定和上下文属性;C++类需继承QObject、加Q_OBJECT宏、用Q_PROPERTY/Q_INVOKABLE声明接口,并在main.cpp注册或注入;QML可直接访问属性、调用函数、监听信号,复杂数据推荐QAbstractListModel。
Qt Quick 中 C++ 与 QML 协作核心是 QObject 暴露、信号槽、属性绑定和上下文属性;C++ 类需继承 QObject、加 Q_OBJECT 宏、用 Q_PROPERTY/Q_INVOKABLE 声明接口,并在 main.cpp 注册或注入;QML 可直接访问属性、调用函数、监听信号,复杂数据推荐 QAbstractListModel。

在 Qt Quick 项目中,C++ 负责处理业务逻辑、数据计算、系统调用等重任务,QML 则专注界面描述与用户交互。两者通过 QObject 暴露、信号与槽、属性绑定、上下文属性 等机制高效协作——不是“谁调用谁”,而是“各司其职、双向通信”。
1. 将 C++ 类暴露给 QML(核心步骤)
QML 无法直接访问普通 C++ 类,必须继承 QObject,并用 Q_OBJECT 宏声明,再注册到 QML 引擎。
- 在 C++ 类头文件中:继承
QObject,添加Q_OBJECT,用Q_PROPERTY声明可被 QML 读写的属性,用Q_INVOKABLE或slots标记可被 QML 调用的函数 - 在 main.cpp 中:调用
qmlRegisterType注册为 QML 类型;或用("MyModule", 1, 0, "MyCppClass") engine.rootContext()->setContextProperty("myObj", &instance)注入单例对象 - 在 QML 文件中:
import MyModule 1.0后即可像写原生类型一样使用:MyCppClass { id: cppObj }
2. QML 调用 C++ 函数与修改属性
只要函数标记为 Q_INVOKABLE 或是 public slot,QML 就能直接调用;Q_PROPERTY 属性支持自动绑定,QML 修改会触发 C++ 的 setter(如果定义了),反之亦然。
- 示例 C++ 属性:
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)→ QML 中cppObj.name = "Hello"自动调用setName() - 示例可调用函数:
Q_INVOKABLE void doWork(int value);→ QML 中cppObj.doWork(42) - 注意:参数和返回值类型需是 QML 支持的类型(如 int、QString、QList
、自定义注册类型等)
3. C++ 响应 QML 信号与主动通知 QML
QML 元素(如 Button)发出的信号,C++ 可以用 QObject::connect() 连接到槽函数;C++ 自己定义的信号(需 signals: 块 + Q_EMIT)也能被 QML 的 onSignalName 监听。
- QML 发信号给 C++:
button.clicked: cppObj.handleClicked()(推荐)或 C++ 侧用connect(qmlButton, &QQuickItem::clicked, &cppObj, &MyClass::handleClicked) - C++ 发信号给 QML:定义
signals: void dataReady(const QString& msg);,QML 中写onDataReady: console.log(msg) - 关键:信号参数类型要 QML 友好,避免裸指针或未注册的自定义类
4. 传递复杂数据:列表与模型
单纯传 QList 或 QVariantList 可行,但动态增删、性能要求高时,应使用 QAbstractListModel 子类,并在 QML 中用 ListView + model 绑定。
- C++ 模型需实现
rowCount()、data()、roleNames(),并调用beginInsertRows()/endInsertRows()通知变更 - 注册模型:用
setContextProperty("myModel", &model)或返回指针给 QML 的property var model - QML 中:
ListView { model: myModel; delegate: Text { text: model.name } },其中name对应 C++ 中 roleNames() 定义的角色名
基本上就这些。不复杂但容易忽略细节:头文件加 Q_OBJECT、moc 必须运行、类型注册时机要在 loadQml() 之前、跨线程通信需用 queued connection。跑通一个带属性+信号+简单调用的小例子,后续扩展就很自然了。


































