• QML与C++混合编程详解


    1QMLC++为什么要混合编程

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

    2QML访问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
     
    1. #ifndef GEMINI_H  
    2. #define GEMINI_H  
    3. // Gemini.h  
    4. #include <QObject>  
    5. #include <QDebug>  
    6. class Gemini : public QObject  
    7. {  
    8.     Q_OBJECT  
    9. signals:  
    10.     void begin();  
    11. public slots:  
    12.     void doSomething() {  
    13.         qDebug() << "Gemini::doSomething() called";  
    14.     }  
    15. };  
    16. #endif // GEMINI_H  

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

    (2)修改main.cpp

    [cpp] view plain copy
     
    1. // main.cpp  
    2. #include <QGuiApplication>  
    3. #include <QQmlApplicationEngine>  
    4. #include <QtQml>  
    5. #include <Gemini.h>  
    6. int main(int argc, char *argv[])  
    7. {  
    8.     QGuiApplication app(argc, argv);  
    9.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
    10.     QQmlApplicationEngine engine;  
    11.     engine.load(QUrl(QStringLiteral("qrc:///main.qml")));  
    12.     return app.exec();  
    13. }  

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

    (3)修改main.qml

    [sql] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. import QtQuick.Window 2.1  
    4. import Union.Lotto.Gemini 1.0  
    5. Window {  
    6.     visible: true  
    7.      360; height: 360  
    8.     title: "Union Lotto Game"  
    9.     color: "white"  
    10.     MouseArea {  
    11.         anchors.fill: parent  
    12.         onClicked: {  
    13.             gemini.begin()  
    14.         }  
    15.     }  
    16.     Gemini {  
    17.         id: gemini  
    18.         onBegin: doSomething()  
    19.     }  
    20. }  

     

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

    枚举类型——

    (1)修改头文件Gemini.h

    [cpp] view plain copy
     
    1. #ifndef GEMINI_H  
    2. #define GEMINI_H  
    3. // Gemini.h  
    4. #include <QObject>  
    5. #include <QDebug>  
    6. class Gemini : public QObject  
    7. {  
    8.     Q_OBJECT  
    9.     Q_ENUMS(BALL_COLOR)  
    10. public:  
    11.     Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  
    12.         qDebug() << "Gemini::Gemini() called";  
    13.     }  
    14.     enum BALL_COLOR {  
    15.         BALL_COLOR_YELLOW,  
    16.         BALL_COLOR_RED,  
    17.         BALL_COLOR_BLUE,  
    18.         BALL_COLOR_ALL  
    19.     };  
    20. signals:  
    21.     void begin();  
    22. public slots:  
    23.     void doSomething(BALL_COLOR ballColor) {  
    24.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
    25.         if(ballColor != m_ballColor) {  
    26.             m_ballColor = ballColor;  
    27.             qDebug() << "ball color changed";  
    28.         }  
    29.     }  
    30. private:  
    31.     BALL_COLOR m_ballColor;  
    32. };  
    33. #endif // GEMINI_H  

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

    (2)修改main.qml

    [plain] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. import QtQuick.Window 2.1  
    4. import Union.Lotto.Gemini 1.0  
    5. Window {  
    6.     visible: true  
    7.      360; height: 360  
    8.     title: "Union Lotto Game"  
    9.     color: "white"  
    10.     MouseArea {  
    11.         anchors.fill: parent  
    12.         onClicked: {  
    13.             gemini.begin()  
    14.         }  
    15.     }  
    16.     Gemini {  
    17.         id: gemini  
    18.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
    19.     }  
    20. }  

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

    成员函数——

    (1)修改头文件Gemini.h

    [cpp] view plain copy
     
    1. #ifndef GEMINI_H  
    2. #define GEMINI_H  
    3. // Gemini.h  
    4. #include <QObject>  
    5. #include <QDebug>  
    6. class Gemini : public QObject  
    7. {  
    8.     Q_OBJECT  
    9.     Q_ENUMS(BALL_COLOR)  
    10. public:  
    11.     Gemini() : m_ballColor(BALL_COLOR_YELLOW) {  
    12.         qDebug() << "Gemini::Gemini() called";  
    13.     }  
    14.     enum BALL_COLOR {  
    15.         BALL_COLOR_YELLOW,  
    16.         BALL_COLOR_RED,  
    17.         BALL_COLOR_BLUE,  
    18.         BALL_COLOR_ALL  
    19.     };  
    20.     Q_INVOKABLE void stop() {  
    21.         qDebug() << "Gemini::stop() called";  
    22.     }  
    23. signals:  
    24.     void begin();  
    25. public slots:  
    26.     void doSomething(BALL_COLOR ballColor) {  
    27.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
    28.         if(ballColor != m_ballColor) {  
    29.             m_ballColor = ballColor;  
    30.             qDebug() << "ball color changed";  
    31.         }  
    32.     }  
    33. private:  
    34.     BALL_COLOR m_ballColor;  
    35. };  
    36. #endif // GEMINI_H  

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

    (2)修改main.qml

    [plain] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. import QtQuick.Window 2.1  
    4. import Union.Lotto.Gemini 1.0  
    5. Window {  
    6.     visible: true  
    7.      360; height: 360  
    8.     title: "Union Lotto Game"  
    9.     color: "white"  
    10.     MouseArea {  
    11.         anchors.fill: parent  
    12.         onClicked: {  
    13.             gemini.begin()  
    14.             gemini.stop()  
    15.         }  
    16.     }  
    17.     Gemini {  
    18.         id: gemini  
    19.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
    20.     }  
    21. }  

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

    C++类的属性——

    (1)修改头文件Gemini.h

    [cpp] view plain copy
     
    1. #ifndef GEMINI_H  
    2. #define GEMINI_H  
    3. // Gemini.h  
    4. #include <QObject>  
    5. #include <QDebug>  
    6. class Gemini : public QObject  
    7. {  
    8.     Q_OBJECT  
    9.     Q_ENUMS(BALL_COLOR)  
    10.     Q_PROPERTY(unsigned int ballNumber READ ballNumber WRITE setBallNumber NOTIFY ballNumberChanged)  
    11. public:  
    12.     Gemini() : m_ballColor(BALL_COLOR_YELLOW), m_ballNumber(0) {  
    13.         qDebug() << "Gemini::Gemini() called";  
    14.     }  
    15.     enum BALL_COLOR {  
    16.         BALL_COLOR_YELLOW,  
    17.         BALL_COLOR_RED,  
    18.         BALL_COLOR_BLUE,  
    19.         BALL_COLOR_ALL  
    20.     };  
    21.     unsigned int ballNumber() const {  
    22.         return m_ballNumber;  
    23.     }  
    24.     void setBallNumber(const unsigned int &ballNumber) {  
    25.         if(ballNumber != m_ballNumber) {  
    26.             m_ballNumber = ballNumber;  
    27.             emit ballNumberChanged();  
    28.         }  
    29.     }  
    30.     Q_INVOKABLE void stop() {  
    31.         qDebug() << "Gemini::stop() called";  
    32.     }  
    33. signals:  
    34.     void begin();  
    35.     void ballNumberChanged();  
    36. public slots:  
    37.     void doSomething(BALL_COLOR ballColor) {  
    38.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
    39.         if(ballColor != m_ballColor) {  
    40.             m_ballColor = ballColor;  
    41.             qDebug() << "ball color changed";  
    42.         }  
    43.     }  
    44. private:  
    45.     BALL_COLOR m_ballColor;  
    46.     unsigned int m_ballNumber;  
    47. };  
    48. #endif // GEMINI_H  

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

    下面是Q_PROPERTY()宏的原型:

    [cpp] view plain copy
     
    1. Q_PROPERTY()(type name  
    2.   (READ getFunction [WRITE setFunction] |  
    3.              MEMBER memberName [(READ getFunction | WRITE setFunction)])  
    4.             [RESET resetFunction]  
    5.             [NOTIFY notifySignal]  
    6.             [REVISION int]  
    7.             [DESIGNABLE bool]  
    8.             [SCRIPTABLE bool]  
    9.             [STORED bool]  
    10.             [USER bool]  
    11.             [CONSTANT]  
    12.             [FINAL])  

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

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

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

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

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

    http://doc.qt.io/qt-5/properties.html#qt-s-property-system

    (2)修改main.qml

    [sql] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. import QtQuick.Window 2.1  
    4. import Union.Lotto.Gemini 1.0  
    5. Window {  
    6.     visible: true  
    7.      360; height: 360  
    8.     title: "Union Lotto Game"  
    9.     color: "white"  
    10.     MouseArea {  
    11.         anchors.fill: parent  
    12.         onClicked: {  
    13.             gemini.begin()  
    14.             gemini.stop()  
    15.             gemini.ballNumber = 10  
    16.         }  
    17.     }  
    18.     Gemini {  
    19.         id: gemini  
    20.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
    21.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
    22.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
    23.     }  
    24. }  

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

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

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

    [cpp] view plain copy
     
    1. qmlRegisterInterface()  
    2. qmlRegisterRevision()  
    3. qmlRegisterSingletonType()  
    4. qmlRegisterType()  
    5. qmlRegisterTypeNotAvailable()  
    6. qmlRegisterUncreatableType()  

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

    [cpp] view plain copy
     
    1.   template<typename T>  
    2.   int qmlRegisterType(const char *uri,  
    3. int versionMajor,  
    4. int versionMinor,   
    5.        const char *qmlName);  

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

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

    main.cpp中将Gemini类注册为在QML中可以使用的Gemini类型,主版本为1,次版本为0,库的名字是Union.Lotto.Gemini。main.qml中导入了这个库,使用Gemini构造了一个对象,id为gemini,这样就可以借助id来访问C++了。

    注册动作必须在QML上下文创建之前,否则无效。

    另外:QQuickView为QtQuickUI提供了一个窗口,可以方便地加载QML文件并显示其界面。QApplication派生自QGuiApplication,而QGuiApplication又派生自QCoreApplication,这三个类是常见的管理Qt应用程序的类。QQmlApplicationEngine可以方便地从一个单一的QML文件中加载应用程序,它派生自QQmlEngine,QQmlEngine则提供了加载QML组件的环境,可以与QQmlComponent、QQmlContext等一起使用。

    5QML上下文属性设置

    在C++应用程序加载QML对象时,我们可以直接嵌入一些C++数据来给QML使用,这里需要用到QQmlContext::setContextProperty(),即设置QML上下问属性,它可以是一个简单的类型,也可以是任何我们自定义的类对象。

    (1)修改main.cpp

    [cpp] view plain copy
     
    1. // main.cpp  
    2. #include <QGuiApplication>  
    3. #include <QQuickView>  
    4. #include <QQmlContext>  
    5. #include <Gemini.h>  
    6. int main(int argc, char *argv[])  
    7. {  
    8.     QGuiApplication app(argc, argv);  
    9.     QQuickView view;  
    10.     Gemini gemini;  
    11.     view.rootContext()->setContextProperty("gemini", &gemini);  
    12.     view.setSource(QUrl(QStringLiteral("qrc:///main.qml")));  
    13.     view.show();  
    14.     return app.exec();  
    15. }  

    彻底修改一下main.cpp吧,这里使用了QQuickView,注意头文件的变化,Gemini类先实例化为gemini对象,然后注册为QML上下文属性。

    (2)修改main.qml

    [plain] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. Item {  
    4.      360; height: 360  
    5.     MouseArea {  
    6.         anchors.fill: parent  
    7.         onClicked: {  
    8.             gemini.begin()  
    9.             gemini.stop()  
    10.         }  
    11.     }  
    12.     Connections {  
    13.         target: gemini  
    14.         onBegin:console.log("aaaa")  
    15.     }  
    16. }  

    既然main.cpp修改了那么多东西,main.qml也要做相应的修改,在main.qml中不能使用Gemini类型来实例化了,也不能调用doSomething()槽函数了,因为doSomething()函数中的枚举类型在QML中是访问不到的,正确的用法是通过QML上下文属性“gemini”来访问C++,可以访问信号begin()和成员函数stop(),此时的信号处理器就需要用Connections来处理了,如上面例子中所示。

    6C++访问QML

    同样,在C++中也可以访问QML中的属性、函数和信号。

    在C++中加载QML文件可以用QQmlComponent或QQuickView,然后就可以在C++中访问QML对象了。QQuickView提供了一个显示用户界面的窗口,而QQmlComponent没有。

    QQuickView::rootObject()返回了组件实例,是一个有用的函数。前面的例子中已经使用过QQuickView了,下面的例子介绍QQmlComponent的用法。

    使用QQmlComponent——

    修改main.cpp

    [cpp] view plain copy
     
    1. // main.cpp  
    2. #include <QGuiApplication>  
    3. #include <QtQml>  
    4. #include <Gemini.h>  
    5. int main(int argc, char *argv[])  
    6. {  
    7.     QGuiApplication app(argc, argv);  
    8.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
    9.     QQmlEngine engine;  
    10.     // set qml context property  
    11.     // Gemini aGemini;  
    12.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
    13.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
    14.     component.create();  
    15.     return app.exec();  
    16. }  

    例子中注释的部分是设置QML上下文属性的方法。

    C++中访问QML中的属性——

    在C++中加载了QML文件并进行组件实例化后,就可以在C++中访问、修改这个实例的属性值了,可以是QML内建属性,也可以是自定义属性。

    (1)修改main.qml

    [plain] view plain copy
     
    1. // main.qml  
    2. import QtQuick 2.2  
    3. import QtQuick.Window 2.1  
    4. import Union.Lotto.Gemini 1.0  
    5. Window {  
    6.     visible: true  
    7.      360; height: 360  
    8.     title: "Union Lotto Game"  
    9.     color: "white"  
    10.     Rectangle {  
    11.         objectName: "rect"  
    12.         anchors.fill: parent  
    13.         color: "yellow"  
    14.     }  
    15.     MouseArea {  
    16.         anchors.fill: parent  
    17.         onClicked: {  
    18.             gemini.begin()  
    19.             gemini.stop()  
    20.             gemini.ballNumber = 10  
    21.         }  
    22.     }  
    23.     Gemini {  
    24.         id: gemini  
    25.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
    26.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
    27.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
    28.     }  
    29. }  

    在main.qml中添加了一个Rectangle,设置objectName属性值为“rect”,这个值是为了在C++中能够找到这个Rectangle

    (2)修改main.cpp

    [cpp] view plain copy
     
    1. // main.cpp  
    2. #include <QGuiApplication>  
    3. #include <QtQml>  
    4. #include <Gemini.h>  
    5. int main(int argc, char *argv[])  
    6. {  
    7.     QGuiApplication app(argc, argv);  
    8.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
    9.     QQmlEngine engine;  
    10.     // set qml context property  
    11.     // Gemini aGemini;  
    12.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
    13.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
    14.     QObject *object = component.create();  
    15.     qDebug() << "width value is" << object->property("width").toInt();  
    16.     object->setProperty("width", 500);  
    17.     qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();  
    18.     QQmlProperty::write(object, "height", 500);  
    19.     QObject *rect = object->findChild<QObject*>("rect");  
    20.     if(rect) {  
    21.         rect->setProperty("color", "black");  
    22.     }  
    23.     return app.exec();  
    24. }  

    首先,使用了QObject::property()/setProperty()来读取、修改width属性值。

    接着,使用了QQmlProperty::read()/write()来读取、修改height属性值。

    另外,如果某个对象的类型是QQuickItem,例如QQuickView::rootObject()的返回值,这时就可以使用QQuickItem::width/setWidth()来访问、修改width属性值了。

    有时候,QML组件是一个复杂的树型结构,包含兄弟组件和孩子组件,我们可以使用QObject::findchild()/findchildren()来查找,如上面例子所示。

    C++中访问QML中的函数与信号——

    在C++中,使用QMetaObject::invokeMethod()可以调用QML中的函数,从QML传递过来的函数参数和返回值会被转换为C++中的QVariant类型,成功返回true,参数不正确或被调用函数名错误返回false,invokeMethod()共有四个重载函数,用法相似。必须使用Q_ARG()宏来声明函数参数,用Q_RETURN_ARG()宏来声明函数返回值,其原型如下:

    [cpp] view plain copy
     
    1. QGenericArgument           Q_ARG(Type, const Type & value)  
    2. QGenericReturnArgument     Q_RETURN_ARG(Type, Type & value)  

    使用QObject::connect()可以连接QML中的信号,connect()共有四个重载函数,它们都是静态函数。必须使用SIGNAL()宏来声明信号,SLOT()宏声明槽函数。

    使用QObject::disconnect()可以解除信号与槽函数的连接。

    (1)修改main.qml

    [plain] view plain copy
     
    1. import QtQuick 2.2  
    2. import QtQuick.Window 2.1  
    3. import Union.Lotto.Gemini 1.0  
    4. Window {  
    5.     visible: true  
    6.      360; height: 360  
    7.     title: "Union Lotto Game"  
    8.     color: "white"  
    9.     signal qmlSignal(string message)  
    10.     onQmlSignal: console.log("qml signal message is", message) // this is a qml signal  
    11.     function qmlFunction(parameter) {  
    12.         console.log("qml function parameter is", parameter) // Hello from C++  
    13.         return "function from qml"  
    14.     }  
    15.     Rectangle {  
    16.         objectName: "rect"  
    17.         anchors.fill: parent  
    18.         color: "yellow"  
    19.     }  
    20.     MouseArea {  
    21.         anchors.fill: parent  
    22.         onClicked: {  
    23.             gemini.begin()  
    24.             gemini.stop()  
    25.             gemini.ballNumber = 10  
    26.             qmlSignal("this is a qml signal")  
    27.         }  
    28.     }  
    29.     Gemini {  
    30.         id: gemini  
    31.         onBegin: doSomething(Gemini.BALL_COLOR_RED)  
    32.         onBallNumberChanged: console.log("new ball number is", ballNumber) // 10  
    33.         Component.onCompleted: console.log("default ball number is", ballNumber) // 0  
    34.     }  
    35. }  

    main.qml中添加了qmlSignal()信号和qmlFunction()函数,信号在QML中发送,函数在C++中调用。

    (2)修改Gemini.h

    [cpp] view plain copy
     
    1. #ifndef GEMINI_H  
    2. #define GEMINI_H  
    3. // Gemini.h  
    4. #include <QObject>  
    5. #include <QDebug>  
    6. class Gemini : public QObject  
    7. {  
    8.     Q_OBJECT  
    9.     Q_ENUMS(BALL_COLOR)  
    10.     Q_PROPERTY(unsigned int ballNumber READ ballNumber WRITE setBallNumber NOTIFY ballNumberChanged)  
    11. public:  
    12.     Gemini() : m_ballColor(BALL_COLOR_YELLOW), m_ballNumber(0) {  
    13.         qDebug() << "Gemini::Gemini() called";  
    14.     }  
    15.     enum BALL_COLOR {  
    16.         BALL_COLOR_YELLOW,  
    17.         BALL_COLOR_RED,  
    18.         BALL_COLOR_BLUE,  
    19.         BALL_COLOR_ALL  
    20.     };  
    21.     unsigned int ballNumber() const {  
    22.         return m_ballNumber;  
    23.     }  
    24.     void setBallNumber(const unsigned int &ballNumber) {  
    25.         if(ballNumber != m_ballNumber) {  
    26.             m_ballNumber = ballNumber;  
    27.             emit ballNumberChanged();  
    28.         }  
    29.     }  
    30.     Q_INVOKABLE void stop() {  
    31.         qDebug() << "Gemini::stop() called";  
    32.     }  
    33. signals:  
    34.     void begin();  
    35.     void ballNumberChanged();  
    36. public slots:  
    37.     void doSomething(BALL_COLOR ballColor) {  
    38.         qDebug() << "Gemini::doSomething() called with" << ballColor;  
    39.         if(ballColor != m_ballColor) {  
    40.             m_ballColor = ballColor;  
    41.             qDebug() << "ball color changed";  
    42.         }  
    43.     }  
    44.     void cppSlot(const QString &message) {  
    45.         qDebug() << "Called the C++ slot with message:" << message; // this is a qml signal  
    46.     }  
    47. private:  
    48.     BALL_COLOR m_ballColor;  
    49.     unsigned int m_ballNumber;  
    50. };  
    51. #endif // GEMINI_H  

    Gemini类中添加了cppSlot()槽函数,将要在main.cpp中与QML的信号connect

    (3)修改main.cpp

    [cpp] view plain copy
     
    1. #include <QGuiApplication>  
    2. #include <QtQml>  
    3. #include <Gemini.h>  
    4. int main(int argc, char *argv[])  
    5. {  
    6.     QGuiApplication app(argc, argv);  
    7.     qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
    8.     QQmlEngine engine;  
    9.     // set qml context property  
    10.     // Gemini aGemini;  
    11.     // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
    12.     QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
    13.     QObject *object = component.create();  
    14.     qDebug() << "width value is" << object->property("width").toInt();  
    15.     object->setProperty("width", 500);  
    16.     qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();  
    17.     QQmlProperty::write(object, "height", 500);  
    18.     QObject *rect = object->findChild<QObject*>("rect");  
    19.     if(rect) {  
    20.         rect->setProperty("color", "black");  
    21.     }  
    22.     QVariant returnedValue;  
    23.     QVariant message = "Hello from C++";  
    24.     QMetaObject::invokeMethod(object, "qmlFunction",  
    25.                               Q_RETURN_ARG(QVariant, returnedValue),  
    26.                               Q_ARG(QVariant, message));  
    27.     qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml  
    28.     Gemini test;  
    29.     QObject::connect(object, SIGNAL(qmlSignal(QString)),  
    30.                      &test, SLOT(cppSlot(QString)));  
    31.     return app.exec();  
    32. }  

    在main.cpp中添加了QMeta::invokeMethod()和QObject::connect()来分别访问QML中函数和信号。

    7、总结

    本文主要介绍了QML与C++混合编程常用的方法与技巧,在使用过程中有几点值得注意:

             自定义类一定要派生自QObject类或其子类。

             必须使用Q_OBJECT宏。

             注册自定义类到Qt元对象系统或设置自定义类对象实例为QML上下文属性是必须的。

             两者交互进行数据传递时,要符合QML与C++间数据类型的转换规则。

    http://blog.csdn.net/ieearth/article/details/42243553

  • 相关阅读:
    严选B端产品设计原则
    从严选供应链采购看业务系统架构
    网易严选数据任务治理实践
    hibernate主键生成策略
    the user operation is waiting
    hibernate入门案例
    jsp重定向和转发
    jsp编码过程
    在eclipse中部署jsp项目
    oracle误删表解决方案
  • 原文地址:https://www.cnblogs.com/findumars/p/6090850.html
Copyright © 2020-2023  润新知