29.qt quick-在QML中調用C++類
- 2021 年 6 月 18 日
- 筆記
- 4.5 Qt Quick
- 初學者小建議: 不妨先點贊後再學習, 進群免費拿demo,參考本文章一起學習, 效果更佳o~~~
1.Qml調用C++類
Qt QML模塊提供了一組API,用來將C++類擴展QML中。您可以編寫擴展來添加自己的QML類型,擴展現有的Qt類型,或調用無法從普通QML代碼訪問的C/C++函數
本章將學習如何使用C++類編寫QML擴展,其中包括屬性、QML function和屬性綁定等
為了方便大家理解,本章示例的函數實現能寫在頭文件,就寫在頭文件.
2.創建QML
將C++類擴展QML時,一般用來實現QML目前無法實現的功能,比如訪問系統信息,文件信息等。
本章demo是顯示一個簡單的餅圖,創建一個C++類提供給QML使用
這裡導入一個“import Charts 1.0”模塊,然後創建一個名為“PieChart”的QML類型,該類型具有兩個屬性:name和color。
import QtQuick.Window 2.12 import Charts 1.0 Window { visible: true width: 640 height: 480 PieChart { width: 100; height: 100 name: "A simple pie chart" color: "red" } }
要做到這一點,我們需要一個C++類,它封裝了這個PieChart類型及其name和color兩個屬性。
3.創建C++類
由於QML大量使用了Qt的元對象系統,因此該類必須是:
- 繼承於QObject的派生類
- 並且有Q_OBJECT宏
以下是我們的餅圖PieChart類,在piechart.h中定義:
#include <QtQuick/QQuickPaintedItem> #include <QColor> #include <QPen> #include <QPainter> #include <QDebug> #include <QTimer> class PieChart : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) Q_PROPERTY(QColor color READ color WRITE setColor) public: PieChart(QQuickItem *parent = 0); QString name() const { return m_name; } void setName(const QString &name) { m_name = name; } QColor color() const { return m_color; } void setColor(const QColor &color) { m_color = color; } void paint(QPainter *painter); private: QString m_name; QColor m_color; public slots: void onTimeout(); };
請注意,儘管color在QML中指定為字符串.比如“#00FF00”,但它會自動轉換為QColor對象,像「640×480」這樣的字符串可以自動轉換為QSize值。
- 該類繼承自QQuickPaintedItem,因為我們希望在使用QPainter API執行繪圖操作時重寫QQuickPaintedItem::paint()。
- 如果類只是表示了某些數據類型,而不是實際需要顯示的內容,它可以簡單地從QObject繼承。
- 如果我們想創建一個不需要使用QPainter API執行繪圖操作的可視化項,我們可以只對QQuickItem子類進行子類。
Ps:
- QQuickItem: Qt Quick中的所有可視項都繼承自QQuickItem。雖然QQuickItem沒有視覺外觀,但它定義了視覺項目中常見的所有屬性,如x和y位置、寬度和高度、錨定和Key處理支持。
- QQuickPaintedItem:繼承自QQuickItem,並擴展了Qt中的QPainter API函數,使得QPainter將能夠直接繪製到QML場景的紋理上。調用update()時可以重新繪製。在paint()中使用setAntaliasing()時可以設置抗鋸齒渲染
PieChart類使用Q_PROPERTY宏定義了兩個屬性name和color,並重寫QQuickPaintedItem::paint()。
3.1 Q_PROPERTY介紹
Q_PROPERTY 宏定義屬性的一些主要關鍵字的意義如下:
- READ 指定一個讀取屬性值的函數,沒有 MEMBER 關鍵字時必須設置 READ。
- WRITE 指定一個設定屬性值的函數,只讀屬性沒有 WRITE 設置。
- MEMBER 指定一個成員變量與屬性關聯,成為可讀可寫的屬性,無需再設置 READ 和 WRITE。
- RESET 是可選的,用於指定一個設置屬性缺省值的函數。
- NOTIFY 是可選的,用於設置一個信號,當屬性值變化時發射此信號(在QML中經常用到,比如onXChanged)。
- DESIGNABLE 表示屬性是否在 Qt Designer 里可見,缺省為 true。
- CONSTANT 表示屬性值是一個常數,對於一個對象實例,READ 指定的函數返回值是常數,但是每個實例的返回值可以不一樣。具有 CONSTANT 關鍵字的屬性不能有 WRITE 和 NOTIFY 關鍵字。
- FINAL 表示所定義的屬性不能被子類重載。
在C++中屬性的使用
不管是否用 READ 和 WRITE 定義了接口函數,只要知道屬性名稱,就可以通過 QObject::property() 讀取屬性值,並通過 QObject::setProperty() 設置屬性值。
比如定義一個類:
class MyObj : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName) public: MyObj(QQuickItem *parent = 0) { } QString name() const { return m_name; } void setName(const QString &name) { qDebug()<<name; m_name = name; } // 添加了一個打印 private: QString m_name; // 用來保存name屬性的值 };
然後我們調用setProperty時:
MyObj ct; ct.setProperty("name","1234"); // 將會調用setName()接口函數,並且打印"1234"
在QML中屬性的使用(在”5.屬性綁定”會講解)
在QML中,屬性就更加常見了,比如Rectangle的color屬性,其實本質就是:
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) public: QColor color() const { return m_color; } void setColor(const QColor &color) { if (color == m_color) return; m_color = color; emit colorChanged(); } signals: void xChanged(const QString &name); private: QColor m_color;
假如在c++類中自己更改屬性時,並且該屬性設置了NOTIFY關鍵字,那麼必須更改後,主動emit來觸發屬性更改信號,比如:
m_color = QColor::QColor(255, 0, 0, 255); emit colorChanged();
3.2 piechart.cpp最終如下所示:
#include "piechart.h" PieChart::PieChart(QQuickItem *parent) : QQuickPaintedItem(parent) { } void PieChart::paint(QPainter *painter) { QPen pen(m_color, 2); painter->setPen(pen); painter->setRenderHints(QPainter::Antialiasing, true); painter->drawPie(boundingRect().adjusted(1, 1, -1, -1), 90 * 16, 290 * 16); }
4.在main.cpp中通過qmlRegisterXXX註冊C++類到QML中
我們已經創建好了C++類,剩下的就是註冊到QML中即可大功告成了.註冊函數是qmlRegisterType(),當然也可以通過qmlRegisterSingletonType()註冊單例類(後面章節介紹).
qmlRegisterType函數模版聲明如下:
template<typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName); // uri: 類似於java包名,比如"import QtQuick 2.12","QtQuick"就是包名,而2.12是versionMajor和versionMinor拼接的版本號 // qmlName: 包名中的類型名稱,比如Rectangle就是QtQuick包名中的其中一個類型名稱
main函數如下所示:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "piechart.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<PieChart>("Charts", 1, 0, "PieChart"); // 註冊C++類 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(); }
運行效果如下所示:
5.屬性綁定
屬性綁定是QML的一個強大功能,它允許自動同步不同類型的值。當屬性值更改時,它使用信號通知和更新其他類型的值。
我們來創建兩個PieChart圖,名稱分別為chartA和chartB,然後我們在chartB里進行color屬性綁定“color: chartA.color”.
修改main.qml:
import QtQuick 2.14 import QtQuick.Window 2.12 import Charts 1.0 Window { visible: true width: 640 height: 480 Row { PieChart { id: chartA width: 100; height: 100 name: "A simple pie chart" color: "red" } PieChart { id: chartB width: 100; height: 100 name: "A simple pie chart" color: chartA.color } } MouseArea { anchors.fill: parent onClicked: { chartA.color = "blue" } } }
修改piechart.h:
#include <QtQuick/QQuickPaintedItem> #include <QColor> #include <QPen> #include <QPainter> #include <QDebug> #include <QTimer> class PieChart : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) public: PieChart(QQuickItem *parent = 0); QString name() const { return m_name; } void setName(const QString &name) { if (name != m_name) { m_name = name; emit nameChanged(name); } } QColor color() const { return m_color; } void setColor(const QColor &color) { if (color != m_color) { m_color = color; emit colorChanged(color); update(); } } void paint(QPainter *painter); private: QString m_name; QColor m_color; signals: void nameChanged(const QString &name); void colorChanged(const QColor &color); public slots: void onTimeout(); };
這裡我們在Q_PROPERTY()中添加了一個NOTIFY功能:
- 屬性綁定主要是靠的是屬性中的NOTIFY功能,每當color值更改時,就會發出colorChanged信號。從而使得綁定的目標屬性自動更新值.
- 調用WRITE功能的函數時(比如setColor()),我們必須判斷要設置的值是否與當前屬性值相等,這樣確保信號不會必要地發出,從而導致可能死循環的事情發生.
當我們點擊應用後,由於立即設置chartA.color = “blue”,然後由於屬性綁定,所以chartB也跟着改變了.最終兩個chart都變成了藍色:
6.使用Q_INVOKABLE修飾函數提供給QML使用
比如在QML中,我們想使用C++類的clearChart()函數來清除繪圖時,那我們需要在C++類的clearChart()函數前面使用Q_INVOKABLE修飾來註冊到元對象中(本質就是signal和slots).這樣QML就可以像使用function那樣調用該函數了.
修改類頭文件,添加clearChart():
class PieChart : public QQuickPaintedItem { ... public: ... Q_INVOKABLE void clearChart(); };
在cpp文件中實現函數:
void PieChart::clearChart() { setColor(QColor(Qt::transparent)); update(); }
修改main.qml:
Window { visible: true width: 640 height: 480 Row { PieChart { id: chartA width: 100; height: 100 name: "A simple pie chart" color: "red" } PieChart { id: chartB width: 100; height: 100 name: "A simple pie chart" color: "blue" } } MouseArea { anchors.fill: parent onClicked: { chartA.clearChart();} } }
當我們點擊應用後,就會調用chartA.clearChart()方法,從而清除chartA:
7.信號、槽函數提供給QML使用
上一節我們講過使用Q_INVOKABLE修飾來註冊到元對象中其實本質就是signal和slots.這是因為:
所以不管用Q_INVOKABLE,還是signal和slots修飾,最終都會變成QT_ANNOTATE_FUNCTION(…)
修改piechart.h,添加一個clearChart2槽函數:
class PieChart : public QQuickPaintedItem { ... public slots: void clearChart2() { setColor(QColor(Qt::transparent)); update(); } ... };
修改main.qml:
MouseArea {
anchors.fill: parent
onClicked: { chartA.clearChart2();}
}
發現,最終效果和調用chartA.clearChart()的效果一樣.
8.使用Q_ENUM()將枚舉提供給QML使用
在C++類中,我們可以通過Q_ENUM()將C++類的枚舉類型註冊到元對象中,修改頭文件如下所示:
class PieChart : public QQuickPaintedItem { ... public : enum Priority { High, Low, VeryHigh, VeryLow }; Q_ENUM(Priority) Q_INVOKABLE Priority setPriority(Priority value) { qDebug()<<value; } ... };
在main.qml中添加:
Component.onCompleted: {
chartA.setPriority(chartA.VeryHigh);
}
運行效果如下所示:
需要注意的是: 在qml中,我們只能使用這些枚舉,假如是打印這些枚舉變量,值將會是0.
未完待續,下章學習如何向QML中註冊單例類