在C++中与QML对象交互

简述

所有的 QML 对象类型 - 无论由引擎内部实现还是由第三方源定义,都是 QObject 派生的类型。这意味着,QML 引擎可以使用 Qt 元对象系统动态实例化任何 QML 对象类型并检查创建的对象。

这对于在 C++ 中创建 QML 对象非常有用,无论是显示一个可视化呈现的 QML 对象,还是将非可视 QML 对象数据集成到 C++ 应用程序中。一旦 QML 对象被创建,就可以从 C++ 中检查它,以便读取和写入属性、调用方法、以及接收信号通知。

  • 简述
  • 从 C 加载 QML 对象
  • 根据 objectName 访问加载的 QML 对象
  • 从 C 访问 QML 对象类型的成员
    • 属性
    • 调用 QML 方法
    • 连接到 QML 信号
      • 基本类型的信号参数
      • 对象类型的信号参数

版权所有:一去丶二三里,转载请注明出处:http://blog.csdn.net/liang19890820

从 C++ 加载 QML 对象

可以使用 QQuickView 或 QQmlComponent 来加载 QML 文档。QQmlComponent 将 QML 文档加载为一个 C++ 对象,然后可以从 C++ 代码中修改该对象。QQuickView 也做到了这一点,但由于 QQuickView 是一个基于 QWindow 的派生类,加载的对象也将被渲染至可视化显示,QQuickView 通常用于将一个可视化的 QML 对象集成到应用程序的用户界面中。

例如,有一个 QML 文件,如下所示:

// main.qml
import QtQuick 2.3

Item {
    width: 100; height: 100
}

可以使用 QQmlComponent 或 QQuickView 的 C++ 代码加载该 QML 文档。当使用 QQmlComponent 时,需要调用 QQmlComponent::create() 来创建组件的新实例:

// 使用 QQmlComponent
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
QObject *object = component.create();
//...
delete object;

而 QQuickView 会自动创建组件的实例,该实例可以通过 QQuickView::rootObject() 来访问:

// 使用 QQuickView
QQuickView view;
view.setSource(QUrl("qrc:/main.qml"));
view.show();
QObject *object = view.rootObject();

实例(object)创建后,就可以使用 QObject::setProperty() 或者 QQmlProperty 来修改其属性:

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

或者,将 object 转换为其实际类型,并使用编译时安全性调用方法。在这种情况下,main.qml 的基本对象是一个 Item,由 QQuickItem 类定义:

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

根据 objectName 访问加载的 QML 对象

QML 组件实质上是具有子对象的对象树,子对象有兄弟,也有孩子。可以使用 QObject::objectName 属性和 QObject::findChild() 来定位 QML 组件的子对象。

例如,如果 QML 中的根 Item 有一个 Rectangle 子项:

// main.qml
import QtQuick 2.3

Item {
    width: 100; height: 100

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

可以通过这样来定位孩子:

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

注意: 一个对象可能有多个具有相同 objectName 的子项,这种情况下,QObject::findChildren() 可用于查找具有匹配 objectName 的所有子项。

警告: 虽然可以使用 C++ 深入对象树中访问和操作 QML 对象,但建议不要在应用程序测试和原型设计之外采用此方法。QML 和 C++ 集成的一个优势是实现 QML 用户界面独立于 C++ 逻辑和数据集后端,如果 C++ 端深入到 QML 组件中直接操作它们,这种策略就会被打破。对于 C++ 实现,最好尽可能少地了解 QML 用户界面实现和 QML 对象树的组成。

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

属性

QML 中声明的任何属性都可以从 C++ 中访问。

例如,下面的 QML 声明了一个简单的字符串:

// main.qml
import QtQuick 2.3

Item {
    property string hey: "Hello, Qter!"
}

在 C++ 中,属性 hey 的值可以使用 QQmlProperty 来设置和读取,也可使用 QObject::setProperty() 和 QObject::property():

QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
QObject *object = component.create();

qDebug() << "Property value:" << QQmlProperty::read(object, "hey").toString();
QQmlProperty::write(object, "hey", "Hello, Qt!");

qDebug() << "Property value:" << object->property("hey").toString();
object->setProperty("hey", "Hello, QML!");
qDebug() << "Property value:" << object->property("hey").toString();

输出如下:

Property value: “Hello, Qter!”
Property value: “Hello, Qt!”
Property value: “Hello, QML!”

注意: 应该始终使用 QObject::setProperty()、QQmlProperty 或 QMetaProperty::write() 来改变 QML 的属性值,以确保 QML 引擎感知属性的变化。

例如,有一个自定义类型 PushButton,它有一个 buttonText 属性。在内部,该属性以成员变量 m_buttonText 来反映值,可以像下面这样直接修改成员变量:

// 糟糕的代码
QQmlComponent component(engine, "MyButton.qml");
PushButton *button = qobject_cast<PushButton*>(component.create());
button->m_buttonText = "Click me";

但这并不是一个好主意。由于值被直接改变,绕过了 Qt 元对象系统,QML 引擎并没有意识到属性的变化。这意味着,绑定到 buttonText 的属性不会被更新,并且 onButtonTextChanged 处理程序也不会被调用。

调用 QML 方法

所有的 QML 方法都被暴露给了 Qt 元对象系统,可以使用 QMetaObject::invokeMethod() 从 C++ 中调用。从 QML 传递的方法参数和返回值在 C++ 中被转换为 QVariant 值。

写一个简单的 QML,并为其添加一个方法:

// main.qml
import QtQuick 2.3

Item {
    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        return "Hello, Qter!"
    }
}

然后,在 C++ 中使用 QMetaObject::invokeMethod() 进行调用:

// main.cpp
QQmlEngine engine;
QQmlComponent component(&engine, QUrl("qrc:/main.qml"));
QObject *object = component.create();

QVariant returnedValue;  // 返回值
QVariant msg = "Hello, QML!";  // 方法参数
// 调用 QML 方法
QMetaObject::invokeMethod(object, "qmlFunction",
                          Q_RETURN_ARG(QVariant, returnedValue),
                          Q_ARG(QVariant, msg));

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

输出如下:

Got message: Hello, QML!
QML function returned: “Hello, Qter!”

注意: QMetaObject::invokeMethod() 的 Q_RETURN_ARG() 和 Q_ARG() 参数必须被指定为 QVariant 类型,因为这是用于 QML 方法参数和返回值的通用数据类型。

连接到 QML 信号

所有的 QML 信号在 C++ 中都是可用的,和普通的 Qt C++ 信号一样,可以使用 QObject::connect() 进行连接。反过来,任何 C++ 信号可以由 QML 对象使用信号处理器来接收。

基本类型的信号参数

这里有一个 QML 组件,包含一个名为 qmlSignal 的信号,该信号包含一个 string 类型参数:

// main.qml
import QtQuick 2.3

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

    // QML 信号
    signal qmlSignal(string msg)

    MouseArea {
        anchors.fill: parent
        // 点击鼠标,发射信号
        onClicked: item.qmlSignal("Hello, Qter!")
    }
}

写一个 C++ 类,并实现一个槽函数,用于接收 QML 发射的信号:

// qter.h
#ifndef QTER_H
#define QTER_H

#include <QObject>
#include <qDebug>

class Qter : public QObject
{
    Q_OBJECT

public slots:
    // 槽函数
    void cppSlot(const QString &msg) {
        qDebug() << "Called the C++ slot with message:" << msg;
    }
};

#endif // QTER_H

将信号连接至 C++ 对象的槽函数,当发出 qmlSignal 信号时,就会调用:

// qter.h
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include "qter.h"

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

    QQuickView view(QUrl("qrc:/main.qml"));
    QObject *item = view.rootObject();

    Qter qter;
    // 连接信号槽
    QObject::connect(item, SIGNAL(qmlSignal(QString)), &qter, SLOT(cppSlot(QString)));

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

点击鼠标后,输出如下:

Called the C++ slot with message: “Hello, Qter!”

对象类型的信号参数

当信号的参数为 QML 对象类型时,应使用 var 作为参数类型,并且在 C++ 中应使用 QVariant 类型接收该值。

例如,将上述示例中的参数改为 QML 对象类型:

// main.qml
import QtQuick 2.3

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

    // QML 信号
    signal qmlSignal(var object)

    MouseArea {
        anchors.fill: parent
        // 点击鼠标,发射信号
        onClicked: item.qmlSignal(item)
    }
}

要接收该信号,C++ 类中的槽函数的参数应该改为 QVariant 类型:

// qter.h
#ifndef QTER_H
#define QTER_H

#include <QObject>
#include <QQuickItem>
#include <qDebug>

class Qter : public QObject
{
    Q_OBJECT

public slots:
    // 槽函数
    void cppSlot(const QVariant &v) {
        qDebug() << "Called the C++ slot with value:" << v;

        QQuickItem *item = qobject_cast<QQuickItem*>(v.value<QObject*>());
        qDebug() << "Item Size:" << item->width() << item->height();
    }
};

#endif // QTER_H

当然,连接信号槽的的参数类型也需要修改:

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

点击鼠标后,输出如下:

Called the C++ slot with value: QVariant(QObject*, QQuickItem_QML_0(0x1f4b4cb38d0))
Item Size: 100 100

时间: 2024-09-17 14:53:05

在C++中与QML对象交互的相关文章

从领域、对象、角色、职责、对象交互、场景等方面去分析和设计领域模型(附源码)

好久没有写文章了,最近比较忙,另一方面也是感觉自己在这方面没什么实质性的突破.但是今天终于感觉自己小有所成,有些可以值得和大家分享的东西,并且完成了两个可以表达自己想法的Demo.因此,趁现在有点时间,是写文章和大家分享的时候了. 首先给出这两个Demo的源代码的压缩包的下载地址,因为之前有博友说他没有装VS2010而没办法运行Demo,所以这次我分别用VS2008和VS2010实现了两个版本. http://files.cnblogs.com/netfocus/DCIBasedDDD.rar

《Unreal Engine 4蓝图可视化编程》一第1章 使用蓝图进行对象交互

第1章 使用蓝图进行对象交互 Unreal Engine 4蓝图可视化编程 当开始开发一个游戏时,你想到的第一步应该是建立一个原型.幸运的是,虚幻引擎4和蓝图让基本的游戏功能实现起来比以往任何时候都更容易.这样用户便可以很快地开始测试自己的想法.为了让大家熟悉虚幻编辑器(Unreal Editor)和蓝图(Blueprint),我们将使用一些自带的资源和蓝图建立游戏玩法机制. 本章我们将学习以下内容. 创建新的项目和关卡. 在关卡中置入对象. 通过蓝图改变对象的材质. 使用蓝图编辑器链接所有的蓝

Android中WebView与Js交互的实现方法_Android

获取WebView对象 调用WebView对象的getSettings()方法,获取WebSettings对象 调用WebSettings对象的setJavaScriptEnabled()方法,设置js可用,参数:布尔值 在判断是否支持js的时候,不要用alert(),默认不起作用,可以先用document.write()测试 调用WebView对象的addJavascriptInterface(obj, interfaceName)方法,添加js接口,参数:Object对象,String接口名

Android中WebView与Js交互的实现方法

获取WebView对象 调用WebView对象的getSettings()方法,获取WebSettings对象 调用WebSettings对象的setJavaScriptEnabled()方法,设置js可用,参数:布尔值 在判断是否支持js的时候,不要用alert(),默认不起作用,可以先用document.write()测试 调用WebView对象的addJavascriptInterface(obj, interfaceName)方法,添加js接口,参数:Object对象,String接口名

ai 插件与flex界面-如何把adobe illustrutor 中的art对象得到缩略图并传到flex面版上绘出

问题描述 如何把adobe illustrutor 中的art对象得到缩略图并传到flex面版上绘出 最近,开发一个AI的插件,在AI的图片传到flex上卡住,望各位高手不吝赐教

HTML5中的Range对象的研究

一:Range对象的概念  Range对象代表页面上的一段连续区域,通过Range对象,可以获取或修改页面上的任何区域,可以通过如下创建一个空的Range对象,如下:       var  range = document.createRange(); 在html5中,每一个浏览器窗口及每一个窗口中都有一个selection对象,代表用户鼠标在页面中所选取的区域,(注意:经过测试IE9以下的浏览器不支持Selection对象), 可以通过如下语句创建selection对象:    var  se

.NET中的Response对象

Asp.Net中的Response对象的方法如下表所示,下面将向大家详细介绍它的重要方法. WriteFile 将文件输出到客户端 Write 将数据输出到客户端浏览器 Redirect 将网页重新转到另一地址 Flush 将缓冲区的数据输出到客户端浏览器 End 停止并结束ASP网页的处理 Close 关闭客户端的联机 ClearHeaders 清除缓冲区中的页面标题 Clear 清除缓冲区的数据 BinaryWrite 将二进制字符或字符串输出到客户端浏览器 AppendToLog 将自定义

在SQL中调用COM对象

对象 在SQLSERVER中创建 OLE 对象实例,有时我们想到数据库中执行存储过程的时候,同时调用系统中的COM对象.此时我们可以采用SQL的系统存储过程sp_OACreate ,此存储过程的调用要有一定的权限,只有 sysadmin 固定服务器角色的成员才能执行 sp_OACreate. 语法sp_OACreate progid, | clsid,    objecttoken OUTPUT    [ , context ] 参数progid 是要创建的 OLE 对象的程序标识符 (Prog

在ASP中利用“正则表达式” 对象实现UBB风格的论坛

ubb|对象|正则 上一次,我们谈到在ASP中如何利用"正则表达式"对象来实现各种数据的校验,文中描述了正则表达式对象的强大功能,接下来,我们来看看有关"正则表达式"对象的其他功能.当我们在网上冲浪的时候,尤其是浏览各类论坛的时候,经常会见到"UBB代码"这个词语.什么是UBB代码呢?UBB代码是HTML的一个变种http://www.alixixi.com/program/a/,是Ultimate Bulletin Board (国外一个BBS