Qt最核心的一个基础特性,就是元对象系统,通过元对象系统,你可以查询QObject的某个派生类的类名,有哪些信号槽,属性,可调用方法等信息,然后也可以使用QMetaObject::invokeMethod()调用QObject的某个注册到元对象系统中的方法。而对于使用Q_PROPERTY定义的属性,可以使用QObject的property()方法访问属性,如果该属性定义了WRITE方法,还可以使用setProperty()修改属性。所以,只要我们找到QML环境中的某个对象,就可以通过元对象系统来访问它的属性、信号、槽等。
查找一个对象的孩子
QObject类的构造函数有一个parent参数,可以指定一个对象的父亲,QML中的对象其实借助这个组成了以根Item为根的一颗对象树。而QObject定义了一个属性objectName,这个对象名字属性,就可以用于查找对象。查找对象的方法有findChild()和findChildren():
template<typename T>
inline T findChild(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
return static_cast<T>(qt_qFindChild_helper(this, aName, ObjType::staticMetaObject, options));
}
template<typename T>
inline QList<T> findChildren(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
QList<T> list;
qt_qFindChildren_helper(this, aName, ObjType::staticMetaObject, reinterpret_cast<QList<void *> *>(&list), options);
return list;
}
使用元对象调用QML对象的方法
QMetaObject的invokeMethod()方法用来调用一个对象的信号、槽、可调用的方法,它是一个静态方法:
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(Q_NULLPTR),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
obj:被调用对象的指针
memeber:被调用对象的方法名字
connectType:连接类型,和connect最后一个参数一样
ret:方法返回值
val0,val1…:传递给被调用方法的参数
假设一个对象有一个槽QString slotFunc(QString,int,double),那么调用方式为:
QString ret;
QMetaObject::invokeMethod(obj,"slotFunc",Qt::DirectConnection,
Q_RETURN_ARG(QString,retVal),
Q_ARG(QString,"sqrt"),
Q_ARG(int,11),
Q_ARG(double,12.7)
示例
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Window {
objectName: "rootObject";
width: 360;
height: 360;
visible: true;
Text {
objectName: "textLabel";
text: "Hello World";
anchors.centerIn: parent;
font.pixelSize: 26;
}
Button {
anchors.right: parent.right;
anchors.rightMargin: 4;
anchors.bottom: parent.bottom;
text: "quit";
objectName: "quitButton";
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QQuickView>
#include <QtQml>
#include "changeqmlcolor.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
QObject *root = nullptr;
QList<QObject*> rootObjects = engine.rootObjects();
int count = rootObjects.size();
for (int i = 0; i < count; i++)
{
if (rootObjects.at(i)->objectName() == "rootObject")
{
root = rootObjects.at(i);
break;
}
}
new ChangeQmlColor(root);
QObject* quitButton = root->findChild<QObject*>("quitButton");
if (quitButton)
{
QObject::connect(quitButton, SIGNAL(clicked()), &app, SLOT(quit()));
}
QObject* textLabel = root->findChild<QObject*>("textLabel");
if (textLabel)
{
bool bRet = QMetaObject::invokeMethod(textLabel, "setText", Q_ARG(QString, "world hello"));
qDebug() << "call setText return -" << bRet;
textLabel->setProperty("color", QColor::fromRgb(255, 0, 0));
bRet = QMetaObject::invokeMethod(textLabel, "doLayout");
qDebug() << "call doLayout return -" << bRet;
}
return app.exec();
}
控制台输出为:
QMetaObject::invokeMethod: No such method QQuickText::setText(QString)
call setText return - false
call doLayout return - true
可以看出Text对象对应于QQuickText类,而QQuickText类没有setText方法,因此调用setText方法失败。