StupidBeauty
Read times:412Posted at:Sat Apr 27 03:19:57 2019 - no title specified

Qt 5.12文档翻译:在C++中与QML对象进行交互,Interacting with QML Objects from C++

内容目录

在C++代码中载入QML 对象

通过对象名字来访问到被载入的QML 对象

在C++代码中访问某个QML 对象类型的成员

属性

调用QML中的方法

连接到QML 中的信号

所有 的QML 对象,无论它们是 由引擎内部实现的,还是 由第三方代码定义的 都是继承自 QObject 的类型。 这就意味着, QML引擎能够利用Qt 元对象系统 来动态地实例化任意QML 对象类型,并对创建出来的对象进行操作

这样,就可以在C++代码中创建QML 对象了,可以用于创建一个需要显示出来的可见QML 对象,也可以用于将某个不可见的QML 对象集成到C++程序中。一旦某个QML对象被创建出来了,就可以在C++代码中对它进行操作,以便读取或写入它的属性、调用它的方法、以及接收信号通知。

在C++代码中载入QML 对象

可通过 QQmlComponent QQuickView 来载入一个QML 文档。 QQmlComponent 会将 QML文档载入 为一个C++对象,使得日后 可通过C++代码对它进行修改。 QQuickView 也会实现相同的功能, 不过,由于 QQuickView 是继承自 QWindow 的类,所以 被载入的对象还会同时 被渲染到屏幕上; QQuickView 通常用于 将可显示的QML 对象集成到程序的界面中去。

例如,假设 有以下的某个 MyItem.qml 文件:

import QtQuick 2.0

Item {

width: 100 ; height: 100

}

这个 QML文档, 可使用以下C++代码通过 QQmlComponent QQuickView 来载入。如果使用 的是 QQmlComponent ,就需要调用 QQmlComponent::create ()来创建该组件的一个实例,而如果使用 QQuickView 的话 就会自动创建该组件的一个实例,并且日后可通过 QQuickView::rootObject ()来访问它:

// 使用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();

此处 object ,即是 刚被创建的 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 对象的高度( height )发生了变化,那么,其宽度( width )将会再次变化,因为它们之间的绑定关系仍然处于活跃状态。然而,如果 是在调用了 QQmlProperty(object, "width").write(500) 之后,高度( height )发生了变化,那么,宽度( width )将不会跟着变化,因为它们之间的绑定关系已经消失。

或者,妳也可以将 此处 对象转换成它的实际类型, 并调用它的方法 ,以确保 编译期安全 在这个示例中, MyItem.qml 的基类对象是 Item ,而它又是由 QQuickItem 类来定义的:

QQuickItem *item = qobject_cast< QQuickItem *>( object );

item->setWidth( 500 );

妳还可以连接 到该组件的任意信号或调用该组件的方法,对应的接口是 QMetaObject::invokeMethod ()和 QObject::connect ()。参考下文 中的 调用QML 方法 连接 QML 的信号 ,以了解更多细节。

通过对象名字来访问到被载入的QML 对象

QML组件 ,本质上就是对象树,其中的子代对象拥有相邻节点和各自的子代对象。QML 组件 的子代对象,可通过 QObject::findChild ()方法和 QObject::objectName 属性来定位到。例如,假设 MyItem.qml 中的根元素拥有一个 Rectangle 子代元素:

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 会为它的代理(delegate)创建多个实 ,因此 ,如果其代理声明了某个特定的对象名字(objectName),那么,这个 ListView 就会拥有多个具有相同对象名字( objectName )的子代对象。 在这种情况下,可使用 QObject::findChildren ()来找到所有拥有对应对象名字( objectName )的子代对象。

警告 尽管我们提供了这种能力,使得能够在C++代码中访问QML 对象并且对它们进行操作,但是,我们并不建议这么做,除非是出于测试目的和原型开发目的。将QML 与C++整合起来使用的做法,所具有的其中一个优点就是,能够以 QML 来实现界面,让它与C++侧的逻辑和数据后端相分离。如果在C++侧直接对QML 进行操作,这种分离就失效了。一旦这么做了之后,要想改动QML 界面而又不影响对应的C++代码,就很难做到了。

在C++代码中访问某个QML 对象类型的成员

属性

在QML 对象中声明的任意属性,都会自动成为在C++中可访问的属性。对于以下这个QML 元素:

// MyItem.qml

import QtQuick 2.0

Item {

property int someNumber: 100

}

可通过 QQmlProperty QObject::setProperty ()和 QObject::property ()配套使用 someNumber 属性的值进行设置和读取:

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 ,它拥有一个 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方法 ,都被暴露给了元对象系统,因而 可以在C++代码中通过 QMetaObject::invokeMethod ()来调用。对于QML 的方法参数和返回值,在C++中都会被转换成 QVariant 值来处理。

以下是一个利用 QMetaObject::invokeMethod ()来调用QML 方法的C++程序:

QML

// MyItem.qml

import QtQuick 2.0

Item {

function myQmlFunction(msg) {

console.log( "Got message:" , msg)

return "some return value"

}

}

C++

// main.cpp

QQmlEngine engine;

QQmlComponent component(&engine, "MyItem.qml" );

QObject * object = component.create();

QVariant returnedValue;

QVariant msg = "Hello from C++" ;

QMetaObject ::invokeMethod( object , "myQmlFunction" ,

Q_RETURN_ARG( QVariant , returnedValue),

Q_ARG( QVariant , msg));

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

delete object ;

注意,在调用 QMetaObject::invokeMethod ()时,传递给 Q_RETURN_ARG ()和 Q_ARG ()的参数,必须指定为 QVariant 类型,因为,这是在QML 方法中针对参数和返回值的通用数据类型。

连接到QML 中的信号

QML 中的所有信号,都是可以在C++代码中使用的,所以 可以像对待普通的Qt C++信号那样,使用 QObject::connect ()来连接。 反过来,任何的 C++信号 ,都可以被QML对象用 信号处理 来接收到。

以下的QML组件中,带有一个名为 qmlSignal 的信号,它在发射时,会带有一个字符串类型的参数。 在代码中,使用 QObject::connect ()将这个信号连接到了一个C++对象的信号槽上,这样,每当 qmlSignal 信号被发射时, cppSlot() 方法都会被调用:

// MyItem.qml

import QtQuick 2.0

Item {

id: item

width: 100 ; height: 100

signal qmlSignal( string msg)

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对象类型用作信号 的参数,那么,应当将它的类型写作 var 而在C++代码中,应当以 QVariant 类型来接收它的值:

// MyItem.qml

import QtQuick 2.0

Item {

id: item

width: 100 ; height: 100

signal qmlSignal( var anObject)

MouseArea {

anchors.fill: parent

onClicked: item.qmlSignal(item)

}

}

class MyClass : 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 dimensions:" << item->width()

<< item->height();

}

};

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

QApplication 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 ();

}

Your opinions

Your name:Email:Website url:Opinion content:
- no title specified

HxLauncher: Launch Android applications by voice commands