[Qt5] How to connect c++ with QML

Qt5处于过度阶段,架构繁琐,学习成本不低。尤其是UI代码竟然被重写,变了天。

Qt中的c++可能是连接OPENCV与QML的一个不错的桥梁,在此学习这部分实用的技术。

Reference: http://blog.csdn.net/foruok/article/details/32698603

一、在 QML 中调用 c++

  

  1. 实现 c++

  2. 注册 QML 类型

  3. QML 中导入类型

  4. QML 创建由 C++ 导出的类型的实例并使用

 

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

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

对要导出的 C++ 类都有要求,不是一个类的所有方法、变量都可以被 QML 使用。

因此我们先来看看怎样让一个方法属性可以被 QML 使用。

 


  

对要导出的 C++ 类都有要求,不是一个类的所有方法、变量都可以被 QML 使用。
  • 从 QObject 或 QObject 的派生类继承
  • 使用 Q_OBJECT 宏

 


  

信号,槽 可以直接被调用 (方法)

成员函数时使用 Q_INVOKABLE 宏来修饰,可以让该方法被元对象系统调用。

class ColorMaker : public QObject
{
    Q_OBJECT

public:
    ColorMaker(QObject *parent = 0);
    ~ColorMaker();
    
    Q_INVOKABLE GenerateAlgorithm algorithm() const;
    Q_INVOKABLEvoid setAlgorithm(GenerateAlgorithm algorithm);

signals:
    void colorChanged(const QColor & color);
    void currentTime(const QString &strTime);

publicslots:
    void start();
    void stop();
};

 

 QML 中使用 ${Object}.${method} 来访问:

Component.onCompleted: {
colorMaker.color
= Qt.rgba(0,180,120, 255); colorMaker.setAlgorithm(ColorMaker.LinearIncrease); changeAlgorithm(colorAlgorithm, colorMaker.algorithm()); }

 


  

Q_PROPERTY 宏  (属性)

Q_PROPERTY 宏用来定义可通过元对象系统访问的属性。

通过它定义的属性,可以在 QML 中访问、修改,也可以在属性变化时发射特定的信号。

要想使用 Q_PROPERTY 宏,你的类必须是 QObject 的后裔,必须在类首使用 Q_OBJECT 宏。

 

比较常用的是 READ / WRITE / NOTIFY 三个选项。我们来看看都是什么含义。
  • READ 标记  如果你没有为属性指定 MEMBER 标记,则 READ 标记必不可少;声明一个读取属性的函数,该函数一般没有参数,返回定义的属性。
  • WRITE 标记  可选配置。声明一个设定属性的函数。它指定的函数,只能有一个与属性类型匹配的参数,必须返回 void 。
  • NOTIFY 标记   可选配置。给属性关联一个信号(该信号必须是已经在类中声明过的),当属性的值发生变化时就会触发该信号。信号的参数,一般就是你定义的属性。

 

class QQuickText : public QQuickImplicitSizeItem
{
    Q_OBJECT
    Q_ENUMS(HAlignment)

    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
    Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
    ...
    
public:
    enum HAlignment { AlignLeft = Qt::AlignLeft,
                       AlignRight = Qt::AlignRight,
                       AlignHCenter = Qt::AlignHCenter,
                       AlignJustify = Qt::AlignJustify };
    ...
    QString text() const;
    void setText(const QString &);

    QFont font() const;
    void setFont(const QFont &font);

    QColor color() const;
    void setColor(const QColor &c);
    ...
};

 


  1. 实现 c++类 (define好了类,之后实现各函数)

  2. 注册 QML 类型 (下一步)

 

要注册一个 QML 类型,有多种方法可用,如

qmlRegisterSingletonType() 用来注册一个单例类型, 

qmlRegisterType() 注册一个非单例的类型, // 模板函数

qmlRegisterTypeNotAvailable() 注册一个类型用来占位, 

qmlRegisterUncreatableType() 通常用来注册一个具有附加属性的附加类

 

其中一个原型:

template<typename T>  

// 包名,主版本,次版本,类名 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);

 

#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QtQml>
#include "colorMaker.h"


int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

   // 注册qml类型 qmlRegisterType
<ColorMaker>("an.qt.ColorMaker", 1, 0, "ColorMaker"); QtQuick2ApplicationViewer viewer; viewer.setMainQmlFile(QStringLiteral("qml/colorMaker/main.qml")); viewer.showExpanded(); return app.exec(); }

 

import QtQuick 2.0
import QtQuick.Controls 1.1
import an.qt.ColorMaker 1.0

Rectangle {
    width: 360;
    height: 360;
    Text {
        id: timeLabel;
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.top: parent.top;
        anchors.topMargin: 4;
        font.pixelSize: 26;
    }
    ColorMaker {
        id: colorMaker;
        color: Qt.green;
    }

    Rectangle {
        id: colorRect;
        anchors.centerIn: parent;
        width: 200;
        height: 200;
        color: "blue";
    }

    Button {
        id: start;
        text: "start";
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.bottom: parent.bottom;
        anchors.bottomMargin: 4;
        onClicked: {
            colorMaker.start();
        }
    }
    Button {
        id: stop;
        text: "stop";
        anchors.left: start.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            colorMaker.stop();
        }
    }

    function changeAlgorithm(button, algorithm){
        switch(algorithm)
        {
        case 0:
            button.text = "RandomRGB";
            break;
        case 1:
            button.text = "RandomRed";
            break;
        case 2:
            button.text = "RandomGreen";
            break;
        case 3:
            button.text = "RandomBlue";
            break;
        case 4:
            button.text = "LinearIncrease";
            break;
        }
    }

    Button {
        id: colorAlgorithm;
        text: "RandomRGB";
        anchors.left: stop.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            var algorithm = (colorMaker.algorithm() + 1) % 5;
            changeAlgorithm(colorAlgorithm, algorithm);
            colorMaker.setAlgorithm(algorithm);
        }
    }

    Button {
        id: quit;
        text: "quit";
        anchors.left: colorAlgorithm.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            Qt.quit();
        }
    }

    Component.onCompleted: {
        colorMaker.color = Qt.rgba(0,180,120, 255);
        colorMaker.setAlgorithm(ColorMaker.LinearIncrease);
        changeAlgorithm(colorAlgorithm, colorMaker.algorithm());
    }

    Connections {
        target: colorMaker;
        onCurrentTime:{
            timeLabel.text = strTime;
            timeLabel.color = colorMaker.timeColor;
        }
    }

    Connections {
        target: colorMaker;
        onColorChanged:{
            colorRect.color = color;
        }
    }
}
qml中调用c++

 


 

  1. 实现 c++

  2. 注册 QML 类型

  3. 在 QML 中导入类型  ( 下一步 )

 

将 对象 直接作为参数 传入 QML

把 C++ 中创建的对象作为属性传递到 QML 环境中,然后在 QML 环境中访问。就不需要 import 语句了!

  

将一个对象注册为属性:

#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QtQml>
#include "colorMaker.h"


int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    
   // viewer.rootContext() 返回的是 QQmlContext 对象。
   // QQmlContext 类代表一个 QML 上下文,它的 setContextProperty() 方法可以为该上下文设置一个全局可见的属性。 viewer.rootContext()
->setContextProperty("colorMaker", new ColorMaker);
// 从堆上分配了一个 ColorMaker 对象 viewer.setMainQmlFile(QStringLiteral("qml/colorMaker/main.qml")); viewer.showExpanded(); return app.exec(); }

 

貌似,这个方法比较方便呐。

import QtQuick 2.0
import QtQuick.Controls 1.1
//[1]
//import an.qt.ColorMaker 1.0

Rectangle {
    width: 360;
    height: 360;
    Text {
        id: timeLabel;
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.top: parent.top;
        anchors.topMargin: 4;
        font.pixelSize: 26;
    }
    /* [2]
    ColorMaker {
        id: colorMaker;
        color: Qt.green;
    }
    */

    Rectangle {
        id: colorRect;
        anchors.centerIn: parent;
        width: 200;
        height: 200;
        color: "blue";
    }

    Button {
        id: start;
        text: "start";
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.bottom: parent.bottom;
        anchors.bottomMargin: 4;
        onClicked: {
            colorMaker.start();
        }
    }
    Button {
        id: stop;
        text: "stop";
        anchors.left: start.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            colorMaker.stop();
        }
    }

    function changeAlgorithm(button, algorithm){
        switch(algorithm)
        {
        case 0:
            button.text = "RandomRGB";
            break;
        case 1:
            button.text = "RandomRed";
            break;
        case 2:
            button.text = "RandomGreen";
            break;
        case 3:
            button.text = "RandomBlue";
            break;
        case 4:
            button.text = "LinearIncrease";
            break;
        }
    }

    Button {
        id: colorAlgorithm;
        text: "RandomRGB";
        anchors.left: stop.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            var algorithm = (colorMaker.algorithm() + 1) % 5;
            changeAlgorithm(colorAlgorithm, algorithm);
            colorMaker.setAlgorithm(algorithm);
        }
    }

    Button {
        id: quit;
        text: "quit";
        anchors.left: colorAlgorithm.right;
        anchors.leftMargin: 4;
        anchors.bottom: start.bottom;
        onClicked: {
            Qt.quit();
        }
    }

    Component.onCompleted: {
        colorMaker.color = Qt.rgba(0,180,120, 255);
        //[3]
        //colorMaker.setAlgorithm(ColorMaker.LinearIncrease);
        colorMaker.setAlgorithm(2);
        changeAlgorithm(colorAlgorithm, colorMaker.algorithm());
    }

    Connections {
        target: colorMaker;
        onCurrentTime:{
            timeLabel.text = strTime;
            timeLabel.color = colorMaker.timeColor;
        }
    }

    Connections {
        target: colorMaker;
        onColorChanged:{
            colorRect.color = color;
        }
    }
}
对象作为参数

 


 

二、在c++中使用 QML 对象

 
我们可以使用 QML 对象的信号、槽,访问它们的属性,都没有问题,因为很多 QML 对象对应的类型,原本就是 C++ 类型,比如
    • Image 对应 QQuickImage ,
    • Text 对应 QQuickText
但是,这些与 QML 类型对应的 C++ 类型都是私有的,你写的 C++ 代码也不能直接访问。怎么办?
 
Qt 最核心的一个基础特性,就是元对象系统。
通过元对象系统,你可以查询 QObject 的某个派生类的类名、有哪些信号、槽、属性、可调用方法等等信息
然后也可以使用 QMetaObject::invokeMethod() 调用 QObject 的某个注册到元对象系统中的方法。
而对于使用 Q_PROPERTY 定义的属性,可以使用 QObject 的 property() 方法访问属性,如果该属性定义了 WRITE 方法,还可以使用 setProperty() 修改属性。
 
所以只要我们找到 QML 环境中的某个对象,就可以通过元对象系统来访问它的属性、信号、槽等。
关键就是:查找对象!
 
查找如下QML的对象:
import QtQuick 2.0
import QtQuick.Controls 1.1

Rectangle {
    objectName: "rootRect";
    width: 360;
    height: 360;
    Text {
        objectName: "textLabel";
        text: "Hello World";
        anchors.centerIn: parent;
        font.pixelSize: 26;
    }

    Button {
        anchors.right: parent.right;
        anchors.rightMargin: 4;
        anchors.bottom: parent.bottom;
        anchors.bottomMargin: 4;
        text: "quit";
        objectName: "quitButton";
    }
}

 

我们给根元素起了个名字 "rootRect" ,给退出按钮起了个名字 "quitButton" ,给文本起了名字 "textLabel" 。
我们会在 C++ 代码中通过这些个名字来查找对应的对象并改变它们。
 
#include <QtGui/QGuiApplication>
#include "qtquick2applicationviewer.h"
#include <QQuickItem>
#include "changeColor.h"
#include <QMetaObject>
#include <QDebug>
#include <QColor>
#include <QVariant>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    viewer.setMainQmlFile(QStringLiteral("qml/callQml/main.qml"));
    viewer.showExpanded();

    QQuickItem * rootItem = viewer.rootObject();
    new ChangeQmlColor(rootItem);  // 内部通过一个定时器,一秒改变一次传入对象的颜色
QObject
* quitButton = rootItem->findChild<QObject*>("quitButton"); if(quitButton) {
      // clicked() 信号连接到 QGuiApplication 的 quit() 槽上 QObject::connect(quitButton, SIGNAL(clicked()),
&app, SLOT(quit())); } QObject *textLabel = rootItem->findChild<QObject*>("textLabel"); if(textLabel) { //1. failed call bool bRet = QMetaObject::invokeMethod(textLabel, "setText", Q_ARG(QString, "world hello")); qDebug() << "call setText return - " << bRet; textLabel->setProperty("color", QColor::fromRgb(255,0,0));
//2. good call bRet
= QMetaObject::invokeMethod(textLabel, "doLayout"); // 对应的c++中的接口 名词不同,所以上面的失败了 qDebug() << "call doLayout return - " << bRet; } return app.exec(); }

 

#ifndef CHANGECOLOR_H
#define CHANGECOLOR_H
#include <QObject>
#include <QTimer>

class ChangeQmlColor : public QObject
{
    Q_OBJECT
public:
    ChangeQmlColor(QObject *target, QObject *parent = 0);
    ~ChangeQmlColor();

protected slots:
    void onTimeout();

private:
    QTimer m_timer;
    QObject *m_target;
};

#endif
ChangeQmlColor定义
#include "changeColor.h"
#include <QDateTime>
#include <QColor>
#include <QVariant>

ChangeQmlColor::ChangeQmlColor(QObject *target, QObject *parent)
    : QObject(parent)
    , m_timer(this)
    , m_target(target)
{
    qsrand(QDateTime::currentDateTime().toTime_t());
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    m_timer.start(1000);
}

ChangeQmlColor::~ChangeQmlColor()
{}

void ChangeQmlColor::onTimeout()
{
    QColor color = QColor::fromRgb(qrand()%256, qrand()%256, qrand()%256);
    m_target->setProperty("color", color);
}
ChangeQmlColor实现

 

posted @ 2016-11-24 08:03  郝壹贰叁  阅读(1987)  评论(0编辑  收藏  举报