以下内容为本人的学习笔记, 如需要转载, 请声明原文链接 [englyf] https://www.jianshu.com/p/66649d0e9bb6
请注意这里使用的环境是
- IDE:Qt5.12
- Lang:C++,QML
- Compiler:vs2017x64
所有 QML 对象都是 QObject 的派生类型, 无论这个对象是引擎的内部实现或者是由第三方源定义而来. 也就是说, QML 引擎可以利用 Qt 的元对象系统(Meta Object System https://doc.qt.io/qt-5/metaobjects.html
)去动态实例化任何的 QML 对象类型, 以及检查被创建的对象.
所以说, 在 C++ 代码中, 无论是因为要显示一个可渲染的 QML 对象, 或者需要集成非可视化的 QML 对象数据, 创建 QML 对象都是非常容易实现的. 当一个 QML 对象被创建之后, 为了读写这个对象的属性, 调用这个对象的方法和接收这个对象的信号通知, 都能够在 C++ 中对此进行检查.
怎么在 C++ 中加载 QML 对象呢?
我们可以使用 Qt 提供的两个类来加载 QML 文档, 分别是
QQmlComponent https://doc.qt.io/qt-5/qqmlcomponent.html 和 QQuickView https://doc.qt.io/qt-5/qquickview.html .
QQmlComponent https://doc.qt.io/qt-5/qqmlcomponent.html 加载一个 QML 文档后, 生成一个 C++ 对象, 可以在 C++ 代码中对这个对象进行修改.
QQuickView https://doc.qt.io/qt-5/qquickview.html 同样可以做到这些, 但 QQuickView https://doc.qt.io/qt-5/qquickview.html 是 QWindow https://doc.qt.io/qt-5/qwindow.html 的一个派生类型, 加载之后的对象也会被渲染出来. QQuickView https://doc.qt.io/qt-5/qquickview.html 通常被用来将可视化的 QML 对象集成到应用的用户界面中.
下面看个举个栗子,
- // textItem.qml
- import QtQuick 2.0
- Item {
- width: 100; height: 100
- }
既可以用 QQmlComponent https://doc.qt.io/qt-5/qqmlcomponent.html 也可以用 QQuickView https://doc.qt.io/qt-5/qquickview.html 将上面这个 QML 文件加载到 C++ 代码中. 如果使用 QQmlComponent https://doc.qt.io/qt-5/qqmlcomponent.html 则需要调用 QQmlComponent::create https://doc.qt.io/qt-5/qqmlcomponent.html#create () 创建组件的实例并返回对象 (实例) 指针, 而使用 QQuickView https://doc.qt.io/qt-5/qquickview.html
就会自动创建组件的实例, 然后调用 QQuickView::rootObject https://doc.qt.io/qt-5/qquickview.html#rootObject ()
获取实例指针.
- // Using QQmlComponent
- QQmlEngine engine;
- QQmlComponent component(&engine,
- QUrl::fromLocalFile("textItem.qml"));
- QObject *object = component.create();
- ...
- delete object;
- // Using QQuickView
- QQuickView view;
- view.setSource(QUrl::fromLocalFile("textItem.qml"));
- view.show();
- QObject *object = view.rootObject();
怎么在 C++ 中设置 QML 对象的属性呢?
现在可以调用上面实例的 QObject::setProperty https://doc.qt.io/qt-5/qobject.html#setProperty () 或者借用 QQmlProperty::write https://doc.qt.io/qt-5/qqmlproperty.html#write () 来修改 Item 的属性:
- object->setProperty("width", 500);
- QQmlProperty(object, "width").write(500);
这两种方式是有区别的, 后者 QQmlProperty::write() 除了设置属性之外, 还会移除原来的绑定, 所以这里要特别注意一下.
比如, 假设在 QML 文件中, 已将 width 绑定到 height:
width: height
如果设置属性的方式是调用 object->setProperty("width", 500), 那么 width 的值只是临时被设置为 500, 一旦 height 改变了, width 也是会跟随改变的, 因为绑定关系没有被移除. 但是, 如果设置属性的方式是调用 QQmlProperty(object, "width").write(500) , 那么 width 的值不会再跟随 height 的改变而改变, 因为原来的绑定关系已被移除.
此外呢, 设置属性还有一种方法就是, 先将对象强制转换为实际类型, 然后使用编译时安全性调用方法. 在上面的文件 textItem.qml 中, Item 由类 QQuickItem https://doc.qt.io/qt-5/qquickitem.html 定义:
- QQuickItem *item = qobject_cast<QQuickItem*>(object);
- item->setWidth(500);
怎么在 C++ 中按照对象名访问已加载的 QML 对象呢?
QML 组件实际上是一组具有子节点的对象树, 子节点同样有兄弟对象和子对象. 可以使用 QObject::findChild https://doc.qt.io/qt-5/qobject.html#findChild () 并传入属性值 QObject::objectName(也即是对象名) 来定位到 QML 组件的子对象. 下面看看我这的栗子大不大:
- // textItem.qml
- import QtQuick 2.0
- Item {
- width: 100; height: 100
- Rectangle {
- anchors.fill: parent
- objectName: "rect"
- }
- }
可以看到根项是 Item, 然后还有个子项 Rectangle https://doc.qt.io/qt-5/qml-qtquick-rectangle.html . 可以通过下面的方式定位子对象:
- QObject *rect = object->findChild<QObject*>("rect");
- if (rect)
- rect->setProperty("color", "red");
这里要注意一下, 一个对象可以有多个相同 objectName(属性) 的子对象. 比如, ListView 创建其委托的多个实例, 如果使用特定的 objectName 声明其委托, 则 ListView 将具有多个相同 objectName 的子节点. 这种情况下, 可以使用 QObject::findChildren https://doc.qt.io/qt-5/qobject.html#findChildren-1 () 来查找符合 objectName 的所有子节点.
特别注意: 虽然可以在 C++ 中访问并且操作 QML 对象, 但是除了测试和原型设计之外, 这种方法是不推荐的! QML 和 C++ 集成的优势之一就是实现与 C++ 逻辑和数据集后端分离的 UI 界面, 如果在 C++ 中直接操作 QML 将意味着放弃优势. 这种方法也使得在不影响对应 C++ 部分的前提下去改动 QML UI 变得困难.
怎么在 C++ 中访问 QML 对象类型的成员呢?
访问属性
任何 QML 对象中声明的属性都自动可以在 C++ 代码中访问. 下面再来个栗子:
- // textItem.qml
- import QtQuick 2.0
- Item {
- property int a: 100
- }
在上面 QML 文件中声明的属性 a 的值可以使用 QQmlProperty https://doc.qt.io/qt-5/qqmlproperty.html 来读写, 或者用 QObject::setProperty https://doc.qt.io/qt-5/qobject.html#setProperty () 来写属性值和用 QObject::property https://doc.qt.io/qt-5/qobject.html#property () 来读属性值:
- QQmlEngine engine;
- QQmlComponent component(&engine, "MyItem.qml");
- QObject *object = component.create();
- qDebug() <<"Property value:" << QQmlProperty::read(object, "someNumber").toInt();
- QQmlProperty::write(object, "someNumber", 5000);
- qDebug() << "Property value:" << object->property("someNumber").toInt();
- object->setProperty("someNumber", 100);
为了确保 QML 引擎知道属性的改变, 你应该始终采用 QObject::setProperty https://doc.qt.io/qt-5/qobject.html#setProperty (), QQmlProperty https://doc.qt.io/qt-5/qqmlproperty.html 或者 QMetaProperty::write https://doc.qt.io/qt-5/qmetaproperty.html#write () 来设置 QML 对象的属性值. 比如, 假如你有个自定义类型 PushButton, 在内部有个属性 buttonText 并且和成员变量 m_buttonText 关联. 像下面这样子直接修改成员变量 m_buttonText 是不受推荐的:
- // un-recommended
- QQmlComponent component(engine, "textItem.qml");
- PushButton *button = qobject_cast<PushButton*>(component.create());
- button->m_buttonText = "clicked !";
如果变量 m_buttonText 被直接修改, 那么 QML 引擎将不会知道属性改变了, 因为这种操作完美地躲开了 Qt 的 meta-object system https://doc.qt.io/qt-5/metaobjects.html . 后果就是, 绑定的 buttonText 属性不会被更新, 而且属性变更信号槽 onButtonTextChanged() 不会被调用.
访问 QML 方法
由于所有的 QML 方法都暴露给了元对象系统 Meta-object system, 所以在 C++ 代码中可以通过 QMetaObject::invokeMethod() 调用对应的 QML 方法, 并且输入的参数和来自 QML 中的返回值在 C++ 中通常被转换成 QVariant https://doc.qt.io/qt-5/qvariant.html 值. 下面有个例子:
- // textItem.qml
- import QtQuick 2.0
- Item {
- function qmlFunction(msg) {
- console.log("Got msg:", msg)
- return "return value"
- }
- }
- // main.cpp
- QQmlEngine engine;
- QQmlComponent component(&engine, "textItem.qml");
- QObject *object = component.create();
- QVariant returnedValue;
- QVariant msg = "hi from C++";
- QMetaObject::invokeMethod(object, "qmlFunction",
- Q_RETURN_ARG(QVariant, returnedValue),
- Q_ARG(QVariant, msg));
- qDebug() << "value returned from QML :" << returnedValue.toString();
- delete object;
这里要注意一下, 参数 () and https://doc.qt.io/qt-5/qmetaobject.html#Q_ARG () 必须指定为 QVariant https://doc.qt.io/qt-5/qvariant.html 类型, 是因为 QVariant https://doc.qt.io/qt-5/qvariant.html 是用于 QML 方法输入参数和返回值的通用数据类型.
连接 QML 信号
- // 未完待续 !!!
- //
参考英文资料[Qt]https://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html
来源: http://www.jianshu.com/p/66649d0e9bb6