来自c++的QML对象交互

所有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();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340