导入到QML中的C++类的槽函数可以有参数吗?与无参槽函数相比有什么需要注意的?求大神解惑

 我来答
匿名用户
2017-12-31
展开全部

1、QML与C++为什么要混合编程

QML与C++为什么要混合编程,简单来说,就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法,下面介绍了两者间交互的方法与技巧。

2、QML访问C++概述

Qt集成了QML引擎和Qt元对象系统,使得QML很容易从C++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。

QML访问C++有两个方法:一是在Qt元对象系统中注册C++类,在QML中实例化、访问。二是在C++中实例化并设置为QML上下文属性,在QML中直接使用。与后者相比,前者可以使C++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。

3、如何实现可以被QML访问的C++类

C++类要想被QML访问,首先必须满足两个条件:一是派生自QObject类或QObject类的子类,二是使用Q_OBJECT宏。QObject类是所有Qt对象的基类,作为Qt对象模型的核心,提供了信号与槽机制等很多重要特性。Q_OBJECT宏必须在private区(C++默认为private)声明,用来声明信号与槽,使用Qt元对象系统提供的内容,位置一般在语句块首行。下面例子在QtCreator3.1.2中创建,Projects选择QtQuickApplication,工程名为Gemini,Component选择QtQuick2.2,然后在自动生成的文件中添砖加瓦。

信号与槽——

(1)添加头文件Gemini.h

[cpp] view plain copy 

  • #ifndef GEMINI_H  

  • #define GEMINI_H  

  • // Gemini.h  

  • #include <QObject>  

  • #include <QDebug>  

  • class Gemini : public QObject  

  • {  

  • Q_OBJECT  

  • signals:  

  • void begin();  

  • public slots:  

  • void doSomething() {  

  • qDebug() << "Gemini::doSomething() called";  

  • }  

  • };  

  • #endif // GEMINI_H  

  • Gemini类中的信号begin()和槽doSomething()都可以被QML访问。槽必须声明为public或protected,信号在C++中使用时要用到emit关键字,但在QML中就是个普通的函数,用法同函数一样,信号处理器形式为on<Signal>,Signal首字母大写。信号不支持重载,多个信号的名字相同而参数不同时,能够被识别的只是最后一个信号,与信号的参数无关。

    (2)修改main.cpp

  • [cpp] view plain copy 
  • // main.cpp  

  • #include <QGuiApplication>  

  • #include <QQmlApplicationEngine>  

  • #include <QtQml>  

  • #include <Gemini.h>  

  • int main(int argc, char *argv[])  

  • {  

  • QGuiApplication app(argc, argv);  

  • qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  

  • QQmlApplicationEngine engine;  

  • engine.load(QUrl(QStringLiteral("qrc:///main.qml")));  

  • return app.exec();  

  • }  

  • 这里把Gemini类注册(qmlRegisterType)到了Qt元对象系统,当然也可以先实例化再设置为QML上下文属性,相关内容将在后面详细介绍。

    (3)修改main.qml

  • [sql] view plain copy 
  • // main.qml  

  • import QtQuick 2.2  

  • import QtQuick.Window 2.1  

  • import Union.Lotto.Gemini 1.0  

  • Window {  

  • visible: true  

  • width: 360; height: 360  

  • title: "Union Lotto Game"  

  • color: "white"  

  • MouseArea {  

  • anchors.fill: parent  

  • onClicked: {  

  • gemini.begin()  

  • }  

  • }  

  • Gemini {  

  • id: gemini  

  • onBegin: doSomething()  

  • }  

  • }  

  • Gemini类注册到Qt元对象系统后,并且在QML文件中导入(import),关键字Gemini就可以在当前QML文件中当作一种QML类型来用了。例子中有个MouseArea,单击鼠标时会发送begin()信号,进而调用doSomething()槽函数。

    枚举类型——

    (1)修改头文件Gemini.h

  • [cpp] view plain copy 
  • #ifndef GEMINI_H  

  • #define GEMINI_H  

  • // Gemini.h  

  • #include <QObject>  

  • #include <QDebug>  

  • class Gemini : public QObject  

  • {  

  • Q_OBJECT  

  • Q_ENUMS(BALL_COLOR)  

  • public:  

  • Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  

  • qDebug() << "Gemini::Gemini() called";  

  • }  

  • enum BALL_COLOR {  

  • BALL_COLOR_YELLOW,  

  • BALL_COLOR_RED,  

  • BALL_COLOR_BLUE,  

  • BALL_COLOR_ALL  

  • };  

  • signals:  

  • void begin();  

  • public slots:  

  • void doSomething(BALL_COLOR ballColor) {  

  • qDebug() << "Gemini::doSomething() called with" << ballColor;  

  • if(ballColor != m_ballColor) {  

  • m_ballColor = ballColor;  

  • qDebug() << "ball color changed";  

  • }  

  • }  

  • private:  

  • BALL_COLOR m_ballColor;  

  • };  

  • #endif // GEMINI_H  

  • Gemini类中添加了public的BALL_COLOR枚举类型,这个枚举类型要想在QML中使用,就用到了Q_ENUMS()宏。

    (2)修改main.qml

  • [plain] view plain copy 
  • // main.qml  

  • import QtQuick 2.2  

  • import QtQuick.Window 2.1  

  • import Union.Lotto.Gemini 1.0  

  • Window {  

  • visible: true  

  • width: 360; height: 360  

  • title: "Union Lotto Game"  

  • color: "white"  

  • MouseArea {  

  • anchors.fill: parent  

  • onClicked: {  

  • gemini.begin()  

  • }  

  • }  

  • Gemini {  

  • id: gemini  

  • onBegin: doSomething(Gemini.BALL_COLOR_RED)  

  • }  

  • }  

  • 在QML中使用枚举类型的方式是<CLASS_NAME>.<ENUM_VALUE>,例如Gemini.BALL_COLOR_RED。

    成员函数——

    (1)修改头文件Gemini.h

  • [cpp] view plain copy 
  • #ifndef GEMINI_H  

  • #define GEMINI_H  

  • // Gemini.h  

  • #include <QObject>  

  • #include <QDebug>  

  • class Gemini : public QObject  

  • {  

  • Q_OBJECT  

  • Q_ENUMS(BALL_COLOR)  

  • public:  

  • Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  

  • qDebug() << "Gemini::Gemini() called";  

  • }  

  • enum BALL_COLOR {  

  • BALL_COLOR_YELLOW,  

  • BALL_COLOR_RED,  

  • BALL_COLOR_BLUE,  

  • BALL_COLOR_ALL  

  • };  

  • Q_INVOKABLE void stop() {  

  • qDebug() << "Gemini::stop() called";  

  • }  

  • signals:  

  • void begin();  

  • public slots:  

  • void doSomething(BALL_COLOR ballColor) {  

  • qDebug() << "Gemini::doSomething() called with" << ballColor;  

  • if(ballColor != m_ballColor) {  

  • m_ballColor = ballColor;  

  • qDebug() << "ball color changed";  

  • }  

  • }  

  • private:  

  • BALL_COLOR m_ballColor;  

  • };  

  • #endif // GEMINI_H  

  • Gemini类中添加了成员函数stop(),在QML中访问的前提是public或protected成员函数,且使用Q_INVOKABLE宏,位置在函数返回类型的前面。

    (2)修改main.qml

  • [plain] view plain copy 
  • // main.qml  

  • import QtQuick 2.2  

  • import QtQuick.Window 2.1  

  • import Union.Lotto.Gemini 1.0  

  • Window {  

  • visible: true  

  • width: 360; height: 360  

  • title: "Union Lotto Game"  

  • color: "white"  

  • MouseArea {  

  • anchors.fill: parent  

  • onClicked: {  

  • gemini.begin()  

  • gemini.stop()  

  • }  

  • }  

  • Gemini {  

  • id: gemini  

  • onBegin: doSomething(Gemini.BALL_COLOR_RED)  

  • }  

  • }  

  • 在QML中访问C++的成员函数的形式是<id>.<method>,例如gemini.stop()。支持函数重载,这个与信号不同。

    C++类的属性——

    (1)修改头文件Gemini.h

  • [cpp] view plain copy 
  • #ifndef GEMINI_H  

  • #define GEMINI_H  

  • // Gemini.h  

  • #include <QObject>  

  • #include <QDebug>  

  • class Gemini : public QObject  

  • {  

  • Q_OBJECT  

  • Q_ENUMS(BALL_COLOR)  

  • Q_PROPERTY(unsigned int ballNumber READ ballNumber WRITE setBallNumber NOTIFY ballNumberChanged)  

  • public:  

  • Gemini() : m_ballColor(BALL_COLOR_YELLOW), m_ballNumber(0) {  

  • qDebug() << "Gemini::Gemini() called";  

  • }  

  • enum BALL_COLOR {  

  • BALL_COLOR_YELLOW,  

  • BALL_COLOR_RED,  

  • BALL_COLOR_BLUE,  

  • BALL_COLOR_ALL  

  • };  

  • unsigned int ballNumber() const {  

  • return m_ballNumber;  

  • }  

  • void setBallNumber(const unsigned int &ballNumber) {  

  • if(ballNumber != m_ballNumber) {  

  • m_ballNumber = ballNumber;  

  • emit ballNumberChanged();  

  • }  

  • }  

  • Q_INVOKABLE void stop() {  

  • qDebug() << "Gemini::stop() called";  

  • }  

  • signals:  

  • void begin();  

  • void ballNumberChanged();  

  • public slots:  

  • void doSomething(BALL_COLOR ballColor) {  

  • qDebug() << "Gemini::doSomething() called with" << ballColor;  

  • if(ballColor != m_ballColor) {  

  • m_ballColor = ballColor;  

  • qDebug() << "ball color changed";  

  • }  

  • }  

  • private:  

  • BALL_COLOR m_ballColor;  

  • unsigned int m_ballNumber;  

  • };  

  • #endif // GEMINI_H  

  • Gemini类中添加了Q_PROPERTY()宏,用来在QObject派生类中声明属性,这个属性如同类的数据成员一样,但它又有一些额外的特性可通过Qt元对象系统来访问。

    下面是Q_PROPERTY()宏的原型:

  • [cpp] view plain copy 
  • 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])  

  • 属性的type、name是必需的,其它是可选项,常用的有READ、WRITE、NOTIFY。属性的type可以是QVariant支持的任何类型,也可以是自定义类型,包括自定义类、列表类型、组属性等。另外,属性的READ、WRITE、RESET是可以被继承的,也可以是虚函数,这些特性并不常用。

    READ:读取属性值,如果没有设置MEMBER的话,它是必需的。一般情况下,函数是个const函数,返回值类型必须是属性本身的类型或这个类型的const引用,没有参数。

    WRITE:设置属性值,可选项。函数必须返回void,有且仅有一个参数,参数类型必须是属性本身的类型或这个类型的指针或引用。

    NOTIFY:与属性关联的可选信号。这个信号必须在类中声明过,当属性值改变时,就可触发这个信号,可以没有参数,有参数的话只能是一个类型同属性本身类型的参数,用来记录属性改变后的值。

    Q_PROPERTY()的详细用法可参考如下网址:

    (2)修改main.qml

  • [sql] view plain copy 
  • // main.qml  

  • import QtQuick 2.2  

  • import QtQuick.Window 2.1  

  • import Union.Lotto.Gemini 1.0  

  • Window {  

  • visible: true  

  • width: 360; height: 360  

  • title: "Union Lotto Game"  

  • color: "white"  

  • MouseArea {  

  • anchors.fill: parent  

  • onClicked: {  

  • gemini.begin()  

  • gemini.stop()  

  • gemini.ballNumber = 10  

  • }  

  • }  

  • Gemini {  

  • id: gemini  

  • onBegin: doSomething(Gemini.BALL_COLOR_RED)  

  • onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  

  • Component.onCompleted: console.log("default ball number is", ballNumber) // 0  

  • }  

  • }  

  • Gemini类中的ballNumber属性可以在QML中访问、修改,访问时调用了ballNumber()函数,修改时调用了setBallNumber()函数,同时还发送了一个信号来自动更新这个属性值。

    4、注册C++类为QML类型

    QObject派生类可以注册到Qt元对象系统,使得该类在QML中同其它内建类型一样,可以作为一个数据类型来使用。QML引擎允许注册可实例化的类型,也可以是不可实例化的类型,常见的注册函数有:

  • [cpp] view plain copy 
  • qmlRegisterInterface()  

  • qmlRegisterRevision()  

  • qmlRegisterSingletonType()  

  • qmlRegisterType()  

  • qmlRegisterTypeNotAvailable()  

  • qmlRegisterUncreatableType()  

  • 这些注册函数各有其用,可根据实际需要选择,使用时需要包含<QtQml>。常用的为qmlRegisterType(),它有三个重载函数,这里只介绍其一:

  • [cpp] view plain copy 
  • template<typename T>  

  • int qmlRegisterType(const char *uri,  

  • int versionMajor,  

  • int versionMinor,   

  • const char *qmlName);  

  • 这个模板函数注册C++类到Qt元对象系统中,uri是需要导入到QML中的库名,versionMajor和versionMinor是其版本数字,qmlName是在QML中可以使用的类型名。例如上面例子main.cpp中的代码:

  • [cpp] view plain copy 
  • qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  

推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式