• Qt QML与C++混合编程


    一、QML与C++混合编程简介

        QML与C++混合编程就是使用QML高效便捷地构建UI,而C++则用来实现业务逻辑和复杂算法。

    二、QML访问C++

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

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

    三、C++类的实现

        C++类要想被QML访问,首先必须满足两个条件:一是派生自QObject类或QObject类的子类,二是使用Q_OBJECT宏。QObject类是所有Qt对象的基类,作为Qt对象模型的核心,提供了信号与槽机制等很多重要特性。Q_OBJECT宏必须在private区(C++默认为private)声明,用来声明信号与槽,使用Qt元对象系统提供的内容,位置一般在语句块首行。Projects选择Qt Quick Application,工程名为Hello。

    1、信号与槽实现

    A、C++类实现

     1 #ifndef HELLO_H
     2 #define HELLO_H
     3 #include <QObject>
     4 #include <QDebug>
     5  
     6 class Hello: public QObject
     7 {
     8 Q_OBJECT
     9 public slots:
    10   void doSomething()
    11   {
    12     qDebug() << "Hello::dosomething() is called.";
    13   }
    14 signals:
    15   void begin();
    16 };
    17  
    18 #endif // HELLO_H

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

    B、注册C++类型

     1 #include <QGuiApplication>
     2 #include <QQmlApplicationEngine>
     3 #include <QtQml>
     4 #include "hello.h"
     5  
     6 int main(int argc, char *argv[])
     7 {
     8   QGuiApplication app(argc, argv);
     9   //注册C++类型Hello
    10   qmlRegisterType<Hello>("Hello.module",1,0,"Hello");
    11  
    12   QQmlApplicationEngine engine;
    13   engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    14  
    15   return app.exec();
    16 }

    将C++类注册到Qt元对象系统。

    C、在QML文件中导入C++类并使用

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.2
     3 //导入注册的C++类
     4 import Hello.module 1.0
     5  
     6 Window {
     7     visible: true
     8      640
     9     height: 480
    10     title: qsTr("Hello QML")
    11     MouseArea {
    12         anchors.fill: parent
    13         onClicked: {
    14             hello.begin();//单击鼠标调用begin信号函数
    15         }
    16     }
    17     Hello{
    18         id:hello   //Hello类的实例
    19         onBegin:doSomething()
    20     }
    21 }

        在QML文件中导入注册的C++类(import),关键字Hello就可以在当前QML文件中当作一种QML类型来用。MouseArea铺满界面,单击鼠标时会发送begin()信号,进而调用doSomething()槽函数。

    2、枚举类型实现

    A、C++类中枚举的定义

     1 #ifndef HELLO_H
     2 #define HELLO_H
     3 #include <QObject>
     4 #include <QDebug>
     5  
     6 class Hello: public QObject
     7 {
     8 Q_OBJECT
     9 Q_ENUMS(Color)
    10 public:
    11   Hello():m_color(RED)
    12   {
    13     qDebug() << "Hello() is called.";
    14   }
    15   //枚举
    16   enum Color
    17   {
    18     RED,
    19     BLUE,
    20     BLACK
    21   };
    22 public slots:
    23   void doSomething(Color color)
    24   {
    25     qDebug() << "Hello::dosomething() is called " << color;
    26   }
    27  
    28 signals:
    29   void begin();
    30 private:
    31   Color m_color;
    32 };
    33  
    34 #endif // HELLO_H

        C++类中添加了public的Color枚举类型,枚举类型要想在QML中使用,需要使用Q_ENUMS()宏。

    B、QML文件中使用C++枚举类型

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.2
     3 //导入注册的C++类
     4 import Hello.module 1.0
     5  
     6 Window {
     7     visible: true
     8      640
     9     height: 480
    10     title: qsTr("Hello QML")
    11     MouseArea {
    12         anchors.fill: parent
    13         onClicked: {
    14             hello.begin();//单击鼠标调用begin信号函数
    15         }
    16     }
    17     Hello{
    18         id:hello   //Hello类的实例
    19         onBegin:doSomething(Hello.RED)
    20     }
    21 }

        QML中使用枚举类型的方式是通过C++类型名使用“.”操作符直接访问枚举成员,如Hello.RED。

    3、成员函数实现

    A、成员函数定义

     1 #ifndef HELLO_H
     2 #define HELLO_H
     3 #include <QObject>
     4 #include <QDebug>
     5  
     6 class Hello: public QObject
     7 {
     8 Q_OBJECT
     9 Q_ENUMS(Color)
    10 public:
    11   Hello():m_color(RED)
    12   {
    13     qDebug() << "Hello() is called.";
    14   }
    15   //枚举
    16   enum Color
    17   {
    18     RED,
    19     BLUE,
    20     BLACK
    21   };
    22   Q_INVOKABLE void show()
    23   {
    24     qDebug() << "show() is called.";
    25   }
    26 public slots:
    27   void doSomething(Color color)
    28   {
    29     qDebug() << "Hello::dosomething() is called " << color;
    30   }
    31  
    32 signals:
    33   void begin();
    34 private:
    35   Color m_color;
    36 };
    37  
    38 #endif // HELLO_H

        如果QML中访问C++成员函数,则C++成员函数必须是public或protected成员函数,且使用Q_INVOKABLE宏,位置在函数返回类型的前面。

    B、QML中调用C++类成员函数

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.2
     3 //导入注册的C++类
     4 import Hello.module 1.0
     5  
     6 Window {
     7     visible: true
     8      640
     9     height: 480
    10     title: qsTr("Hello QML")
    11     MouseArea {
    12         anchors.fill: parent
    13         onClicked: {
    14             hello.begin();//单击鼠标调用begin信号函数
    15             hello.show();
    16         }
    17     }
    18     Hello{
    19         id:hello   //Hello类的实例
    20         onBegin:doSomething(Hello.RED)
    21     }
    22 }

        在QML中访问C++的成员函数的形式是“.”,如hello.show(),支持函数重载。

    4、C++类的属性

    A、C++类中属性的定义

     1 #ifndef HELLO_H
     2 #define HELLO_H
     3 #include <QObject>
     4 #include <QDebug>
     5  
     6 class Hello: public QObject
     7 {
     8 Q_OBJECT
     9 Q_ENUMS(Color)
    10 //属性声明
    11 Q_PROPERTY(Color color READ color WRITE setColor NOTIFY colorChanged)
    12 public:
    13   Hello():m_color(RED)
    14   {
    15     qDebug() << "Hello() is called.";
    16   }
    17   //枚举
    18   enum Color
    19   {
    20     RED,
    21     BLUE,
    22     BLACK
    23   };
    24   Q_INVOKABLE void show()
    25   {
    26     qDebug() << "show() is called.";
    27   }
    28   Color color() const
    29   {
    30     return m_color;
    31   }
    32   void setColor(const Color& color)
    33   {
    34     if(color != m_color)
    35     {
    36         m_color = color;
    37         emit colorChanged();
    38     }
    39   }
    40 public slots:
    41   void doSomething(Color color)
    42   {
    43     qDebug() << "Hello::dosomething() is called " << color;
    44   }
    45  
    46 signals:
    47   void begin();
    48   void colorChanged();
    49 private:
    50   Color m_color;//属性
    51 };
    52  
    53 #endif // HELLO_H

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

    Q_PROPERTY()(type name
    
         (READ getFunction [WRITE setFunction] |
    
                 MEMBER memberName [(READ getFunction | WRITE setFunction)])
    
                [RESET resetFunction]
    
                [NOTIFY notifySignal]
    
                [REVISION int]
    
                [DESIGNABLE bool]
    
                [SCRIPTABLE bool]
    
                [STORED bool]
    
                [USER bool]
    
                [CONSTANT]
    
                [FINAL])

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

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

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

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

    B、QML中修改属性

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.2
     3 //导入注册的C++类
     4 import Hello.module 1.0
     5  
     6 Window {
     7     visible: true
     8      640
     9     height: 480
    10     title: qsTr("Hello QML")
    11     MouseArea {
    12         anchors.fill: parent
    13         onClicked: {
    14             hello.begin()//单击鼠标调用begin信号函数
    15             hello.show()
    16             hello.color = 2  //修改属性
    17         }
    18     }
    19     Hello{
    20         id:hello   //Hello类的实例
    21         onBegin:doSomething(Hello.RED)
    22         onColorChanged:console.log("color changed.")
    23     }
    24 }

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

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

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

    qmlRegisterInterface()
    
    qmlRegisterRevision()
    
    qmlRegisterSingletonType()
    
    qmlRegisterType()
    
    qmlRegisterTypeNotAvailable()
    
    qmlRegisterUncreatableType()
    
    template<typename T>
    
    int qmlRegisterType(const char *uri,int versionMajor,
    
           int versionMinor, const char *qmlName);

        模板函数注册C++类到Qt元对象系统中,uri是需要导入到QML中的库名,versionMajor和versionMinor是其版本数字,qmlName是在QML中可以使用的类型名。

        qmlRegisterType<Hello>("Hello.module",1,0,"Hello");

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

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

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

    五、QML上下文属性设置

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

    A、C++设置上下文属性

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

        Hello类先实例化为hello对象,然后注册为QML上下文属性。

    B、QML使用

     1 import QtQuick 2.5
     2  
     3 Item {
     4      640
     5     height: 480
     6     MouseArea {
     7         anchors.fill: parent
     8         onClicked: {
     9             hello.begin()//单击鼠标调用begin信号函数
    10             hello.show()
    11         }
    12     }
    13     Connections{
    14        target:hello
    15        onBegin:console.log("hello")
    16     }

        在main.qml中不能使用Hello类型来实例化,也不能调用doSomething()槽函数,因为doSomething()函数中的枚举类型在QML中是访问不到的,正确的用法是通过已经设置的QML上下文属性“hello”来访问C++,可以访问信号begin()和成员函数show(),此时的信号处理器需要用Connections来处理。

    六、C++访问QML

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

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

    1、C++使用QQmlComponent

     1 #include <QGuiApplication>
     2 #include <QQmlApplicationEngine>
     3 #include <QtQml>
     4 #include "hello.h"
     5  
     6 int main(int argc, char *argv[])
     7 {
     8   QGuiApplication app(argc, argv);
     9   //注册C++类型Hello
    10   qmlRegisterType<Hello>("Hello.module",1,0,"Hello");
    11  
    12   QQmlApplicationEngine engine;
    13   QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
    14   component.create();
    15  
    16   return app.exec();
    17 }

    2、C++使用QML的属性

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

    A、QML中定义部分元素的属性

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.0
     3 import Hello.module 1.0
     4  
     5 Window {
     6     visible: true
     7      640
     8     height: 480
     9     title: "Hello QML"
    10     color: "white"
    11     MouseArea {
    12         anchors.fill: parent
    13         onClicked: {
    14             hello.begin()//单击鼠标调用begin信号函数
    15             hello.doSomething(2)
    16         }
    17     }
    18     Rectangle{
    19         objectName: "rect"
    20         anchors.fill: parent
    21         color:"red"
    22         }
    23     Hello{
    24        id:hello
    25        onBegin:console.log("hello")
    26        onColorChanged:hello.show()
    27     }
    28 }

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

    B、C++中使用QML元素的属性

     1 #include <QGuiApplication>
     2 #include <QQmlApplicationEngine>
     3 #include <QtQml>
     4 #include "hello.h"
     5  
     6 int main(int argc, char *argv[])
     7 {
     8   QGuiApplication app(argc, argv);
     9   //注册C++类型Hello
    10   qmlRegisterType<Hello>("Hello.module",1,0,"Hello");
    11  
    12   QQmlApplicationEngine engine;
    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);//设置window的宽
    17   qDebug() << "width value is" << object->property("width").toInt();
    18   qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
    19   QQmlProperty::write(object, "height", 300);//设置window的高
    20   qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
    21   QObject* rect = object->findChild<QObject*>("rect");//查找名称为“rect”的元素
    22   if(rect)
    23   {
    24       rect->setProperty("color", "blue");//设置元素的color属性值
    25       qDebug() << "color is " << object->property("color").toString();
    26   }
    27  
    28   return app.exec();
    29 }

        QObject::property()/setProperty()来读取、修改width属性值。

        QQmlProperty::read()/write()来读取、修改height属性值。

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

        QML组件是一个复杂的树型结构,包含兄弟组件和孩子组件,可以使用QObject::findchild()/findchildren()来查找。

    3、C++使用QML中信号与函数

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

    QGenericArgument           Q_ARG(Type, const Type & value)

    QGenericReturnArgument     Q_RETURN_ARG(Type, Type & value)

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

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

    A、QML中定义信号与函数

     1 import QtQuick 2.5
     2 import QtQuick.Window 2.0
     3 import Hello.module 1.0
     4  
     5 Window {
     6     visible: true
     7      640
     8     height: 480
     9     title: "Hello QML"
    10     color: "white"
    11     //定义信号
    12     signal qmlSignal(string message)
    13     //定义函数
    14     function qmlFunction(parameter) {
    15         console.log("qml function parameter is", parameter)
    16         return "function from qml"
    17     }
    18     MouseArea {
    19         anchors.fill: parent
    20         onClicked: {
    21             hello.begin()//单击鼠标调用begin信号函数
    22             hello.doSomething(2)
    23             qmlSignal("This is an qml signal.")//发送信号
    24         }
    25     }
    26     Rectangle{
    27         objectName: "rect"
    28         anchors.fill: parent
    29         color:"red"
    30         }
    31     Hello{
    32        id:hello
    33        onBegin:console.log("hello")
    34        onColorChanged:hello.show()
    35     }
    36 }

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

    B、C++中调用

     1 #include <QGuiApplication>
     2 #include <QQmlApplicationEngine>
     3 #include <QtQml>
     4 #include "hello.h"
     5  
     6 int main(int argc, char *argv[])
     7 {
     8   QGuiApplication app(argc, argv);
     9   //注册C++类型Hello
    10   qmlRegisterType<Hello>("Hello.module",1,0,"Hello");
    11  
    12   QQmlApplicationEngine engine;
    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);//设置window的宽
    17   qDebug() << "width value is" << object->property("width").toInt();
    18   qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
    19   QQmlProperty::write(object, "height", 300);//设置window的高
    20   qDebug() << "height value is" << QQmlProperty::read(object, "height").toInt();
    21   QObject* rect = object->findChild<QObject*>("rect");//查找名称为“rect”的元素
    22   if(rect)
    23   {
    24       rect->setProperty("color", "blue");//设置元素的color属性值
    25       qDebug() << "color is " << object->property("color").toString();
    26   }
    27   //调用QML中函数
    28   QVariant returnedValue;
    29   QVariant message = "Hello from C++";
    30   QMetaObject::invokeMethod(object, "qmlFunction",
    31                             Q_RETURN_ARG(QVariant, returnedValue),
    32                             Q_ARG(QVariant, message));
    33   qDebug() << "returnedValue is" << returnedValue.toString(); // function from qml
    34   Hello hello;
    35   //连接QML元素中的信号到C++槽函数
    36   QObject::connect(object, SIGNAL(qmlSignal(QString)),
    37                    &hello, SLOT(qmlSlot(QString)));
    38  
    39   return app.exec();
    40 }

    C++类中的槽函数:

    1 public slots:
    2   void doSomething(Color color)
    3   {
    4     qDebug() << "Hello::dosomething() is called " << color;
    5   }
    6   void qmlSlot(const QString& message)
    7   {
    8     qDebug() << "C++ called: " << message;
    9   }

    七、QML与C++混合编程注意事项

        QML与混合编程注意事项:

        A、自定义C++类一定要派生自QObject类或其子类,并使用Q_OBJECT宏。

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

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

  • 相关阅读:
    前端JavaScript(2) --常用内置对象,函数,伪数组 arguments,关于DOM的事件操作,DOM介绍
    前端JavaScript(3)-关于DOM操作的相关案例,JS中的面向对象、定时器、BOM、位置信息
    前端JavaScript(1) --Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏
    前端CSS(3)
    CSS标签大全
    前端CSS(2)
    前端CSS(1)
    前端HTML(二/三)
    前端基础(1)
    第一个自定义HTML网页
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14270424.html
Copyright © 2020-2023  润新知