[Qt5] How to connect c++ with QML
Qt5处于过度阶段,架构繁琐,学习成本不低。尤其是UI代码竟然被重写,变了天。
Qt中的c++可能是连接OPENCV与QML的一个不错的桥梁,在此学习这部分实用的技术。
Reference: http://blog.csdn.net/foruok/article/details/32698603
一、在 QML 中调用 c++
-
实现 c++类
-
注册 QML 类型
-
在 QML 中导入类型
-
在 QML 创建由 C++ 导出的类型的实例并使用
Qt 提供了两种在 QML 环境中使用 C++ 对象的方式:
- 在 C++ 中实现一个类,注册到 QML 环境中, QML 环境中使用该类型创建对象
- 在 C++ 中构造一个对象,将这个对象设置为 QML 的上下文属性,在 QML 环境中直接使用改属性
对要导出的 C++ 类都有要求,不是一个类的所有方法、变量都可以被 QML 使用。
因此我们先来看看怎样让一个方法或属性可以被 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 标记 如果你没有为属性指定 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);
...
};
-
实现 c++类 (define好了类,之后实现各函数)
-
注册 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; } } }
-
实现 c++类
-
注册 QML 类型
-
在 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 对象
-
- Image 对应 QQuickImage ,
- Text 对应 QQuickText
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";
}
}
#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
#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); }