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

  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

  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

  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

  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

  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

  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

  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

  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()宏的原型:

  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

  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引擎允许注册可实例化的类型,也可以是不可实例化的类型,常见的注册函数有:

  1. qmlRegisterInterface()
  2. qmlRegisterRevision()
  3. qmlRegisterSingletonType()
  4. qmlRegisterType()
  5. qmlRegisterTypeNotAvailable()
  6. qmlRegisterUncreatableType()

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

  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中的代码:

  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

  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

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

6C++访问QML

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

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

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

使用QQmlComponent——

修改main.cpp

  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

  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

  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()宏来声明函数返回值,其原型如下:

  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

  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

  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

  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

QML与C++混合编程详解的更多相关文章

  1. 【Qt】Qt Quick 之 QML 与 C++ 混合编程详解

    Qt Quick 之 QML 与 C++ 混合编程详解 - CSDN博客   专栏:Qt Quick简明教程 - CSDN博客   .

  2. QML与C++混合编程详解(转)

    原文转自:http://blog.csdn.net/ieearth/article/details/42243553 原文转自:https://www.cnblogs.com/findumars/p/ ...

  3. C51与汇编混合编程详解

    C51和汇编混合编程(1)-C语言中嵌入汇编 1.在 C文件中要嵌入汇编代码片以如下方式加入汇编代码: #pragma ASM ;Assembler Code Here #pragma ENDASM ...

  4. ORACLE PL/SQL编程详解

    ORACLE PL/SQL编程详解 编程详解 SQL语言只是访问.操作数据库的语言,并不是一种具有流程控制的程序设计语言,而只有程序设计语言才能用于应用软件的开发.PL /SQL是一种高级数据库程序设 ...

  5. Flex布局新旧混合写法详解(兼容微信)

    原文链接:https://www.usblog.cc/blog/post/justzhl/Flex布局新旧混合写法详解(兼容微信) flex是个非常好用的属性,如果说有什么可以完全代替 float 和 ...

  6. Linux串口编程详解(转)

    串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...

  7. [强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!)

    原文:[强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!) [强烈推荐]ORACLE PL/SQL编程详解之七: 程序包的创建与应用(聪明在于学习,天 ...

  8. [推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆)

    原文:[推荐]ORACLE PL/SQL编程详解之三:PL/SQL流程控制语句(不给规则,不成方圆) [推荐]ORACLE PL/SQL编程详解之三: PL/SQL流程控制语句(不给规则,不成方圆) ...

  9. 【强烈强烈推荐】《ORACLE PL/SQL编程详解》全原创(共八篇)--系列文章导航

    原文:[强烈强烈推荐]<ORACLE PL/SQL编程详解>全原创(共八篇)--系列文章导航 <ORACLE PL/SQL编程详解> 系列文章目录导航 ——通过知识共享树立个人 ...

随机推荐

  1. POJ:2010-Moo University - Financial Aid

    Moo University - Financial Aid Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 10894 Acce ...

  2. Android 内嵌 HTML5 并进行交互

    Android与HTML5的交互主要是两个部分, 与HTML5的交互以及与JavaScript的交互, 与HTML5的交互可以通过注册onclick事件转化为与JavaScript的交互 Androi ...

  3. AD RMS总结

    AD RMS 认识篇 AD RMS(Active Directory Right Mangement Servic)活动目录权限服务. 首先我通过了解AD RMS的用途去深入学习AD RMS.在过去用 ...

  4. 《Cracking the Coding Interview》——第1章:数组和字符串——题目1

    2014-03-18 01:25 题目:给定一个字符串,判断其中是否有重复字母. 解法:对于可能有n种字符的字符集,用一个长度为n的数组统计每个字符的出现次数,大于1则表示有重复. 代码: // 1. ...

  5. 嵌入式(Embedded System)笔记 —— Cortex-M3 Introduction and Basics(下)

    随着课内的学习,我想把每节课所学记录下来,以作查阅.以饲读者.由于我所上的是英文班课程,因此我将把关键术语的英文给出,甚至有些内容直接使用英文. 本次所介绍内容仍是关于Cortex-M3的基础内容,相 ...

  6. python实现单链表的反转

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #!/usr/bin/env python #coding = utf-8 ...

  7. tarjan算法求LCA

    tarjan算法求LCA LCA(Least Common Ancestors)的意思是最近公共祖先,即在一棵树中,找出两节点最近的公共祖先. 这里我们使用tarjan算法离线算法解决这个问题. 离线 ...

  8. 当网卡收到一个包的目的地址是本主机其他接口的IP时.2

    arp包进入主机后要经过的过滤是:rp_filter rp_filter会过滤网段 所以说不要在进行arp_ignore测试的时候把rp_filter设置成2, 此时就不会对源地址进行路由的检查了 然 ...

  9. qemu中是怎么模拟的新的设备

    kvm_cpu_exec 和demo中演示的一样

  10. MySQL常用客户端 命令

    登录MySQL mysql -h localhost -uroot -p 授权指定用户访问指定数据库 GRANT ALL ON cookbook.* TO 'cbuser'@'localhost' I ...