QML通过Q_PROPERTY和setContextProperty()访问C++属性,C++用QMetaObject::invokeMethod()调用QML函数,信号跨语言连接需参数严格匹配,复杂数据用QVariantList/QVariantMap传递。

QML如何读取C++对象的属性
QML不能直接访问C++普通类的成员变量,必须通过Q_PROPERTY声明并配合Q_OBJECT宏。关键点在于:属性需有可读函数(READ),且返回类型要能被QML识别(如int、QString、QVariant或已注册的自定义类型)。
常见错误是忘记调用setContextProperty()或未将C++对象设为QObject*指针传入:
class DataProvider : public QObject {
Q_OBJECT
Q_PROPERTY(QString message READ message NOTIFY messageChanged)
public:
QString message() const { return m_message; }
signals:
void messageChanged();
private:
QString m_message = "Hello from C++";
};
// 在main.cpp中:
QQmlApplicationEngine engine;
DataProvider provider;
engine.rootContext()->setContextProperty("dataProvider", &provider);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QML中即可用dataProvider.message访问——但注意:&provider生命周期必须长于引擎,否则会崩溃。
C++如何调用QML里的函数
使用QMetaObject::invokeMethod(),前提是QML对象已暴露为C++可访问的QObject*,且目标函数用function关键字定义、无参数或仅含基本类型参数。
立即学习“C++免费学习笔记(深入)”;
- QML中函数不能是箭头函数或内联匿名函数
- 参数类型不匹配(如传
Array给期望QVariantList的C++槽)会导致调用静默失败 - 必须确保QML组件已完成加载(
status === Loader.Ready)再获取对象
示例(QML):
Item {
function doSomething(value) {
console.log("Received:", value)
return "done"
}
}
C++侧调用:
QObject *qmlObj = engine.rootObjects().first();
QVariant returned;
QMetaObject::invokeMethod(qmlObj, "doSomething",
Q_RETURN_ARG(QVariant, returned),
Q_ARG(QVariant, 42));
// returned.toString() == "done"
信号与槽如何跨语言连接
C++发出的信号可被QML中的onSignalName自动捕获,QML发出的信号也能被C++用connect()监听——但双方信号参数必须严格匹配,且QML信号需在Signal对象中明确定义。
容易出错的地方:
- C++信号参数含自定义类型时,必须先调用
qRegisterMetaType并提供() Q_DECLARE_METATYPE - QML中监听未声明的信号(比如拼错名字)不会报错,只是不触发
- 用
Connections组件连接动态创建的QML对象时,需确保target非空,否则连接无效
QML监听C++信号示例:
// C++端信号:void dataUpdated(const QString &text, int count);
// QML中:
Connections {
target: dataProvider
onDataUpdated: {
console.log("New data:", text, count)
}
}
传递复杂数据结构(如列表、对象)的注意事项
QML原生支持Array和简单对象字面量,但C++侧若想传QList或嵌套结构,必须保证所有层级都由QML可序列化类型构成(QString、int、double、QVariantList、QVariantMap)。
不推荐直接传std::vector或自定义struct——即便用了QVariant::fromValue(),QML也无法解包。
正确做法:
- 用
QVariantList代替std::vector - 用
QVariantMap代替std::map - 若需传模型数据,优先走
QAbstractListModel+ListView,而非手动构造大JSON式对象
例如C++构造一个列表供QML遍历:
QVariantList makeItems() {
QVariantList list;
for (int i = 0; i < 3; ++i) {
QVariantMap item;
item["id"] = i;
item["name"] = QString("item_%1").arg(i);
list << item;
}
return list;
}
// 暴露为Q_PROPERTY(QVariantList items READ makeItems)
QML里用Repeater或ListView消费这类数据没问题,但别试图在C++里修改QVariantMap后指望QML自动响应——它不是活对象,只是快照。需要响应式更新,必须走属性通知或模型重载。











