QML中使用C++类
Qt提供了两种在QML中使用C++对象的方式:
- 在C++中实现一个类,注册为QML环境的一个类型,在QML环境中使用该类创建对象。
- 在C++中构造一个对象,将这个对象设置为QML的上下文属性,在QML环境中直接使用该属性。
定义可以导出的C++类
要想将一个类或对象导出到QML中,必须满足下面两个条件:
- 从QObject或QOjbect的派生类继承
- 使用Q_OBJECT宏
这两个条件也是使用QT信号槽的前提,就是为了让一个类能够进入QT的元对象系统中,只有使用元对象系统,一个类的某些方法或属性才可能通过字符串形式的名字来调用,才具有了在QML中访问的基础条件。而具有一下特征的属性或者方法才可以被QML访问。 (1)信号、槽 只要是信号或者槽,都可以在QML中访问,可以把C++对象的信号连接到QML中定义的方法上,也可以把QML对象的信号连接到C++对象的槽上,还可以直接调用C++对象的槽或信号。
class ColorMaker:public QObject
{
public:
ColorMaker(QObject* parent = 0);
signals:
void colorChanged(const QColor& color);
void currentTime(const QString& strTime);
public slots:
void start();
void stop();
};
被signals和slots修饰的信号和槽都可以在QML中使用。 (2)Q_INVOKABLE宏 在定义一个类的成员函数时使用Q_INVOKABLE宏来修饰,就可以让该方法被元对象系统调用,这个宏必须放在返回类型前面。
class ColorMaker:public QObject
{
public:
ColorMaker(QObject* parent = 0);
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void colorChanged(const QColor& color);
void currentTime(const QString& strTime);
public slots:
void start();
void stop();
};
一旦使用Q_INVOKABLE将某个方法注册到元对象系统中,在QML中就可以用${Objcet}. ${method}来访问了。 (3)Q_ENUMS宏 使用Q_ENUMS宏可以将类中的枚举类型注册到元对象系统中供QML使用。
class ColorMaker:public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
public:
ColorMaker(QObject* parent = 0);
enum GenerateAlgorithm
{
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void colorChanged(const QColor& color);
void currentTime(const QString& strTime);
public slots:
void start();
void stop();
};
一旦使用Q_ENUMS宏注册了枚举类型,在QML中就可以用${CLASS_NAME} . ${ENUM_VALUE}的形式来访问,例如ColorMaker.RandomRGB。 (4)Q_PROPERTY宏 Q_PROPERTY顾名思义,可以通过它把类中的成员属性注册到元对象系统中供QML使用,在QML中可以访问修改,也可以在属性发生变化时反射特定的信号。
Q_PROPERTY(type name
READ getFunction
[WRITE setFunction]
[RESET resetFunction]
[NOTIFY notifySignal]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
看起来比较复杂,不过不是所有的选项都必须设定,看一个简短的额属性声明:
Q_PROPERTY(int x READ x)
上面声明了一个类型为int,名称为x的属性,通过x()方法来访问。 其实在我们实际使用中,很少能够用全Q_PROPERTY的所有选项,就拿往QML导出类这种情景来说,比较常用的是READ、WRITE、NOTIFY三个选项:
- READ,如果你没有为属性指定MEMBER标记,则READ标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性
- WRITE,可选配置,声明一个设定属性的函数,它指定的函数没有返回值,只能有一个与属性类型匹配的参数
- NOTIFY,可选配置,给属性关联一个信号(该信号必须是已经在类中声明过的),当属性值发生变化时就会触发该信号,信号的参数,一般就是你定义的属性
现在给ColorMaker类添加一些属性,以便QML可以获取、设置颜色值:
#ifndef COLORMAKER_H
#define COLORMAKER_H
#include<QObject>
class ColorMaker:public QObject
{
Q_OBJECT
Q_ENUMS(GenerateAlgorithm)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QColor timeColor READ timeColor)
public:
ColorMaker(QObject* parent = 0);
enum GenerateAlgorithm
{
RandomRGB,
RandomRed,
RandomGreen,
RandomBlue,
LinearIncrease
};
QColor color() const;
void setColor(const QColor& color);
QColor timeColor() const;
Q_INVOKABLE GenerateAlgorithm algorithm() const;
Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm);
signals:
void colorChanged(const QColor& color);
void currentTime(const QString& strTime);
public slots:
void start();
void stop();
protected:
void timerEvent(QTimerEvent* e);
private:
GenerateAlgorithm m_algorithm;
QColor m_currentColor;
int m_nColorTimer;
};
#endif
#include "colormaker.h"
#include <QTimerEvent>
#include <QDateTime>
ColorMaker::ColorMaker(QObject* parent)
:QObject(parent)
,m_algorithm(RandomRed)
,m_currentColor(Qt::black)
,m_nColorTimer(0)
{
qsrand(QDateTime::currentDateTime().toTime_t());
}
QColor ColorMaker::color() const
{
return m_currentColor;
}
void ColorMaker::setColor(const QColor &color)
{
m_currentColor = color;
emit 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::start()
{
if(m_nColorTimer == 0)
{
m_nColorTimer = startTimer(1000);
}
}
void ColorMaker::timerEvent(QTimerEvent *e)
{
if(e->timerId() == m_nColorTimer)
{
switch (m_algorithm)
{
case RandomRGB:
m_currentColor.setRgb(qrand() % 255, qrand() % 255, qrand() % 255);
break;
case RandomGreen:
m_currentColor.setGreen(qrand() % 255);
break;
case RandomRed:
m_currentColor.setRed(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 colorChanged(m_currentColor);
emit currentTime(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
}
else
{
QObject::timerEvent(e);
}
}
上面的例子使用一个周期为1000毫秒的定时器来产生颜色,定时器触发时根据算法来构造新的颜色值,发射colorChanged信号,同时也发射一个currentTime信号。
注册一个QML可用的类型
前面已经实现了可以供QML访问的类,现在看看怎样将一个C++类注册为QML类型,以及怎样在QML中使用这个类型。 要达到这种目的,大概可以分为4步: 1.实现C++类 2.注册QML类型 3.在QML中导入类型 4.在QML中创建由C++导出的类型的实例并使用 ColorMaker类已经实现了,下面看看其他三个步骤。 (1)注册QML类型 要注册一个QML类型,有多种方法可用,如qmlRegisterSingletonType()用来注册一个单例类型,qmlRegisterType()注册一个非单例类型,qmlRegisterTypeNotAvailable()注册一个类型用来展位,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头文件。 typename:模板参数typename即为C++类的类名。 uri:指定一个唯一的包名,例如“import QtQuick.Controls 1.2”,其中的“QtQuick.Controls”就是包名uri,而1.1则是版本,是versionMajor和versionMinor的组合 qmlName:QML中可以使用的类名
下面是ColorMaker示例的mian.cpp文件:
#include <QGuiApplication>
#include <QQuickView>
#include "colormaker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<ColorMaker>("an.qt.ColorMaker",1,0,"ColorMaker");
QQuickView viewer;
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.setSource(QUrl("qrc:/main.qml"));
viewer.show();
return app.exec();
}
(2)在QML中导入C++注册的类型 一旦在C++中注册好了QML类型,就可以在QML文档中引入你注册的包,然后使用注册的类型了。
import an.qt.ColorMaker 1.0
(3)在QML中创建C++导入类型的实例 引入包后,就可以在QML中创建C++导入类型的对象了,与QML内建类型的使用完全一样,下面是创建一个ColorMaker实例的代码:
Rectangle{
width:360;
height:360;
ColorMaker{
id:colorMaker;
color:Qt.green;
}
}
完整ColorMaker实例
前面C++代码展示过了,下面是main.qml代码。
import QtQuick 2.6
import QtQuick.Window 2.2
import an.qt.ColorMaker 1.0
import QtQuick.Controls 1.2
Rectangle {
width: 360;
height: 360;
Text {
id: timeLabel;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 4;
font.pixelSize: 36;
}
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;
onClicked: {
colorMaker.start();
}
}
Button{
id: stop;
text: "stop";
anchors.left: start.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
colorMaker.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);
}
}
Button{
id: quit;
text: "quit";
anchors.left: colorAlgorithm.right;
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;
onCurrentTime:{
timeLabel.text = strTime;
timeLabel.color = colorMaker.timeColor;
}
}
Connections{
target: colorMaker;
onColorChanged:{
colorRect.color = color;
}
}
}
在QML中使用C++对象
将一个C++对象导出为QML属性
上面介绍了怎样将C++类导出为QML可以使用的类型,下面介绍如何把C++中创建的对象作为属性传递到QML环境中,然后在QML环境中访问。 (1)注册属性 要将一个对象注册为属性很简单,ColorMaker的main.cpp修改后如下:
#include <QGuiApplication>
#include <QQuickView>
#include <QtQml>
#include "colormaker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView viewer;
viewer.setResizeMode(QQuickView::SizeRootObjectToView);
viewer.rootContext()->setContextProperty("colorMaker", new ColorMaker);
viewer.setSource(QUrl("qrc:/main.qml"));
viewer.show();
return app.exec();
}
注意新增加的语句
viewer.rootContext()->setContextProperty("colorMaker", new ColorMaker);
正是这行代码从堆上分配了一个ColorMaker对象,然后注册为QML上下文的属性,起了个名字就叫colorMaker。 viewer.rootContext()返回的是QQmlContext对象,QQmlContext类代表一个QML上下文,它的setContextProperty()方法可以为该上下文设置一个全局可见的属性,需要注意的是,new出来的对象,QQmlContext只是使用,不会帮你删除,你需要找一个合适的时机删除它。 如果使用QQmlApplicationEngine+Window的程序结构,main函数应该是这样的:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlEngine>
#include <QtQml>
#include "colormaker.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("colorMaker", new ColorMaker);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
另外要注意的是,因为去掉了qmlRegisterType()调用,所以在main.qml中不能再访问ColorMaker类了,不能通过类名来访问枚举类型了。 (2)在QML中使用关联到C++对象的属性 修改后的main.qml如下:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
Rectangle {
width: 360;
height: 360;
Text {
id: timeLabel;
anchors.left: parent.left;
anchors.leftMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 4;
font.pixelSize: 36;
}
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;
onClicked: {
colorMaker.start();
}
}
Button{
id: stop;
text: "stop";
anchors.left: start.right;
anchors.leftMargin: 4;
anchors.bottom: start.bottom;
onClicked: {
colorMaker.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);
}
}
Button{
id: quit;
text: "quit";
anchors.left: colorAlgorithm.right;
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;
onCurrentTime:{
timeLabel.text = strTime;
timeLabel.color = colorMaker.timeColor;
}
}
Connections{
target: colorMaker;
onColorChanged:{
colorRect.color = color;
}
}
}
|