????????Qt quick能夠生成非常絢麗界面,但有其局限性的,對于一些業(yè)務邏輯和復雜算法,比如低階的網(wǎng)絡(luò)編程如 QTcpSocket ,多線程,又如 XML 文檔處理類庫 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起來不方便,所以就有了quick和C++混合編程的需求。
原理和方法
????????簡單來說,混合編程就是通過Qml高效便捷的構(gòu)建UI界面,而使用C ++來實現(xiàn)業(yè)務邏輯和復雜算法。Qt集成了QML引擎和Qt元對象系統(tǒng),使得QML很容易從C ++中得到擴展,在一定的條件下,QML就可以訪問QObject派生類的成員,例如信號、槽函數(shù)、枚舉類型、屬性、成員函數(shù)等。
要想在Qml中訪問C ++對象,必然要找到一種方法在兩者之間建立聯(lián)系,而Qt中提供了兩種在 QML 環(huán)境中使用C ++對象的方式:
- 在C ++中實現(xiàn)一個類,注冊到Qml環(huán)境中,Qml環(huán)境中使用該類型創(chuàng)建對象
- 在C ++中構(gòu)造一個對象,將這個對象設(shè)置為Qml的上下文屬性,在Qml環(huán)境中直接使用該屬性
兩種方式之間的區(qū)別是第一種可以使C ++類在QML中作為一個數(shù)據(jù)類型,例如函數(shù)參數(shù)類型或?qū)傩灶愋?,也可以使用其枚舉類型、單例等,功能更強大。
QML可以訪問的C++ 類
????????先來看第一種方式,C++類要想被QML訪問,首先必須滿足兩個條件:一是派生自QObject類或QObject類的子類,二是使用Q_OBJECT宏。QObject類是所有Qt對象的基類,作為Qt對象模型的核心,提供了信號與槽機制等很多重要特性。Q_OBJECT宏必須在private區(qū)(C++默認為private)聲明,用來聲明信號與槽,使用Qt元對象系統(tǒng)提供的內(nèi)容,位置一般在語句塊首行。我們還是新建一個Qt quick工程,然后我們一點一點的來看如何實現(xiàn)可以被QML訪問的C++類。首先肯定是需要構(gòu)建一個類:
?
?文章來源地址http://www.zghlxwxcb.cn/news/detail-657721.html
信號和槽
只要是信號和槽,都可以在 QML 中訪問,可以把 C++ 對象的信號連接到 QML 中定義的方法上,也可以把 QML 對象的信號連接到 C++ 對象的槽上,還可以直接調(diào)用 C++ 對象的槽或信號……所以,這是最簡單好用的一種途徑。
在mixing.h文件中定義一個信號和一個槽函數(shù)
?
#ifndef MIXING_H
#define MIXING_H
#include <QObject>
#include <QColor>
class Mixing : public QObject
{
Q_OBJECT
public:
explicit Mixing(QObject *parent = nullptr);
signals:
void colorChanged(const QColor & color);
public slots:
void start();
};
#endif // MIXING_H
Mixing類中的信號colorChanged()和槽函數(shù)start都可以被Qml訪問,但是注意槽必須被聲明為public或protected,而且信號在 C++ 中使用時要用到emit關(guān)鍵字,但是在Qml中就是個普通的函數(shù)。我們想要實現(xiàn)的效果是點擊鼠標,改變窗體顏色,看一下信號和槽是如何在Qml和 C++中傳遞的。
在mixing.cpp中聲明槽函數(shù)
#include "mixing.h"
#include <QDebug>
Mixing::Mixing(QObject *parent) : QObject(parent)
{
}
void Mixing::start()
{
qDebug() << "start";
emit colorChanged(Qt::blue);
}
?這里傳遞了一個顏色到Qml中,當然,現(xiàn)在我們肯定還無法在qml文件中使用Mixing類,那么如何將Mixing類注冊為Qml類型呢?其實有很多辦法,我們這里就舉一個最常規(guī)的注冊類型qmlRegisterType。它比較常見的原型:
template<typename T>
int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
?
模板參數(shù)typename ,就是你要實現(xiàn)的 C++ 類的類名。它的第一個參數(shù)uri?,讓你指定一個唯一的包名,類似Java 中的那種,一是用來避免名字沖突,二是可以把多個相關(guān)類聚合到一個包中方便引用。比如我們常寫這個語句 "import QtQuick.Controls 2.3" ,其中的 "QtQuick.Controls" 就是包名 uri ,而2.3則是版本,是versionMajor和versionMinor的組合。?qmlName則是 QML中可以使用的類名。
我們通過qmlRegisterType將Mixing注冊為qml類型,修改main.cpp文件
?
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "mixing.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
qmlRegisterType<Mixing>("an.qt.Mixing", 1, 0, "Mixing");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
?
我們把Mixing類注冊成為Qml類型Mixing,主版本是1,次版本是0,包名是an.qt.Mixing。注意:注冊動作一定要放在 QML 上下文創(chuàng)建之前,否則的話,注冊是沒有用的。
接下來我們就可以在qml文件中導入Mixing類,并且使用它了。
import QtQuick 2.9
import QtQuick.Window 2.2
import an.qt.Mixing 1.0
Window {
id:root
visible: true
width: 640
height: 480
title: qsTr("mixing")
MouseArea{
anchors.fill: parent
onClicked: {
mixing.start()
}
}
Mixing{
id: mixing
onColorChanged: {
root.color = color
}
}
}
?
Mixing這時就像Qml中一個普通的類型一樣使用了,點擊鼠標,調(diào)用Mixing中的start函數(shù),輸出“start”,改變窗體的顏色為藍色。
?
枚舉類型
如果想要在注冊的類中使用枚舉類型,可以使用Q_ENUMS 宏將該枚舉注冊到元對象系統(tǒng)中。修改mixing.h文件
class Mixing : public QObject
{
Q_OBJECT
Q_ENUMS(BALL_COLOR)
public:
explicit Mixing(QObject *parent = nullptr);
enum BALL_COLOR{
BALL_COLOR_YELLOW,
BALL_COLOR_BLUE,
BALL_COLOR_GREEN,
};
signals:
void colorChanged(const QColor & color);
public slots:
void start(BALL_COLOR ballColor);
};
我們注冊了枚舉類型之后,在Qml中就可以用 ${CLASS_NAME}.${ENUM_VALUE} 的形式來訪問了??匆幌聁ml文件中的內(nèi)容。主要修改的是MouseArea部分。
MouseArea{
anchors.fill: parent
acceptedButtons:Qt.LeftButton | Qt.RightButton;
onClicked: {
if(mouse.button === Qt.LeftButton)
{
mixing.start(Mixing.BALL_COLOR_BLUE)
}else if(mouse.button === Qt.RightButton){
mixing.start(Mixing.BALL_COLOR_GREEN)
}
}
onDoubleClicked: {
mixing.start(Mixing.BALL_COLOR_YELLOW)
}
}
從上面的代碼可以看出,調(diào)用枚舉類型的方法形如Mixing.BALL_COLOR_BLUE,注意前面是類名哦,不是設(shè)置的id。我們這次要實現(xiàn)的效果是點擊鼠標左鍵,窗口顏色變藍;點擊鼠標右鍵,窗口顏色變綠;雙擊鼠標,窗口顏色變黃。
然后將邏輯處理部分寫到mixing.cpp文件。
void Mixing::start(BALL_COLOR ballColor)
{
QColor color;
qDebug() << "start";
switch (ballColor) {
case BALL_COLOR_BLUE:
color = Qt::blue;
break;
case BALL_COLOR_GREEN:
color = Qt::green;
break;
case BALL_COLOR_YELLOW:
color = Qt::yellow;
break;
}
emit colorChanged(color);
}
用了一個switch選擇器來實現(xiàn)顏色的切換。效果如下:
?C++ 類的屬性和成員函數(shù)
????????在定義一個類的成員函數(shù)時使用Q_INVOKABLE宏來修飾,但是注意的是在QML中訪問的前提是public或protected成員函數(shù),而且這個宏必須放在返回函數(shù)前面。
而定義屬性則需要使用Q_PROPERTY?宏,通過它定義的屬性,可以在 QML 中訪問、修改,也可以在屬性變化時發(fā)射特定的信號。要想使用 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])
屬性的type、name是必需的,其它是可選項,最常用的有READ、WRITE、NOTIFY。屬性的type可以是QVariant支持的任何類型,也可以是自定義類型,包括自定義類、列表類型、組屬性等。另外,屬性的READ、WRITE、RESET是可以被繼承的,也可以是虛函數(shù),這些特性并不常用。
- READ:讀取屬性值,如果沒有設(shè)置MEMBER的話,它是必需的。一般情況下,函數(shù)是個const函數(shù),返回值類型必須是屬性本身的類型或這個類型的const引用,沒有參數(shù)。
- WRITE:設(shè)置屬性值,可選項。函數(shù)必須返回void,有且僅有一個參數(shù),參數(shù)類型必須是屬性本身的類型或這個類型的指針或引用。
- NOTIFY:與屬性關(guān)聯(lián)的可選信號。這個信號必須在類中聲明過,當屬性值改變時,就可觸發(fā)這個信號,可以沒有參數(shù),有參數(shù)的話只能是一個類型同屬性本身類型的參數(shù),用來記錄屬性改變后的值。
我們從代碼中看一下如何定義,修改mixing.h文件
class Mixing : public QObject
{
Q_OBJECT
Q_ENUMS(BALL_COLOR)
Q_PROPERTY(unsigned int number READ Number WRITE setNumber NOTIFY Numberchanged)
public:
explicit Mixing(QObject *parent = nullptr);
enum BALL_COLOR{
BALL_COLOR_YELLOW,
BALL_COLOR_BLUE,
BALL_COLOR_GREEN,
};
unsigned int Number() const;
void setNumber(const unsigned int &Number);
Q_INVOKABLE void stop();
signals:
void colorChanged(const QColor & color);
void Numberchanged();
public slots:
void start(BALL_COLOR ballColor);
private:
unsigned int m_Number;
}
可以看到我們通過Q_INVOKABLE修飾了stop函數(shù)。通過Q_PROPERTY修飾了名為number的屬性,number通過Number函數(shù)讀得數(shù)據(jù),通過setNumber函數(shù)寫入數(shù)據(jù),觸發(fā)信號是Numberchanged函數(shù)。在cpp文件中寫這幾個函數(shù)的內(nèi)容。
unsigned int Mixing::Number() const { return m_Number; } void Mixing::setNumber(const unsigned int &number) { if(number != m_Number) { m_Number = number; emit Numberchanged(); } } void Mixing::stop() { qDebug() << "顏色改變啦?。。?; }
接下來我們就可以在Qml中調(diào)用函數(shù)和屬性了,打開界面什么都不做時,會輸出number的初始值,因為沒有為其初始化,所以大家返回的數(shù)據(jù)可能不一定為0。雙擊時,會設(shè)置number的值,這里我們設(shè)置的是10,主要就是通過setNumber來寫數(shù)的。當number的值改變,會觸發(fā)Numberchanged信號,發(fā)射出去。所以還需要在Qml中寫一個信號處理函數(shù),也是輸出number的值,不過現(xiàn)在輸出的就是改變之后的number了。
MouseArea{
anchors.fill: parent
acceptedButtons:Qt.LeftButton | Qt.RightButton;
onClicked: {
if(mouse.button === Qt.LeftButton)
{
mixing.start(Mixing.BALL_COLOR_BLUE)
}else if(mouse.button === Qt.RightButton){
mixing.start(Mixing.BALL_COLOR_GREEN)
}
}
onDoubleClicked: {
mixing.start(Mixing.BALL_COLOR_YELLOW)
mixing.number = 10;
}
}
Mixing{
id: mixing
onColorChanged: {
root.color = color
mixing.stop(color)
}
Component.onCompleted:
{
console.log("default ball number is", number)
}
onNumberChanged:
{
console.log("new ball number is", number)
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-657721.html
?
到了這里,關(guān)于QT Quick之quick與C++混合編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!