QtQuick技术的引入,使得我们能够快速构建流畅的用户界面,具有动画、各种绚丽效果的UI都不在话下,但是,它又不是万能的,也有很多局限性,原来的Qt的一些技术,比如网络编程QTcpSocket、多线程、又如XML文档处理类库QXmlStreamReader、QXmlStreamWirter、再如文件操作QFile、QTextStream等,在QMl中要不不可用,要不用起来不方便,所以,很多时候我们会混合使用QML和C++,使用QML构建界面,使用C++实现非界面的业务逻辑和复杂的运算。接下来,让我们一起来看看,QML与C++直接是如何进行交互的。
Qt提供了两种在QML环境使用C++对象的方式:
不管哪种方式,对要导出的C++类都有要求,不是一个类的所有方法、变量都可以在QML语境中使用,接下来,一起看看怎么让一个方法或属性可以被QML代码访问吧!
前提条件
想要将一个类或者对象导出到QML中,下列条件必须满足:
和使用QT信号槽的前提条件一样,目的是为了让一个类能进入QT强大的元对象系统,只有使用了元对象系统,才能让一个类的方法或属性可能通过字符串形式的名字来调用,才具有在QML中访问的基础条件。
具有什么特征的属性或者方法才可以被QML访问呢?
(1) 信号、槽
只要是信号或者槽,都可以在QML中访问,可以把C++对象的信号连接到QML定义的方法上,也可以把QML对象的信号连接到C++对象的槽上,还可以直接调用C++对象的槽或者信号。
(2) Q_INVOKABLE宏
在定义一个类的成员函数时,使用Q_INVOKABLE宏修饰,就可以让该方法让元对象系统调用。这个宏必须放在返回值类型前面。
Q_INVOKABLE void function();
(3) Q_ENUMS宏
如果你要导出的类定义了想在QML中使用的枚举类型,可以使用Q_ENUMS宏将该枚举类型注册到元对象系统中。这样,QML代码中就可以使用导出的枚举类型了。
Q_ENUMS(ColorType);
enum ColorType{
Red,
Green,
Blue,
};
(4) Q_PROPERTY宏
Q_PRORETY用来定义可通过元对象系统访问的属性,通过他定义的属性,可以在QML中访问、修改,也可以在修改的时候发出特定的信号,要想使用Q_PROPERTY宏,你的类必须是QObject的派生类,必须在类首使用Q_OBJECT宏。
下面是Q_PROPERTY宏的原型:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
看着是不是很复杂? 你可以为一个属性,可以设定的选项数超过10个,是不是很头疼? 不过,不是所有选项都必须设置,来看一个简单的例子:
Q_PROPERTY(int x READ x)
定义了一个类型为int,名为x的属性,通过方法x()来访问。
常用参数的含义:
type name:type属性类型,可以是int、float、QString、QObject、QColor、QFont等,name是属性的名字;
READ: 如果你没有为属性指定MEMBER标记,则READ标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性。
WRITR: 可选配置。声明一个设定属性的函数。它指定的函数没有返回值,只有一个能与属性类型相匹配的参数。
NOTIFY: 可选配置。给属性关联一个信号(该信号必须是已经在类中声明过的),当属性的值发生变化时,就会触发该信号。信号的参数,一般就是你定义的属性。
其他标记的含义,可查阅Qt帮助手册。
#ifndef WORKER_H
#define WORKER_H
#include
#include
class worker : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChange)
public:
worker(QObject *parent = nullptr);
QColor color(){
return m_color;
}
void setColor(QColor color){
m_color = color;
}
signals:
void colorChange(QColor color);
private:
QColor m_color;
};
#endif // WORKER_H
学习了怎么实现一个可供QML访问的类,接下来我们一起来看看,怎样将一个C++类型注册为一个QML类型,以及怎样在QML中使用这个类型
要达到这种目的,大概可以分为4步:
要注册一个QML类型,有以下方法:
在这,我只是说说常规的类型注册,其他的可以参考QT官方文档:
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);
qmlRegisterType()是个函数模板,有两个原型,第一个原型一般用来注册一个新类型,第二个原型可以为特定的版本注册类型,而这原型牵涉到Qt Quick是类型和版本机制,比较复杂,所以来说说第一个原型的使用。
使用qmlRegisterType()需要包含头文件 #include 或者 #include ;
qmlRegisterType 各参数代表的意思:
#include
#include
#include
#include "worker.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Worker>("my.qt.Worker",1,0,"Worker");
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();
}
上面代码,将Worker 类注册为QML类型,主版本为1,次版本为0,包名则是my.qt.Worker。
注意:注册动作要放在QML 上下文创建之前,否则不起作用。
一旦你在C++中注册好了QML类型,就可以在qml文档中引入你注册的包,然后使用注册的类型。
import my.qt.Worker 1.0
import QtQuick 2.12
import my.qt.Worker 1.0
Rectangle {
visible: true
width: 300
height: 200
MyWorker{
id:worker
onColorChange: {
console.log("color change");
rect.color = color
}
}
Rectangle{
id: rect
anchors.fill: parent
Button{
id:button
anchors.centerIn: parent
text: "testButton"
onClicked: {
worker.color = "grey"
console.log("button clicked")
}
}
}
Component.onCompleted: {
worker.color = "blue"
}
}
#ifndef Worker_H
#define Worker_H
#include
#include
#include
class Worker : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChange)
public:
Worker(QObject *parent = nullptr);
QColor color(){
return m_color;
}
void setColor(QColor color){
m_color = color;
emit colorChange(color);
}
signals:
void colorChange(QColor color);
private:
QColor m_color;
};
#endif // Worker_H
上面看了怎样将C++类导出为QML可以使用的类型,你还可以将C++中创建的队形作为属性传递到QML环境中,然后在QML环境中访问。
#include
#include
#include
#include
#include "worker.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// qmlRegisterType("my.qt.Worker",1,0,"MyWorker");
QGuiApplication app(argc,argv);
QQuickView viewer;
viewer.rootContext()->setContextProperty("myWorker",new Worker);
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.setSource(QUrl("qrc:/main.qml"));
viewer.show();
return app.exec();
}
我在viewer后增加了setContextProperty();正是这行代码,在堆上分配了一个Worker对象,然后注册为QML上下文属性,起了个名字叫myWorker,
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
Rectangle {
visible: true
width: 300
height: 200
Rectangle{
id: rect
anchors.fill: parent
Button{
id:button
anchors.centerIn: parent
text: "testButton"
onClicked: {
myWorker.setColor("red")
console.log("button clicked")
}
}
}
Connections{
target: myWorker
onColorChange:{
rect.color = color
}
}
}
转下篇…