(七)C++与QML混合编程---在QML中使用C++对象

(七)C++与QML混合编程---在QML中使用C++对象,第1张

class="superseo">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

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 // COLORMAKER_H
#include "colormaker.h"
#include 
#include 

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 
#include 
#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 
#include 
#include 
#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 
#include 
#include 
#include 
#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 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);
		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;
        }
    }

}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/734235.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存