来自c++的QML对象交互

11 篇文章 0 订阅
8 篇文章 1 订阅

所有QML对象类型都是qobject派生的类型,无论它们是由引擎内部实现的还是由第三方源定义的。这意味着QML引擎可以使用Qt元对象系统动态实例化任何QML对象类型,并检查创建的对象。
这对于从c++代码创建QML对象很有用,无论是显示一个可以可视化呈现的QML对象,还是将非可视化的QML对象数据集成到c++应用程序中。一旦创建了QML对象,就可以在c++中对其进行检查,以便读取和写入属性、调用方法和接收信号.

1.从c++加载QML对象

QML文档可以通过QQmlComponent或QQuickView加载。QQmlComponent加载一个QML文档作为一个c++对象,然后可以从c++代码中修改它。QQuickView也这样做,但由于QQuickView是一个qwindow派生类,加载的对象也将被渲染成可视化显示;QQuickView通常用于将可显示的QML对象集成到应用程序的用户界面中。
例如,假设有一个MyItem。QML文件,看起来像这样:

import QtQuick 2.0

Item {
    width: 100; height: 100
}

与来自c++的QML对象交互
所有QML对象类型都是qobject派生的类型,无论它们是由引擎内部实现的还是由第三方源定义的。这意味着QML引擎可以使用Qt元对象系统动态实例化任何QML对象类型,并检查创建的对象。
这对于从c++代码创建QML对象很有用,无论是显示一个可以可视化呈现的QML对象,还是将非可视化的QML对象数据集成到c++应用程序中。一旦创建了QML对象,就可以从c++中对其进行检查,以便对属性进行读写

// Using QQmlComponent
QQmlEngine engine;
QQmlComponent component(&engine,
        QUrl::fromLocalFile("MyItem.qml"));
QObject *object = component.create();
...
delete object;
// Using QQuickView
QQuickView view;
view.setSource(QUrl::fromLocalFile("MyItem.qml"));
view.show();
QObject *object = view.rootObject();

这个对象是MyItem的实例。已创建的QML组件。你现在可以使用QObject::setProperty()或者QQmlProperty::write()来修改条目的属性:

object->setProperty("width", 500);
QQmlProperty(object, "width").write(500);

QObject::setProperty()和QQmlProperty::write()的区别在于,后者除了设置属性值之外,还会删除绑定。例如,假设上面的width赋值是对height的绑定:

width: height

如果在object->setProperty("width", 500)之后Item的高度发生了变化,那么宽度将再次更新,因为绑定仍然是活动的。但是,如果在调用QQmlProperty(object, "width").write(500)之后改变了高度,那么宽度将不会改变,因为绑定不再存在了。
或者,您可以将对象强制转换为其实际类型,并调用具有编译时安全性的方法。在本例中,MyItem的基对象。qml是一个Item,由QQuickItem类定义:

QQuickItem *item = qobject_cast<QQuickItem*>(object);
item->setWidth(500);

你也可以使用QMetaObject::invokeMethod()和QObject::connect()连接到任何信号或调用组件中定义的方法。请参阅下面的调用QML方法和连接到QML信号了解更多细节。

2.通过对象名称访问已加载的QML对象

QML组件本质上是对象树,它的子组件有兄弟和自己的子组件。QML组件的子对象可以使用QObject::objectName属性和QObject::findChild()来定位。例如,如果MyItem中的根项。qml有一个子矩形项:

import QtQuick 2.0

Item {
    width: 100; height: 100

    Rectangle {
        anchors.fill: parent
        objectName: "rect"
    }
}

孩子可以这样定位:

QObject *rect = object->findChild<QObject*>("rect");
if (rect)
    rect->setProperty("color", "red");

注意,一个对象可以有多个具有相同objectName的子对象。例如,ListView创建了它的委托的多个实例,所以如果它的委托是用一个特定的objectName声明的,ListView将有多个具有相同objectName的子对象。在这种情况下,QObject::findChildren()可用于查找具有匹配objectName的所有子对象。

警告:虽然可以从c++访问QML对象并操作它们,但这不是推荐的方法,除非用于测试和原型设计。QML和c++集成的一个优点是能够在QML中实现与c++逻辑和数据集后端分离的ui,如果c++端开始直接操作QML,这就会失败。这种方法还使得更改QML UI变得困难,而不会影响它的c++对应版本。

3 从c++访问QML对象类型的成员

Properties

在QML对象中声明的任何属性都可以从c++中自动访问。给定一个这样的QML条目:

// MyItem.qml
import QtQuick 2.0

Item {
    property int someNumber: 100
}

someNumber属性的值可以使用QQmlProperty或者QObject::setProperty()和QObject::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);

你应该总是使用QObject::setProperty(), QQmlProperty或QMetaProperty::write()来改变一个QML属性值,以确保QML引擎知道属性的改变。例如,假设你有一个自定义类型PushButton,它带有一个内部反映m_buttonText成员变量值的buttonText属性。像这样直接修改成员变量并不是一个好主意:

//bad code
QQmlComponent component(engine, "MyButton.qml");
PushButton *button = qobject_cast<PushButton*>(component.create());
button->m_buttonText = "Click me";

由于值是直接改变的,这就绕过了Qt的元对象系统,并且QML引擎不会意识到属性的改变。这意味着buttonText的属性绑定不会被更新,任何onButtonTextChanged处理程序都不会被调用。

4 .调用QML方法

所有QML方法都公开给元对象系统,可以从c++中使用QMetaObject::invokeMethod()调用。可以在冒号字符后指定参数和返回值的类型,如下面的代码片段所示。这可能很有用,例如,当您想要将一个带有特定签名的c++信号连接到一个qml定义的方法时。如果省略类型,c++签名将使用QVariant。

下面是一个c++应用程序,使用QMetaObject::invokeMethod()调用QML方法:

// MyItem.qml
import QtQuick 2.0

Item {
    function myQmlFunction(msg: string) : string {
        console.log("Got message:", msg)
        return "some return value"
    }
}
// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();

QString returnedValue;
QString msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
        Q_RETURN_ARG(QString, returnedValue),
        Q_ARG(QString, msg));

qDebug() << "QML function returned:" << returnedValue;
delete object;

注意冒号后面指定的参数和返回类型。您可以使用基本类型和对象类型作为类型名称。
如果在QML中省略了类型,那么在调用QMetaObject::invokeMethod时,必须用Q_RETURN_ARG()和Q_ARG()指定QVariant作为类型。

5.连接到QML信号

所有QML信号对c++都是自动可用的,并且可以像任何普通的Qt c++信号一样使用QObject::connect()来连接。作为回报,QML对象可以使用信号处理程序接收任何c++信号。


这里有一个QML组件,它带有一个名为qmlSignal的信号,该信号是通过一个字符串类型的参数发出的。该信号使用QObject::connect()连接到c++对象的插槽,因此每当发出qmlSignal时,就会调用cppSlot()方法:

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(msg: string)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal("Hello from QML")
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QString)),
                     &myClass, SLOT(cppSlot(QString)));

    view.show();
    return app.exec();
}

信号参数中的QML对象类型在c++中被转换为指向该类的指针.

// MyItem.qml
import QtQuick 2.0

Item {
    id: item
    width: 100; height: 100

    signal qmlSignal(anObject: Item)

    MouseArea {
        anchors.fill: parent
        onClicked: item.qmlSignal(item)
    }
}
class MyClass : public QObject
{
    Q_OBJECT
public slots:
    void cppSlot(QQuickItem *item) {
       qDebug() << "Called the C++ slot with item:" << item;

       qDebug() << "Item dimensions:" << item->width()
                << item->height();
    }
};

int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);

    QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
    QObject *item = view.rootObject();

    MyClass myClass;
    QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
                     &myClass, SLOT(cppSlot(QVariant)));

    view.show();
    return app.exec();
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meta.Qing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值