QML与c++混合编程

QML与C++混合编程详解 Qt集成了QML引擎和Qt元对象系统,使得QML很容易从C++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。QML访问C++有两个方法:一是在Qt元对象系统中注册C++类,在QML中实例化、访问。二是在C++中实例化并设置为QML上下文属性,在QML中直接使用。与后者相比,前者可以使C++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。文章最后为大家准备了Qt资料。 阅读详情

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
  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.     width: 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.     width: 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.     width: 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.     width: 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等一起使用。

5、QML上下文属性设置

在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.     width: 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来处理了,如上面例子中所示。

6、C++访问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.     width: 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.     width: 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++间数据类型的转换规则。

QML学习【一】QML语言基础 QML语言基础 阅读详情

相关推荐

【QML初学者文档】一问读懂QML基础开发(内附大量简单源码)

QML是Qt的一大特色,拥有强大的UI制作能力。但是想要学好QML,还需要一些细节和技巧的掌握。本篇文章将为你提供完整的QML知识体系,让你成为Qt的UI魔法师!

BenBierBa的博客 2万+

[QML]事无巨细开始实践QML开发(一)什么是QML,为什么学习QML,先写一个简单的页面

然后比较重要的一点是,QML本身可能对中文的支持有点问题,也就是说QML中如果出现了中文,可能就会出现乱码。当然了,Qt也考虑到了这个问题,当你需要调试这个程序的时候,请右键项目-》属性-》Qt Project Settings->QML->Enable QMl Debugging->改为是,这样之后你在qml文件中打上的断点就可以被命中了,当然了这也带来一个问题,就是当你在之后删掉这个断点后,每次调试或者启动这个程序都会告诉你程序命中了断点但是找不到这个断点,不过这并不是什么首要的问题,不是吗?

Gefangenes的博客 1万+

quick 2 — qml 与c++的混合编程

满足2个前提:(1)从QObjec或QObject的派生类继承;(2)使用Q_OBJECT宏;这两个条件是为了让一个类进入Qt的元对象系统,只有使用元对象系统,类的某些方法才可能通过字符串形式的名字调用,才能在QML中被访问。

qq_36939187的博客 1271

QML了解

QMl是一种高效的开发UI 的语言。QML(Qt Meta-Object Language,Qt元对象语言)是一种用于描述应用程序用户界面的声明式编程语言,可以使组件以动态方式进行交互,并且允许组件在用户界面中很容易地实现复用和自定义 # view.qml import QtQuick 2.0 Rectangle { width: 200 height: 200 color: "green" Text { text: "Hello World"

weixin_43956958的博客 2万+

QML与C++混合编程使用

C++与QML的交互是通过暴露C++对象给QML环境得以实现的: 在C++实现中,非可视化的型别均为QObject的子类,可视化的类型均为QDeclarativeItem的子类。注意:QDeclarativeItem等同于QML的Item类。 如果用户想要定义自己的型别,做法如下: 在C++中,实现派生于QObject或QDeclarativeItem的子类,它是新定义item的实体对象; 在C++中,将1中实现的新item类型注册给QML; 在QML中,导入含有1中定义的新item的模块; 在

1万+

QML基本元素和属性

任何知识,都有自己的体系架构,掌握了体系架构,按图索骥的去学习的效率要远高于漫无目的的学习。这里我参照QML的知识框架和一些问题来对QML进行系统的总结。问题列表如下: 1.QML是什么? 2.为什么使用QML? 3.QML的基本语法? 4.QML的元素都有哪些? 1.QML是什么? QML是Qt推出的Qt Quick技术的一部分,是一种用于描述用户界面的类似html的声明式语言。它将用户界面分解成一些更小的元素,这些元素能够结合成一个组件。这点类似于json结构,每个对象既有自己的行为和属性同

yang1fei2的博客 9572

QML与C++混合编程

简述: QML与C++混合编程就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法。 1> Qt集成了QML引擎和Qt元对象系统,使得QML很容易从C++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。 QML访问C++有两个方法: 序号 方法 备注 1 在Qt元对象系统中注册C++类,在QML中实例化、访问 可以使C++类在QML中作为一个数据类型 2 在C++中实例

hebbely的博客 2955

QML与C++混合编程实战案例:图像处理

QML(Qt Modeling Language)是Qt框架中用于设计用户界面的声明性语言,而C++是其背后的编程语言,负责逻辑处理。混合编程技术结合了QML在UI设计上的直观性和C++在性能优化上的强大能力。本章将概述QML与C++如何协作,以及它在现代应用程序中的重要性。QML与C++的混合编程技术不仅提高了开发效率,还优化了程序性能。本章仅作为引入,为读者提供了一个整体的认识。在后续章节中,我们将深入探讨如何将QML与C++有效结合,及其在实际项目中的应用和优化。

weixin_34618526的博客 970

QML与C++混合编程详解(QML与c++信号与槽的操作)

1、QML与C++为什么要混合编程 QML与C++为什么要混合编程,简单来说,就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法,下面介绍了两者间交互的方法与技巧。 2、QML访问C++概述 Qt集成了QML引擎和Qt元对象系统,使得QML很容易从C++中得到扩展,在一定的条件下,QML就可以访问QObject派生类的成员,例如信号、槽函数、枚举类型、属性、成员函数等。 ...

细雨纷飞~~~ 2514

【自然语言处理(NLP)实战】LSTM网络实现中文文本情感分析(手把手与教学超详细)

在当今数字化时代,人们在社交媒体、评论平台以及各类在线交流中产生了海量的文本数据。这些数据蕴含着丰富的情感信息,从而成为了深入理解用户态度、市场趋势,甚至社会情绪的宝贵资源。自然语言处理(NLP)的发展为我们提供了强大的工具,使得对文本情感进行分析成为可能。在这个领域中,长短时记忆网络(LSTM)凭借其能够捕捉文本序列中长距离依赖关系的能力,成为了情感分析任务中的一项重要技术。通过详细的步骤和示例代码,深入了解如何处理中文文本数据、构建LSTM模型、进行训练和评估。

halo的博客 2万+

10X Genomics单细胞数据分析实战:从FASTQ到UMAP可视化的完整流程

本文详细介绍了10X Genomics单细胞数据分析的完整流程,从FASTQ原始数据处理到UMAP可视化。通过Seurat工具包实现数据质量控制、标准化、降维聚类和细胞注释,特别适合生物信息学新手学习。文章包含可复现的代码示例和常见问题解决方案,帮助研究者快速掌握单细胞测序数据分析的核心技术。

weixin_30952535的博客 535

QtQuick系列教程(3)——QML与C++混合编程

QML与C++混合编程一、QML与C++混合编程简介二、QML访问C++三、C++类的实现1、信号与槽实现2、枚举类型实现3、成员函数实现4、C++类的属性四、注册C++类为QML类型五、QML上下文属性设置六、C++访问QML1、C++使用QQmlComponent2、C++使用QML的属性3、C++使用QML中信号与函数七、QML与C++混合编程注意事项 一、QML与C++混合编程简介 QML...

qq_34462436的博客 1436

MatLab:数字图像处理实验

图像处理基本处理实验目的实验环境实验内容题目一代码结果题目二平移平移代码平移结果水平镜像水平镜像代码水平镜像结果垂直镜像垂直镜像代码垂直镜像结果放大放大代码放大结果缩小缩小代码缩小结果旋转旋转代码旋转结果双线性旋转旋转代码旋转结果 实验目的 1、熟悉并掌握MATLAB工具的使用;  2、实现图像的读取、显示、存储、平移、镜像、放大、缩小及旋转操作; 3、掌握常用的插值方法,并了解其优缺点。 实验环境 MatLab2016 实验内容 题目一 读入一幅RGB图像,变换为灰度图像和二值图像,并在同一个窗口内分别显

luoyao_的博客 9696

Qt QML与C++混合编程

Qt QML与C++混合编程

m0_73443478的博客 2603

AD23导出Gerber文件保姆级教程:从PCB到嘉立创下单,一步不落(附常见问题排查)

本文提供AD23导出Gerber文件的详细教程,涵盖从PCB设计检查到嘉立创下单的全流程,特别针对两层板设计场景,帮助新手工程师避免常见错误。内容包括Gerber文件生成步骤、钻孔文件设置、文件检查工具推荐以及嘉立创下单实操指南,确保PCB打样顺利。

weixin_30924239的博客 440

Android Telephony主要模块及功能

Telephony的代码主要由五个模块组成 1.Telephony Frameworks 代码区域: frameworks/opt/telephony/ 其中主要的文件在如下目录 /frameworks/opt/telephony/src/java/com/android/internal/telephony/ 其中比较常见的有以下几类文件 1.BaseCommands、RIL构成R

泊客 7723

Qt Quick 之 QML 与 C++ 混合编程详解

但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以呢,很多时候我们是会基于这样的原则来混合使用 QML 和 C++: QML 构建界面, C++ 实现非界面的业务逻辑和复杂运算。一旦你在 C++ 中注册好了 QML 类型,就可以在 QML 文档中引入你注册的包,然后使用注册的类型。

宁静致远的博客 4285

基于MATLAB/Simulink平台进行新能源微电网运行控制的建模与仿真

新能源微电网是现代电力系统的重要组成部分,能够有效整合分布式能源(如光伏、风力发电)和储能设备,实现局部区域内的能源高效利用。微电网的运行控制涉及多源能量管理、负荷平衡以及并网/孤岛模式切换等关键问题。基于MATLAB/Simulink平台进行新能源微电网运行控制的建模与仿真,可以帮助研究者验证控制策略的有效性,并优化系统的动态性能和经济性。本项目旨在构建一个完整的新能源微电网模型,包括电源模块、储能模块、负荷模块以及控制器模块,并通过仿真分析其运行特性。

xiaoheshang_123的博客 423

【QML】QML与C++混合编程

QML与C++混合编程 使用QQuickView pro文件中添加quick模块 #include<QApplication> #include<QQuickView> int main(int argc,char* argv[]){ QApplication app(argc,argv); //加载qml文件到视图 QQuickView view; view.setSource(QUrl("column.qml")); view.sho

半生瓜のblog 1567

x2-DFD:A framework for explainable and explainable deepfake detection

该文章提出了X2-DFD框架,用于可解释和可扩展的深度伪造检测。通过评估大型多模态语言模型(MLLMs)在深度伪造检测中的内在能力,发现它们在不同伪造相关特征上的有效性各异。基于此,实施了针对性的微调策略,显著提高了MLLMs的解释能力,并整合了外部深度伪造检测器(EDDs),以结合MLLMs和传统检测器的互补优势,实现更好的检测和解释。

志在山顶的人,不会贪念山腰的风景 510

2026国内Claude Code保姆级教程:安装、避坑、防串台全优化

安装 Claude Code CLI + 切换 npm 国内镜像优先配置固定关闭:自动联网、自动项目扫描VSCode 内置终端直接用,不装多余插件会话隔离+指令约束,彻底解决 Rust、小程序 跨语言串台。

lpfasd123的博客 2555
上一篇: c++内存安全问题
下一篇: qml与c++混合编程之简单demo说明
艺丢丢
博客等级 码龄12年 16粉丝 52原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值