文章目录
一、前言
Qt Quick技术的引入,使得我们能够快速构建流畅的用户界面,具有动画、各种绚丽效果的UI都不在话下。但它不是万能的,也有很多局限性,原来Qt的一些技术,比如低阶的网络编程如QTcpSocket、多线程,又如XML文档处理类库QXmlStreamReader、QXmlStreamWriter,再如文件操作QFile、QTextStream等,在QML中要么不可用,要么用起来不方便,所以,很多时候我们会基于这样的原则来混合使用QML和C++:使用QML构建界面,使用C++实现非界面的业务逻辑和复杂运算。
二、在QML中使用C++类和对象
QML其实是对ECMAScript的扩展,融合了Qt Object系统,它是一种新的解释型语言,QML引擎虽然由Qt C++实现,但QML对象的运行环境,说到底和C++对象的上下文环境是不同的,是平行的两个世界。
如果你想在QML中访问C++对象,那么必然要找到一种途径在两个运行环境之间建立沟通桥梁。Qt提供了两种在QML环境中使用C++对象的方式:
- 在C++中实现一个类,注册为QML环境的一个类型,在QML环境中使用该类型创建对象;(导出C++类到QML)
- 实现C++类;
- 注册QML类型;
- 在QML中导入类型;
- 在QML中创建由C++导出的类型的实例并使用
- 在C++中构造一个对象,将这个对象设置为QML的上下文属性,在QML环境中直接使用该属性;(导出C++对象到QML)
2.1、导出C++类到QML
2.1.1、定义可以导出的C++类
前提条件
要想将一个类或对象导出到QML中,下列的前提条件必须满足:
- 从QObject或QObject的派生类继承;
- 使用Q_OBJECT宏;
看起来好像和使用信号与槽的前提条件一样,没错,的确是一样的。这两个条件是为了让一个类能够进入Qt强大的元对象系统,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才具有了在QML中访问的基础条件。
一旦你导出了一个类,在QML中就必然要访问该类的实例的属性或方法来达到某种目的,而具有什么特征的属性或方法才可以被QML访问呢?
(1)信号、槽
只要是信号或者槽,都可以在QML中访问,你可以吧C++对象的信号连接到QML中定义的方法上,也可以把QML对象的信号连接到C++对象的槽上,还可以直接调用C++对象的槽或信号。
///ColorMaker类
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
class ColorMaker : public QObject
{
Q_OBJECT
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
signals:
void sig_colorChanged(const QColor& color);
void sig_currentTime(const QString& strTime);
public slots:
void slot_start();
void slot_stop();
};
#endif // COLORMAKER_H
定义的两个槽slot_start()、slot_stop()和定义的两个信号sig_colorChanged()、sig_currentTime()都可以在QML中使用;
(2)Q_ENUMS宏
如果你要导出的类定义了想在QML使用的枚举类型,可以使用Q_ENUMS
宏将该枚举类型注册到元对象系统中
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
enum GenerateAlgorithm {
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
signals:
void sig_colorChanged(const QColor& color);
void sig_currentTime(const QString& strTime);
public slots:
void slot_start();
void slot_stop();
};
#endif // COLORMAKER_H
ColorMaker
类中添加了GenerateAlgorithm
枚举类型 ;
一旦使用了Q_ENUMS
宏注册了枚举类型,在QML中就可以用${CLASS_NAME}.${ENUM_VALUE}
的形式来访问了,例如:ColorMaker.LinearIncrease
(3)Q_INVOKABLE宏
在定义一个类的成员函数时使用Q_INVOKABLE
宏来修饰,就可以让该方法被元对象系统调用。
这个宏必须放在返回类型前面。
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
enum GenerateAlgorithm {
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void sig_colorChanged(const QColor& color);
void sig_currentTime(const QString& strTime);
public slots:
void slot_start();
void slot_stop();
};
#endif // COLORMAKER_H
一旦使用了Q_INVOKABLE
宏将某个方法注册到元对象系统中,在QML中就可以用${Object}.${method}
来访问了,例如:
Component.onCompleted: {
colorMaker.setAlgorithm(Colormaker.LinearIncrease);
}
(4)Q_PROPERTY宏
Q_PROPERTY宏用来定义可通过元对象系统访问的属性,通过它定义的属性,可以在QML中访问、修改,也可以在属性变化时发射特定的信号。
要想使用Q_PROPERTY:
- 类必须是QObject的后裔;
- 必须在类首使用Q_OBJECT宏;
关于Q_PROPERTY宏的解释请参考:学习Qt之属性系统详解
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
#include <QColor>
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY sig_colorChanged);
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
enum GenerateAlgorithm {
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
QColor getColor() const;
void setColor(const QColor& color);
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void sig_colorChanged(const QColor& color);
void sig_currentTime(const QString& strTime);
public slots:
void slot_start();
void slot_stop();
private:
GenerateAlgorithm m_algorithm;
QColor m_currentColor;
};
#endif // COLORMAKER_H
至此,C++类就定义完成了,下面是C++类的完整代码:
ColorMaker类完整代码
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include <QObject>
#include <QColor>
class ColorMaker : public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY sig_colorChanged);
Q_PROPERTY(QColor timeColor READ timeColor);
public:
explicit ColorMaker(QObject *parent = nullptr);
~ColorMaker();
enum GenerateAlgorithm {
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
QColor getColor() const;
void setColor(const QColor& color);
QColor timeColor() const;
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void sig_colorChanged(const QColor& color);
void sig_currentTime(const QString& strTime);
public slots:
void slot_start();
void slot_stop();
protected:
void timerEvent(QTimerEvent *event) override;
private:
GenerateAlgorithm m_algorithm;
QColor m_currentColor;
int m_nColorTimer;
};
#endif // COLORMAKER_H
#include "colormaker.h"
ColorMaker::ColorMaker(QObject *parent)
: QObject(parent)
,m_algorithm(RandomRGB)
,m_currentColor(Qt::black)
{
}
ColorMaker::~ColorMaker()
{
}
QColor ColorMaker::getColor() const
{
return m_currentColor;
}
void ColorMaker::setColor(const QColor& color)
{
m_currentColor = color;
emit sig_colorChanged(m_currentColor);
}
QColor ColorMaker::timeColor() const
{
QTime time = QTime::currentTime();
int r = time.hour();
int g = time.minute()*2;
int b = time.second()*4;
return QColor::fromRgb(r,g,b);
}
ColorMaker::GenerateAlgorithm ColorMaker::algorithm() const
{
return m_algorithm;
}
void ColorMaker::setAlgorithm(GenerateAlgorithm algorithm)
{
m_algorithm = algorithm;
}
void ColorMaker::slot_start()
{
if(m_nColorTimer == 0) {
m_nColorTimer = startTimer(1000);
}
}
void ColorMaker::slot_stop()
{
if(m_nColorTimer > 0) {
killTimer(m_nColorTimer);
m_nColorTimer = 0;
}
}
void ColorMaker::timerEvent(QTimerEvent *event)
{
if(event->timerId() == m_nColorTimer) {
switch (m_algorithm) {
case RandomRGB:
m_currentColor.setRgb(qrand()%255,qrand()%255,qrand()%255);
break;
case RandomRed:
m_currentColor.setRed(qrand()%255);
break;
case RandomGreen:
m_currentColor.setGreen(qrand()%255);
break;
case RandomBlue:
m_currentColor.setBlue(qrand()%255);
break;
case LinearIncrease:
int r = m_currentColor.red() + 10;
int g = m_currentColor.green() + 10;
int b = m_currentColor.blue() + 10;
m_currentColor.setRgb(r%255,g%255,b%255);
break;
}
emit sig_colorChanged(m_currentColor);
emit sig_currentTime(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
}else {
QObject::timerEvent(event);
}
}
使用一个周期为1000毫秒的定时器来产生颜色,定时器触发时根据算法来构造新的颜色值,发射sig_colorChanged信号和sig_currentTime信号;
2.1.2、注册一个QML可用的类型
准备好了一个可供QML访问的类之后,还需要将C++类型注册为QML类型;
(1)注册QML类型
要注册一个QML类型,有多种方法可用,例如:
- 用
qmlRegisterSingletonType()
来注册一个单例类型; - 用
qmlRegisterType()
来注册一个非单例类型; - 用
qmlRegisiterTypeNotAvailable()
来注册一个类型用来占位; - 用
qmlRegisterUncreatableType()
来注册一个具有附加属性的附加类型
qmlRegisterType()是个模板函数,有两个原型:
template<typename T>
int qmlRegisterType(const char* uri, int versionMajor, int versionMinor, const char* qmlName);
template<typename T, int metaObjectRevision>
int qmlRegisterType(const char* uri, int versionMajor, int versionMinor, const char* qmlName);
前一个原型一般用来注册一个新类型,而后一个可以为特定的版本注册类型;后面这个牵涉到Qt Quick的类型和版本机制,三言两语不能言尽,在此单说前一个原型的使用;
要使用qmlRegisterType
,需要包含QQmlEngine
或QtQml
头文件
template<typename T>
int qmlRegisterType(const char* uri, int versionMajor, int versionMinor, const char* qmlName);
//typename: 就是实现的C++类的类名
//uri: 指定一个唯一的包名,类似于Java中的那种,一是用来避免名字冲突,二是可以吧多个相关类聚合到一个包中方便引用;
//比如我们常写的“import QtQuick.Control 1.2”,其中的“QtQuick.Controls”就是包名uri
//1.2是版本,是versionMajor和versionMinor的组合;
//qmlName则是QML中可以使用的类名;
注册实例
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "colormaker.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<ColorMaker>("an.qt.ColorMaker", 1, 0, "ColorMaker");
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
注册动作一定要放在QML上下文创建之前,否则的话,没有用;
上面的代码将ColorMaker
类注册为QML类ColorMaker
,主版本为1,次版本为0,起的包名是an.qt.ColorMaker
;
(2)在QML中导入C++注册的类型
一旦你在C++中注册好了QML类型,就可以在QML文档中引入你注册的包,然后使用注册的类型了。
要引入包,使用import语句,比如要使用我们注册的ColorMaker类,可以在QML文档中加入下面的import语句:
import an.qt.ColorMaker 1.0
(3)在QML中创建C++导入类型的实例
引入包后,你就可以在QML中创建C++导入类型的对象了,与QML内建类型的使用完全一样;
qml运行效果
qml完整代码
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import an.qt.ColorMaker 1.0
Window {
id: root;
width: 400;
height: 400;
visible: true;
Rectangle {
width: 360;
height: 360;
Text {
id: timeLabel
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 4;
font.pixelSize: 26;
}
ColorMaker {
id: colorMaker;
color: Qt.green;
}
Rectangle {
id: colorRect;
anchors.centerIn: parent;
width: 200;
height: 200;
color: "blue";
}
Button {
id: start;
text: "start";
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 4;
onClicked: {
colorMaker.slot_start();
}
}
Button {
id: stop;
text: "stop";
anchors.left: start.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
colorMaker.slot_stop();
}
}
function changeAlgorithm(button, algorithm) {
switch(algorithm) {
case 0:
button.text = "RandomRGB";
break;
case 1:
button.text = "RandomRed";
break;
case 2:
button.text = "RandomGreen";
break;
case 3:
button.text = "RandomBlue";
break;
case 4:
button.text = "LinearIncrease";
break;
}
}
Button {
id: colorAlgorithm;
text: "RandomRGB";
anchors.left: stop.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
var algorithm = (colorMaker.algorithm() + 1) % 5;
changeAlgorithm(colorAlgorithm,algorithm);
colorMaker.setAlgorithm(algorithm);
}
}
Button {
id: quit;
text: "quit";
anchors.left: colorAlgorithm.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
Qt.quit();
}
}
Component.onCompleted: {
colorMaker.color = Qt.rgba(0,180,120,255);
colorMaker.setAlgorithm(colorMaker.LinearIncrease);
changeAlgorithm(colorAlgorithm,colorMaker.algorithm());
}
Connections {
target: colorMaker;
onSig_currentTime: {
timeLabel.text = strTime;
timeLabel.color = colorMaker.timeColor;
}
}
Connections {
target: colorMaker;
onSig_colorChanged: {
colorRect.color = color;
}
}
}
}
2.2、导出C++对象到QML
上面讲了怎么把C++类导出为QML可以使用的类型,你还可以把C++中创建的对象作为属性传递到QML环境中,然后再QML环境中访问;
(1)注册属性
要将一个对象注册为属性很简单,例如:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "colormaker.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
//qmlRegisterType<ColorMaker>("an.qt.ColorMaker", 1, 0, "ColorMaker");
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("colorMaker", new ColorMaker);
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
engine.rootContext()
返回的是QQmlContext
对象,QQmlContext
类代表一个QML上下文,它的setContextProperty()
方法可以为该上下文设置一个全局可见的属性;- 注意:new出来的对象,
QQmlContext
只是使用,不会帮你删除,你需要自己找一个合适的时机来删除它;
(2)在QML中使用关联到C++对象的属性
一旦调用setContextProperty()
导出了属性,就可以在QML中使用了,不需要import语句
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
//import an.qt.ColorMaker 1.0
Window {
id: root;
width: 400;
height: 400;
visible: true;
Rectangle {
width: 360;
height: 360;
Text {
id: timeLabel
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 4;
font.pixelSize: 26;
}
/*
ColorMaker {
id: colorMaker;
color: Qt.green;
}
*/
Rectangle {
id: colorRect;
anchors.centerIn: parent;
width: 200;
height: 200;
color: "blue";
}
Button {
id: start;
text: "start";
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 4;
onClicked: {
colorMaker.slot_start();
}
}
Button {
id: stop;
text: "stop";
anchors.left: start.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
colorMaker.slot_stop();
}
}
function changeAlgorithm(button, algorithm) {
switch(algorithm) {
case 0:
button.text = "RandomRGB";
break;
case 1:
button.text = "RandomRed";
break;
case 2:
button.text = "RandomGreen";
break;
case 3:
button.text = "RandomBlue";
break;
case 4:
button.text = "LinearIncrease";
break;
}
}
Button {
id: colorAlgorithm;
text: "RandomRGB";
anchors.left: stop.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
var algorithm = (colorMaker.algorithm() + 1) % 5;
changeAlgorithm(colorAlgorithm,algorithm);
colorMaker.setAlgorithm(algorithm);
}
}
Button {
id: quit;
text: "quit";
anchors.left: colorAlgorithm.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
Qt.quit();
}
}
Component.onCompleted: {
colorMaker.color = Qt.rgba(0,180,120,255);
//colorMaker.setAlgorithm(colorMaker.LinearIncrease);
changeAlgorithm(colorAlgorithm,colorMaker.algorithm());
}
Connections {
target: colorMaker;
onSig_currentTime: {
timeLabel.text = strTime;
timeLabel.color = colorMaker.timeColor;
}
}
Connections {
target: colorMaker;
onSig_colorChanged: {
colorRect.color = color;
}
}
}
}
- main.qml代码主要修改了三处(注释了三处),因为我们导出的属性命名是colorMaker,和导出ColorMaker类是构造的实例的id一样,所以改动不多;
- 导出的属性可以直接使用,与属性关联的对象。它的信号、槽、可调用的方法(使用Q_INVOKABLE宏修饰的方法)、属性都可以使用;
- 不能通过类名来引用枚举值,需要使用对象(如colorMaker.LinearIncrease)或者数字字面量;
三、在C++中使用QML对象
我们可以使用QML对象的信号、槽,访问它们的属性,都没有问题,因为很多QML对象对应的类型,原本就是C++类型,比如Image对应QQuickImage,Text对应QQuickText等等;但是,这些与QML类型对应的C++类型都是私有的,你写的C++代码也不能直接访问,怎么办?
Qt最核心的一个基础特性,就是元对象系统,通过元对象系统,你可以查询QObject的某个派生类的类名、有哪些信号、槽、属性、可调用的方法等信息,然后也可以使用QMetaObject::invokeMethod()
调用QObject的某个注册到元对象系统中的方法。
而对于使用Q_PROPERTY定义的属性,可以使用QObject的property()方法访问属性,如果该属性定义了WRITE方法,还可以使用setProperty()修改属性。
所以,只要我们找到QML环境中的某个对象,就可以通过元对象系统来访问它的属性、信号、槽等。
3.1、查找一个对象的孩子
QObject类的构造函数有一个parent参数,可以指定一个对象的父亲,QML中的对象其实借助这个组成了以根Item为根的一棵对象树。
而QObject定义了一个属性objectName,这个对象名字属性,就可以用于查找对象。查找对象的方法:findChild()和findChildren():
T QObject::findChild(const QString& name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QString& name = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QRegExp& regExp, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
QList<T> QObject::findChildren(const QRegularExpression& re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const;
它们都是模板方法,从命名上也可以看出来,一个返回单个对象,一个返回对象列表。
我们以Qt Widgets为例来说明,如何查询一个或多个对象;
示例1:查找parentWidget的名为“button1”,类型为QPushButton的孩子
QPushButton* button = parentWidget->findChild<QPushButton*>("button1")
示例2:返回parentWidget的所有名为“widgetname”的QWidget类型的孩子列表
QList<QWidget*> widgets = parentWidget->findChildren<QWidget*>("widgetname")
3.2、使用元对象调用QML对象的方法
QMetaObject的invokeMethod()
方法用来调用一个对象的信号、槽、可调用方法。它是个静态方法,原型如下:
bool QMetaObjcet:invokeMethod(
QObject* obj,
const char* member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericReturnArgument vla0 = QGenericReturnArgument(0),
QGenericReturnArgument vla1 = QGenericReturnArgument(),
QGenericReturnArgument vla2 = QGenericReturnArgument(),
QGenericReturnArgument vla3 = QGenericReturnArgument(),
QGenericReturnArgument vla4 = QGenericReturnArgument(),
QGenericReturnArgument vla5 = QGenericReturnArgument(),
QGenericReturnArgument vla6 = QGenericReturnArgument(),
QGenericReturnArgument vla7 = QGenericReturnArgument(),
QGenericReturnArgument vla8 = QGenericReturnArgument(),
QGenericReturnArgument vla9 = QGenericReturnArgument());
- 返回值:返回true说明调用成功;返回false,要么是因为没有你说的那个方法,要么是参数类型不匹配;
- obj:被调用对象的指针;
- member:方法名字;
- type:连接类型;invokeMethod为信号槽而生,你可以指定连接类型,如果被调用的对象和发起调用的线程是同一线程,那么可以使用Qt::DirectConnection、Qt::AutoConnection、Qt::QueuedConnection,如果被调用对象在另一个线程,那么建议使用Qt::QueuedConnection;
- ret:接收返回值;
- 然后就是多达10个可以传递给被调用方法的参数;(看来信号槽的参数个数是有限制的,最好不要超过10个)
对于要传递给被调用方法的参数,使用QGenericReturnArgument 来表示,你可以使用Q_ARG宏来构造一个参数,它的定义是:
QGenericReturnArgument Q_ARG(Type, const Type& value);
返回类型是类似的,使用QGenericReturnArgument 表示,你可以使用Q_RETURN_ARG宏来构造一个接收返回值的参数,它的定义是:
QGenericReturnArgument Q_RETURN_ARG(Type, Type& value);
使用:假设一个对象有这么一个槽QString compute(QString, int, double)
,那么可以这样调用(同步方式):
QString retVal;
QMetaObject::invokeMethod(
obj,
"compute",
Qt::DirectConnection,
Q_RETURN_ARG(QString, retVal),
Q_ARG(QString, "sqrt"),
Q_ARG(int, 42),
Q_ARG(double, 9.7)
);
如果你要让一个线程对象退出,可以这么调用(队列连接方式):
QMetaMethod::invokeMethod(thread, "quit", Qt::QueuedConnection);
3.3、callQml示例
运行效果
示例包含:C++类ChangeColor、main.qml、main.cpp
C++类ChangeColor
#ifndef CHANGECOLOR_H
#define CHANGECOLOR_H
#include <QObject>
#include <QTimer>
class ChangeColor : public QObject
{
Q_OBJECT
public:
explicit ChangeColor(QObject* target,QObject *parent = nullptr);
~ChangeColor();
protected slots:
void onTimeout();
private:
QTimer m_timer;
QObject* m_target;
};
#endif // CHANGECOLOR_H
#include "changeColor.h"
#include <QDateTime>
#include <QColor>
#include <QVariant>
ChangeColor::ChangeColor(QObject* target,QObject *parent)
: QObject(parent)
, m_timer(this)
, m_target(target)
{
qsrand(QDateTime::currentDateTime().toTime_t());
connect(&m_timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
m_timer.start(1000);
}
ChangeColor::~ChangeColor()
{
}
void ChangeColor::onTimeout()
{
QColor color = QColor::fromRgb(qrand()%256,qrand()%256,qrand()%256);
m_target->setProperty("color",color);
}
- 这个类的作用就是定时器,每隔1秒改变m_target的颜色;
- 通过构造函数,传递target到m_target,使用
m_target->setProperty("color",color);
改变目标对象的颜色;
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
Window {
objectName: "rootObject";
visible: true;
width: 640;
height: 480;
title: qsTr("callQml")
Text {
objectName: "textLabel";
text: qsTr("Hello World");
anchors.centerIn: parent;
font.pixelSize: 26;
}
Button {
objectName: "quitButton";
anchors.right: parent.right;
anchors.rightMargin: 4;
anchors.bottom: parent.bottom;
anchors.bottomMargin: 4;
text: "quit";
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "changeColor.h"
#include <QMetaObject>
#include <QDebug>
#include <QColor>
#include <QVariant>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
//================================================================
QObject* root = NULL;
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 ChangeColor(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; //1.failed call
textLabel->setProperty("color", QColor::fromRgb(255,0,0));
bRet = QMetaObject::invokeMethod(textLabel,"doLayout");
qDebug()<<"call doLayout return - " << bRet;
}
//================================================================
return app.exec();
}
- 有关QML文档根对象的获取,如果使用QQuickView+Item的程序结构方式,通过QQuickView的rootObject()直接就可以得到QML文档的根Item对象;
- 如果采用QQmlApplicationEngine+Window的程序结构方式,QML文档根对象的获取就麻烦些;示例中通过QQmlApplicationEngine的rootObjects()方法获取到engine加载QML后生成的所有顶层对象的列表,然后遍历、比较objectName,根据QML中设置的对象名字“rootObject”来找到正确的QML根对象;
- 找到root,创建一个ChangeColor对象,并把root设置为它的m_target,该对象内部使用了一个定时器,一秒改变一次传入对象的颜色;
- 接着使用findChild()找到了quitButton和textLabel,将quitButton的clicked()信号连接到QGuiApplication的quit()槽上;
- 在textLabel上,首先我们企图使用invokeMethod()调用setText()方法来改变textLabel的文本,这个注定是会失败的,因为QML中的Text对象对应C++中的是QQuickText类,而QQuickText类没有名为“setText”的槽或者可调用方法;
- 使用setProperty()修改了textLabel的color属性,所以Hello World这行字变成了红色;