C++与QML混合编程——QML中使用C++对象

一、概述

QtQuick技术的引入,使得我们能够快速构建流畅的用户界面,具有动画、各种绚丽效果的UI都不在话下,但是,它又不是万能的,也有很多局限性,原来的Qt的一些技术,比如网络编程QTcpSocket、多线程、又如XML文档处理类库QXmlStreamReader、QXmlStreamWirter、再如文件操作QFile、QTextStream等,在QMl中要不不可用,要不用起来不方便,所以,很多时候我们会混合使用QML和C++,使用QML构建界面,使用C++实现非界面的业务逻辑和复杂的运算。接下来,让我们一起来看看,QML与C++直接是如何进行交互的。

二、在QML中使用C++类和对象

Qt提供了两种在QML环境使用C++对象的方式:

  • 在C++中实现一个类,注册成为QML环境的一个类型,在QML环境中使用该类型创建对象。
  • 在C++中构建一个对象,将这个对象设置为QML的上下文属性,在QML环境中直接使用该属性。

不管哪种方式,对要导出的C++类都有要求,不是一个类的所有方法、变量都可以在QML语境中使用,接下来,一起看看怎么让一个方法或属性可以被QML代码访问吧!

2.1 定义可以导出的C++类

前提条件
想要将一个类或者对象导出到QML中,下列条件必须满足:

  • 从QObject或QObejct派生类继承
  • 使用Q-OBJECT宏

和使用QT信号槽的前提条件一样,目的是为了让一个类能进入QT强大的元对象系统,只有使用了元对象系统,才能让一个类的方法或属性可能通过字符串形式的名字来调用,才具有在QML中访问的基础条件。

具有什么特征的属性或者方法才可以被QML访问呢?

(1) 信号、槽
只要是信号或者槽,都可以在QML中访问,可以把C++对象的信号连接到QML定义的方法上,也可以把QML对象的信号连接到C++对象的槽上,还可以直接调用C++对象的槽或者信号。

(2) Q_INVOKABLE宏
在定义一个类的成员函数时,使用Q_INVOKABLE宏修饰,就可以让该方法让元对象系统调用。这个宏必须放在返回值类型前面。

Q_INVOKABLE void function();

(3) Q_ENUMS宏
如果你要导出的类定义了想在QML中使用的枚举类型,可以使用Q_ENUMS宏将该枚举类型注册到元对象系统中。这样,QML代码中就可以使用导出的枚举类型了。

    Q_ENUMS(ColorType);
    enum ColorType{
        Red,
        Green,
        Blue,
    };   

(4) Q_PROPERTY宏
Q_PRORETY用来定义可通过元对象系统访问的属性,通过他定义的属性,可以在QML中访问、修改,也可以在修改的时候发出特定的信号,要想使用Q_PROPERTY宏,你的类必须是QObject的派生类,必须在类首使用Q_OBJECT宏。

下面是Q_PROPERTY宏的原型:

 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])

看着是不是很复杂? 你可以为一个属性,可以设定的选项数超过10个,是不是很头疼? 不过,不是所有选项都必须设置,来看一个简单的例子:

Q_PROPERTY(int x READ x)

定义了一个类型为int,名为x的属性,通过方法x()来访问。

常用参数的含义:

  • type name:type属性类型,可以是int、float、QString、QObject、QColor、QFont等,name是属性的名字;

  • READ: 如果你没有为属性指定MEMBER标记,则READ标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性。

  • WRITR: 可选配置。声明一个设定属性的函数。它指定的函数没有返回值,只有一个能与属性类型相匹配的参数。

  • NOTIFY: 可选配置。给属性关联一个信号(该信号必须是已经在类中声明过的),当属性的值发生变化时,就会触发该信号。信号的参数,一般就是你定义的属性。

    其他标记的含义,可查阅Qt帮助手册。

#ifndef WORKER_H
#define WORKER_H
#include 
#include 
class worker : public QObject
{
Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChange)
public:
    worker(QObject *parent = nullptr);
    QColor color(){
        return m_color;
    }
    void setColor(QColor color){
        m_color = color;
    }
signals:
    void colorChange(QColor color);
private:
    QColor m_color;
};

#endif // WORKER_H

2.2 注册一个QML可用的类型

学习了怎么实现一个可供QML访问的类,接下来我们一起来看看,怎样将一个C++类型注册为一个QML类型,以及怎样在QML中使用这个类型
要达到这种目的,大概可以分为4步:

  1. 实现C++类
  2. 注册QML类型
  3. 在QML中导入类型
  4. 在QML中创建由C++导出的类型的实例并使用

2.21 注册QML类型:

要注册一个QML类型,有以下方法:

  • qmlRegisterType() //注册一个非单例类型
  • qmlRegisterSingletonType() //注册一个单例类型
  • qmlRegisterUncreatableType() //注册一个具有附加属性的附加类型
  • qmlRegisterTypeNotAvailable() //注册一个类型用来占位

在这,我只是说说常规的类型注册,其他的可以参考QT官方文档:

template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

template<typename T, int metaObjectRevision>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

qmlRegisterType()是个函数模板,有两个原型,第一个原型一般用来注册一个新类型,第二个原型可以为特定的版本注册类型,而这原型牵涉到Qt Quick是类型和版本机制,比较复杂,所以来说说第一个原型的使用。
使用qmlRegisterType()需要包含头文件 #include 或者 #include ;

qmlRegisterType 各参数代表的意思:

  • uri : 用来指定唯一的包名:如qml中的 QtQuick.Controls
  • versionMajor:主版本号
  • versionMinor:次版本号
  • qmlName:在qml中可以使用的类名
#include 
#include 
#include 
#include "worker.h"
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    qmlRegisterType<Worker>("my.qt.Worker",1,0,"Worker");

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
    return app.exec();
}

上面代码,将Worker 类注册为QML类型,主版本为1,次版本为0,包名则是my.qt.Worker。
注意:注册动作要放在QML 上下文创建之前,否则不起作用。

2.22 在QML中导入C++注册类型

一旦你在C++中注册好了QML类型,就可以在qml文档中引入你注册的包,然后使用注册的类型。

import my.qt.Worker 1.0

2.23 在QML中创建C++导入类型的实例

import QtQuick 2.12
import my.qt.Worker 1.0
Rectangle {
    visible: true
    width: 300
    height: 200
    MyWorker{
        id:worker
        onColorChange: {
            console.log("color change");
            rect.color = color
        }
    }
    Rectangle{
        id: rect
       anchors.fill: parent
       Button{
           id:button
           anchors.centerIn: parent
           text: "testButton"
           onClicked: {
               worker.color = "grey"
               console.log("button clicked")
           }
       }
    }
    Component.onCompleted: {
        worker.color = "blue"
    }
}

#ifndef Worker_H
#define Worker_H
#include 
#include 
#include 
class Worker : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChange)
public:
    Worker(QObject *parent = nullptr);

    QColor color(){
        return m_color;
    }
    void setColor(QColor color){
        m_color = color;
        emit colorChange(color);
    }

signals:
    void colorChange(QColor color);
private:
    QColor m_color;
};

#endif // Worker_H

2.3 将一个C++对象导出为QML属性

上面看了怎样将C++类导出为QML可以使用的类型,你还可以将C++中创建的队形作为属性传递到QML环境中,然后在QML环境中访问。

2.3.1 注册属性

#include 
#include 
#include 
#include 
#include "worker.h"
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
   // qmlRegisterType("my.qt.Worker",1,0,"MyWorker");
    QGuiApplication app(argc,argv);
    QQuickView viewer;
    viewer.rootContext()->setContextProperty("myWorker",new Worker);
    viewer.setResizeMode(QQuickView::SizeRootObjectToView);
    viewer.setSource(QUrl("qrc:/main.qml"));
    viewer.show();
    return app.exec();
}

我在viewer后增加了setContextProperty();正是这行代码,在堆上分配了一个Worker对象,然后注册为QML上下文属性,起了个名字叫myWorker,

2.3.2 在QML中使用关联到的C++对象属性

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.1
Rectangle {
    visible: true
    width: 300
    height: 200
    Rectangle{
        id: rect
       anchors.fill: parent
       Button{
           id:button
           anchors.centerIn: parent
           text: "testButton"
           onClicked: {
               myWorker.setColor("red")
               console.log("button clicked")
           }
       }
    }
    Connections{
        target: myWorker
        onColorChange:{
            rect.color = color
        }
    }
}

三、在C++中使用QML对象

转下篇…

你可能感兴趣的:(QML,c++,qt)