QML与C++混合编程详解

本文详述了QML与C++混合编程的技术要点,包括为何选择混合编程、QML如何访问C++、注册C++类为QML类型、设置QML上下文属性、C++访问QML的方法及总结。重点介绍了信号与槽机制、枚举类型、成员函数、属性的使用,以及在C++中调用QML函数和连接信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

QML与C++混合编程详解
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首字母大写。信号不支持重载,多个信号的名字相同而参数不同时,能够被识别的只是最后一个信号,与信号的参数无关。
(2)修改main.cpp
[cpp] view plain copy

// 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

	#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++的成员函数的形式是.,例如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

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()  
  1. qmlRegisterUncreatableType()
    这些注册函数各有其用,可根据实际需要选择,使用时需要包含。常用的为qmlRegisterType(),它有三个重载函数,这里只介绍其一:
    [cpp] view plain copy

    1. template
    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(“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

  3. import QtQuick 2.2

  4. import QtQuick.Window 2.1

  5. import Union.Lotto.Gemini 1.0

  6. Window {

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

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

  5. #include

  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

  52. #include

  53. #include

  54. #include <Gemini.h>

  55. int main(int argc, char *argv[])

  56. {

  57.  QGuiApplication app(argc, argv);  
    
  58.  qmlRegisterType<Gemini>("Union.Lotto.Gemini", 1, 0, "Gemini");  
    
  59.  QQmlEngine engine;  
    
  60.  // set qml context property  
    
  61. // Gemini aGemini;  
    
  62. // engine.rootContext()->setContextProperty("aGemini", &aGemini);  
    
  63. QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:///main.qml")));  
    
  64. QObject *object = component.create();  
    
  65. qDebug() << "width value is" << object->property("width").toInt();  
    
  66. object->setProperty("width", 500);  
    
  67. qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();  
    
  68. QQmlProperty::write(object, "height", 500);  
    
  69. QObject *rect = object->findChild<QObject*>("rect");  
    
  70. if(rect) {  
    
  71.     rect->setProperty("color", "black");  
    
  72. }  
    
  73. QVariant returnedValue;  
    
  74. QVariant message = "Hello from C++";  
    
  75. QMetaObject::invokeMethod(object, "qmlFunction",  
    
  76.                           Q_RETURN_ARG(QVariant, returnedValue),  
    
  77.                           Q_ARG(QVariant, message));  
    
  78. qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml  
    
  79. Gemini test;  
    
  80. QObject::connect(object, SIGNAL(qmlSignal(QString)),  
    
  81.                  &test, SLOT(cppSlot(QString)));  
    
  82. return app.exec();  
    
  83. }
    在main.cpp中添加了QMeta::invokeMethod()和QObject::connect()来分别访问QML中函数和信号。
    7、总结
    本文主要介绍了QML与C++混合编程常用的方法与技巧,在使用过程中有几点值得注意:
    自定义类一定要派生自QObject类或其子类。
    必须使用Q_OBJECT宏。
    注册自定义类到Qt元对象系统或设置自定义类对象实例为QML上下文属性是必须的。
    两者交互进行数据传递时,要符合QML与C++间数据类型的转换规则。

本文适合于对Qt Quick有基本了解的读者。首先回答一个比较常会被问到的问题:什么是QML,它Quick的关系是什么? Qt Quick是Qt User Interface Creation Kit的缩写,而QML是Qt Quick最重要的组成部分,Qt Quick结合了如下技术: 组件集合,其中大部分是关于图形界面的 基于JavaScript陈述性语言:QML (Qt Meta-Object Language的缩写) 用于管理组件并组件交互的C++ API - QtDeclarative模块 言归正传:通过Qt Creator,我们可以轻松生成一个Qt Quick的应用工程,从而为QML生成应用程序框架。具体操作详见:创建qt quick (qml) 应用程序。 C++QML的交互是通过注册C++对象给QML环境得以实现的: 在C++实现中,非可视化的型别均为QObject的子类,可视化的类型均为QDeclarativeItem的子类。注意:QDeclarativeItem等同于QML的Item类。 如果用户想要定义自己的型别,做法如下: 在C++中,实现派生于QObject或QDeclarativeItem的子类,它是新定义item的实体对象; 在C++中,将1中实现的新item类型注册给QML; 在QML中,导入含有1中定义的新item的模块; 在QML中,向使用标准的item一样使用新定义的item 现举例说明,我们现尝试使用用Qt C++实现的MyButton对象(如下qml代码),它有自己的属性、方法以及信号的handler。用法如下(它使用其它标准的QML item一样),所需要做的是 需要导入包含MyButton的对应模块名称及其版本“MyItems 1.0 ”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值