Qt Quick 技術的引入,使得你可以快速構建 UI ,具備動畫、各類絢麗效果的 UI 都不在話下。但它不是萬能的,也有不少侷限性,原來 Qt 的一些技術,好比低階的網絡編程如 QTcpSocket ,多線程,又如 XML 文檔處理類庫 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要麼不可用,要麼用起來不方便,因此呢,不少時候咱們是會基於這樣的原則來混合使用 QML 和 C++: QML 構建界面, C++ 實現非界面的業務邏輯和複雜運算。算法
那這篇呢,咱們就來看看 QML 和 C++ 之間如何交互。編程
其實話說回來, QML 的不少基本類型本來就是在 C++ 中實現的,好比 Item 對應 QQuickItem , Image 對應 QQuickImage , Text 對應 QQuickText ,……這樣看來,在 QML 中訪問 C++ 對象必然不成問題。然也!反過來,在 C++ 中其實也可使用 QML 對象。網絡
對於這兩種情景,咱們分別講述。先看如何在 QML 中使用 C++ 類和對象。多線程
首先咱們須要建立一個 Qt Quick App ,請參考《Qt Quick 之 Hello World 圖文詳解》創建一個名爲 colorMaker 的項目,接下來咱們的學習將會伴隨 colorMaker 項目進行,等咱們講完,一個完整的 colorMaker 項目也會完成。須要新建兩個文件, colorMaker.h 和 colorMaker.cpp 。app
colorMaker 只是一個示例項目,我在 C++ 中實現一個 ColorMaker 類,它能夠被註冊爲一個 QML 類型供 QML 像內建類型同樣使用,它的實例也能夠導出爲 QML 上下文屬性在 QML 中訪問。咱們的示例只是在界面頂部顯示當前時間(時間文字的顏色隨時間變化而變化),在界面中間顯示一個變色矩形,在界面底部放置幾個按鈕來控制顏色如何變化。dom
圖 1 是效果圖:ide
圖 1 colorMaker 效果圖函數
在 QML 中使用 C++ 類和對象咱們知道, QML 實際上是對 JavaScript 的擴展,融合了 Qt Object 系統,它是一種新的解釋型的語言, QML 引擎雖然由 Qt C++ 實現,但 QML 對象的運行環境,說到底和 C++ 對象的上下文環境是不一樣的,是平行的兩個世界。若是你想在 QML 中訪問 C++ 對象,那麼必然要找到一種途徑來在兩個運行環境之間創建溝通橋樑。佈局
Qt 提供了兩種在 QML 環境中使用 C++ 對象的方式:學習
無論哪一種方式,對要導出的 C++ 類都有要求,不是一個類的全部方法、變量均可以被 QML 使用,所以咱們先來看看怎樣讓一個方法或屬性能夠被 QML 使用。
1 class ColorMaker : public QObject 2 { 3 Q_OBJECT 4 5 public: 6 ColorMaker(QObject *parent = 0); 7 ~ColorMaker(); 8 9 signals: 10 void colorChanged(const QColor & color); 11 void currentTime(const QString &strTime); 12 13 public slots: 14 void start(); 15 void stop(); 16 17 };
1 class ColorMaker : public QObject 2 { 3 Q_OBJECT 4 5 public: 6 ColorMaker(QObject *parent = 0); 7 ~ColorMaker(); 8 9 Q_INVOKABLE GenerateAlgorithm algorithm() const; 10 Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm); 11 12 signals: 13 void colorChanged(const QColor & color); 14 void currentTime(const QString &strTime); 15 16 public slots: 17 void start(); 18 void stop(); 19 };
一旦你使用 Q_INVOKABLE 將某個方法註冊到元對象系統中,在 QML 中就能夠用 ${Object}.${method} 來訪問,colorMaker 的 main.qml 中有使用 algorithm() 和 setAlgorithm() 的 QML 代碼 :
1 Component.onCompleted: { 2 colorMaker.color = Qt.rgba(0,180,120, 255); 3 colorMaker.setAlgorithm(ColorMaker.LinearIncrease); 4 changeAlgorithm(colorAlgorithm, colorMaker.algorithm()); 5 }
1 class ColorMaker : public QObject 2 { 3 Q_OBJECT 4 Q_ENUMS(GenerateAlgorithm) 5 6 public: 7 ColorMaker(QObject *parent = 0); 8 ~ColorMaker(); 9 10 enum GenerateAlgorithm{ 11 RandomRGB, 12 RandomRed, 13 RandomGreen, 14 RandomBlue, 15 LinearIncrease 16 }; 17 18 Q_INVOKABLE GenerateAlgorithm algorithm() const; 19 Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm); 20 21 signals: 22 void colorChanged(const QColor & color); 23 void currentTime(const QString &strTime); 24 25 public slots: 26 void start(); 27 void stop(); 28 };
1 Q_PROPERTY(type name 2 (READ getFunction [WRITE setFunction] | 3 MEMBER memberName [(READ getFunction | WRITE setFunction)]) 4 [RESET resetFunction] 5 [NOTIFY notifySignal] 6 [REVISION int] 7 [DESIGNABLE bool] 8 [SCRIPTABLE bool] 9 [STORED bool] 10 [USER bool] 11 [CONSTANT] 12 [FINAL])
Q_PROPERTY(int x READ x)
1 class QQuickText : public QQuickImplicitSizeItem 2 { 3 Q_OBJECT 4 Q_ENUMS(HAlignment) 5 6 7 Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) 8 Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged) 9 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) 10 ... 11 12 public: 13 enum HAlignment { AlignLeft = Qt::AlignLeft, 14 AlignRight = Qt::AlignRight, 15 AlignHCenter = Qt::AlignHCenter, 16 AlignJustify = Qt::AlignJustify }; 17 ... 18 QString text() const; 19 void setText(const QString &); 20 21 QFont font() const; 22 void setFont(const QFont &font); 23 24 QColor color() const; 25 void setColor(const QColor &c); 26 ... 27 };
如今給咱們的 ColorMaker 類添加一些屬性,以便 QML 能夠獲取、設置顏色值。新的 ColorMaker 類以下:
1 class ColorMaker : public QObject 2 { 3 Q_OBJECT 4 Q_ENUMS(GenerateAlgorithm) 5 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) 6 Q_PROPERTY(QColor timeColor READ timeColor) 7 8 public: 9 ColorMaker(QObject *parent = 0); 10 ~ColorMaker(); 11 12 enum GenerateAlgorithm{ 13 RandomRGB, 14 RandomRed, 15 RandomGreen, 16 RandomBlue, 17 LinearIncrease 18 }; 19 20 QColor color() const; 21 void setColor(const QColor & color); 22 QColor timeColor() const; 23 24 Q_INVOKABLE GenerateAlgorithm algorithm() const; 25 Q_INVOKABLE void setAlgorithm(GenerateAlgorithm algorithm); 26 27 signals: 28 void colorChanged(const QColor & color); 29 void currentTime(const QString &strTime); 30 31 public slots: 32 void start(); 33 void stop(); 34 35 protected: 36 void timerEvent(QTimerEvent *e); 37 38 private: 39 GenerateAlgorithm m_algorithm; 40 QColor m_currentColor; 41 int m_nColorTimer; 42 };
1 #include "colorMaker.h" 2 #include <QTimerEvent> 3 #include <QDateTime> 4 5 ColorMaker::ColorMaker(QObject *parent) 6 : QObject(parent) 7 , m_algorithm(RandomRGB) 8 , m_currentColor(Qt::black) 9 , m_nColorTimer(0) 10 { 11 qsrand(QDateTime::currentDateTime().toTime_t()); 12 } 13 14 ColorMaker::~ColorMaker() 15 { 16 } 17 18 QColor ColorMaker::color() const 19 { 20 return m_currentColor; 21 } 22 23 void ColorMaker::setColor(const QColor &color) 24 { 25 m_currentColor = color; 26 emit colorChanged(m_currentColor); 27 } 28 29 QColor ColorMaker::timeColor() const 30 { 31 QTime time = QTime::currentTime(); 32 int r = time.hour(); 33 int g = time.minute()*2; 34 int b = time.second()*4; 35 return QColor::fromRgb(r, g, b); 36 } 37 38 ColorMaker::GenerateAlgorithm ColorMaker::algorithm() const 39 { 40 return m_algorithm; 41 } 42 43 void ColorMaker::setAlgorithm(GenerateAlgorithm algorithm) 44 { 45 m_algorithm = algorithm; 46 } 47 48 void ColorMaker::start() 49 { 50 if(m_nColorTimer == 0) 51 { 52 m_nColorTimer = startTimer(1000); 53 } 54 } 55 56 void ColorMaker::stop() 57 { 58 if(m_nColorTimer > 0) 59 { 60 killTimer(m_nColorTimer); 61 m_nColorTimer = 0; 62 } 63 } 64 65 void ColorMaker::timerEvent(QTimerEvent *e) 66 { 67 if(e->timerId() == m_nColorTimer) 68 { 69 switch(m_algorithm) 70 { 71 case RandomRGB: 72 m_currentColor.setRgb(qrand() % 255, qrand() % 255, qrand() % 255); 73 break; 74 case RandomRed: 75 m_currentColor.setRed(qrand() % 255); 76 break; 77 case RandomGreen: 78 m_currentColor.setGreen(qrand() % 255); 79 break; 80 case RandomBlue: 81 m_currentColor.setBlue(qrand() % 255); 82 break; 83 case LinearIncrease: 84 { 85 int r = m_currentColor.red() + 10; 86 int g = m_currentColor.green() + 10; 87 int b = m_currentColor.blue() + 10; 88 m_currentColor.setRgb(r % 255, g % 255, b % 255); 89 } 90 break; 91 } 92 emit colorChanged(m_currentColor); 93 emit currentTime(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")); 94 } 95 else 96 { 97 QObject::timerEvent(e); 98 } 99 }
1 template<typename T> 2 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName); 3 4 template<typename T, int metaObjectRevision> 5 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
1 #include <QtGui/QGuiApplication> 2 #include "qtquick2applicationviewer.h" 3 #include <QtQml> 4 #include "colorMaker.h" 5 6 7 int main(int argc, char *argv[]) 8 { 9 QGuiApplication app(argc, argv); 10 qmlRegisterType<ColorMaker>("an.qt.ColorMaker", 1, 0, "ColorMaker"); 11 12 QtQuick2ApplicationViewer viewer; 13 viewer.setMainQmlFile(QStringLiteral("qml/colorMaker/main.qml")); 14 viewer.showExpanded(); 15 16 return app.exec(); 17 }
import an.qt.ColorMaker 1.0
1 Rectangle { 2 width: 360; 3 height: 360; 4 5 ColorMaker { 6 id: colorMaker; 7 color: Qt.green; 8 } 9 }
1 import QtQuick 2.0 2 import QtQuick.Controls 1.1 3 import an.qt.ColorMaker 1.0 4 5 Rectangle { 6 width: 360; 7 height: 360; 8 Text { 9 id: timeLabel; 10 anchors.left: parent.left; 11 anchors.leftMargin: 4; 12 anchors.top: parent.top; 13 anchors.topMargin: 4; 14 font.pixelSize: 26; 15 } 16 ColorMaker { 17 id: colorMaker; 18 color: Qt.green; 19 } 20 21 Rectangle { 22 id: colorRect; 23 anchors.centerIn: parent; 24 width: 200; 25 height: 200; 26 color: "blue"; 27 } 28 29 Button { 30 id: start; 31 text: "start"; 32 anchors.left: parent.left; 33 anchors.leftMargin: 4; 34 anchors.bottom: parent.bottom; 35 anchors.bottomMargin: 4; 36 onClicked: { 37 colorMaker.start(); 38 } 39 } 40 Button { 41 id: stop; 42 text: "stop"; 43 anchors.left: start.right; 44 anchors.leftMargin: 4; 45 anchors.bottom: start.bottom; 46 onClicked: { 47 colorMaker.stop(); 48 } 49 } 50 51 function changeAlgorithm(button, algorithm){ 52 switch(algorithm) 53 { 54 case 0: 55 button.text = "RandomRGB"; 56 break; 57 case 1: 58 button.text = "RandomRed"; 59 break; 60 case 2: 61 button.text = "RandomGreen"; 62 break; 63 case 3: 64 button.text = "RandomBlue"; 65 break; 66 case 4: 67 button.text = "LinearIncrease"; 68 break; 69 } 70 } 71 72 Button { 73 id: colorAlgorithm; 74 text: "RandomRGB"; 75 anchors.left: stop.right; 76 anchors.leftMargin: 4; 77 anchors.bottom: start.bottom; 78 onClicked: { 79 var algorithm = (colorMaker.algorithm() + 1) % 5; 80 changeAlgorithm(colorAlgorithm, algorithm); 81 colorMaker.setAlgorithm(algorithm); 82 } 83 } 84 85 Button { 86 id: quit; 87 text: "quit"; 88 anchors.left: colorAlgorithm.right; 89 anchors.leftMargin: 4; 90 anchors.bottom: start.bottom; 91 onClicked: { 92 Qt.quit(); 93 } 94 } 95 96 Component.onCompleted: { 97 colorMaker.color = Qt.rgba(0,180,120, 255); 98 colorMaker.setAlgorithm(ColorMaker.LinearIncrease); 99 changeAlgorithm(colorAlgorithm, colorMaker.algorithm()); 100 } 101 102 Connections { 103 target: colorMaker; 104 onCurrentTime:{ 105 timeLabel.text = strTime; 106 timeLabel.color = colorMaker.timeColor; 107 } 108 } 109 110 Connections { 111 target: colorMaker; 112 onColorChanged:{ 113 colorRect.color = color; 114 } 115 } 116 }
main.qml 的界面分紅了三部分,參看圖 1 。頂部是一個 Text ,用來顯示由 ColorMaker 提供的時間,我使用 Connections 對象,指定 target 爲 colorMaker ,在 onCurrentTime 信號處理器中改變 timeLabel 的文本和顏色。這裏使用 ColorMaker 的 timeColor 屬性,該屬性的讀取函數是 timeColor ,回看一下 colorMaker.cpp 中的實現:
1 QColor ColorMaker::timeColor() const 2 { 3 QTime time = QTime::currentTime(); 4 int r = time.hour(); 5 int g = time.minute()*2; 6 int b = time.second()*4; 7 return QColor::fromRgb(r, g, b); 8 }
1 #include <QtGui/QGuiApplication> 2 #include "qtquick2applicationviewer.h" 3 #include <QtQml> 4 #include "colorMaker.h" 5 6 7 int main(int argc, char *argv[]) 8 { 9 QGuiApplication app(argc, argv); 10 11 QtQuick2ApplicationViewer viewer; 12 13 viewer.rootContext()->setContextProperty("colorMaker", new ColorMaker); 14 15 viewer.setMainQmlFile(QStringLiteral("qml/colorMaker/main.qml")); 16 viewer.showExpanded(); 17 18 return app.exec(); 19 }
注意我在 viewer 變量後增長的新語句:
viewer.rootContext()->setContextProperty("colorMaker", new ColorMaker);
ReferenceError: ColorMaker is not defined
1 import QtQuick 2.0 2 import QtQuick.Controls 1.1 3 //[1] 4 //import an.qt.ColorMaker 1.0 5 6 Rectangle { 7 width: 360; 8 height: 360; 9 Text { 10 id: timeLabel; 11 anchors.left: parent.left; 12 anchors.leftMargin: 4; 13 anchors.top: parent.top; 14 anchors.topMargin: 4; 15 font.pixelSize: 26; 16 } 17 /* [2] 18 ColorMaker { 19 id: colorMaker; 20 color: Qt.green; 21 } 22 */ 23 24 Rectangle { 25 id: colorRect; 26 anchors.centerIn: parent; 27 width: 200; 28 height: 200; 29 color: "blue"; 30 } 31 32 Button { 33 id: start; 34 text: "start"; 35 anchors.left: parent.left; 36 anchors.leftMargin: 4; 37 anchors.bottom: parent.bottom; 38 anchors.bottomMargin: 4; 39 onClicked: { 40 colorMaker.start(); 41 } 42 } 43 Button { 44 id: stop; 45 text: "stop"; 46 anchors.left: start.right; 47 anchors.leftMargin: 4; 48 anchors.bottom: start.bottom; 49 onClicked: { 50 colorMaker.stop(); 51 } 52 } 53 54 function changeAlgorithm(button, algorithm){ 55 switch(algorithm) 56 { 57 case 0: 58 button.text = "RandomRGB"; 59 break; 60 case 1: 61 button.text = "RandomRed"; 62 break; 63 case 2: 64 button.text = "RandomGreen"; 65 break; 66 case 3: 67 button.text = "RandomBlue"; 68 break; 69 case 4: 70 button.text = "LinearIncrease"; 71 break; 72 } 73 } 74 75 Button { 76 id: colorAlgorithm; 77 text: "RandomRGB"; 78 anchors.left: stop.right; 79 anchors.leftMargin: 4; 80 anchors.bottom: start.bottom; 81 onClicked: { 82 var algorithm = (colorMaker.algorithm() + 1) % 5; 83 changeAlgorithm(colorAlgorithm, algorithm); 84 colorMaker.setAlgorithm(algorithm); 85 } 86 } 87 88 Button { 89 id: quit; 90 text: "quit"; 91 anchors.left: colorAlgorithm.right; 92 anchors.leftMargin: 4; 93 anchors.bottom: start.bottom; 94 onClicked: { 95 Qt.quit(); 96 } 97 } 98 99 Component.onCompleted: { 100 colorMaker.color = Qt.rgba(0,180,120, 255); 101 //[3] 102 //colorMaker.setAlgorithm(ColorMaker.LinearIncrease); 103 colorMaker.setAlgorithm(2); 104 changeAlgorithm(colorAlgorithm, colorMaker.algorithm()); 105 } 106 107 Connections { 108 target: colorMaker; 109 onCurrentTime:{ 110 timeLabel.text = strTime; 111 timeLabel.color = colorMaker.timeColor; 112 } 113 } 114 115 Connections { 116 target: colorMaker; 117 onColorChanged:{ 118 colorRect.color = color; 119 } 120 } 121 }
1 T QObject::findChild(const QString & name = QString(),\ 2 Qt::FindChildOptions options = \ 3 Qt::FindChildrenRecursively) const; 4 QList<T> QObject::findChildren(const QString & name = \ 5 QString(), Qt::FindChildOptions options = \ 6 Qt::FindChildrenRecursively) const; 7 QList<T> QObject::findChildren(const QRegExp & regExp, \ 8 Qt::FindChildOptions options = \ 9 Qt::FindChildrenRecursively) const; 10 QList<T> QObject::findChildren(const QRegularExpression & re,\ 11 Qt::FindChildOptions options = \ 12 Qt::FindChildrenRecursively) const;
QPushButton *button = parentWidget->findChild<QPushButton *>("button1");
QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>("widgetname");
bool QMetaObject::invokeMethod(QObject * obj, const char * member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument( 0 ), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) [static]
QGenericArgument Q_ARG( Type, const Type & value)
QGenericReturnArgument Q_RETURN_ARG( Type, Type & value)
1 QString retVal; 2 QMetaObject::invokeMethod(obj, "compute", Qt::DirectConnection, 3 Q_RETURN_ARG(QString, retVal), 4 Q_ARG(QString, "sqrt"), 5 Q_ARG(int, 42), 6 Q_ARG(double, 9.7));
若是你要讓一個線程對象退出,能夠這麼調用(隊列鏈接方式):
1 QMetaObject::invokeMethod(thread, "quit", 2 Qt::QueuedConnection);
1 import QtQuick 2.0 2 import QtQuick.Controls 1.1 3 4 Rectangle { 5 objectName: "rootRect"; 6 width: 360; 7 height: 360; 8 Text { 9 objectName: "textLabel"; 10 text: "Hello World"; 11 anchors.centerIn: parent; 12 font.pixelSize: 26; 13 } 14 15 Button { 16 anchors.right: parent.right; 17 anchors.rightMargin: 4; 18 anchors.bottom: parent.bottom; 19 anchors.bottomMargin: 4; 20 text: "quit"; 21 objectName: "quitButton"; 22 } 23 }
1 #include <QtGui/QGuiApplication> 2 #include "qtquick2applicationviewer.h" 3 #include <QQuickItem> 4 #include "changeColor.h" 5 #include <QMetaObject> 6 #include <QDebug> 7 #include <QColor> 8 #include <QVariant> 9 10 int main(int argc, char *argv[]) 11 { 12 QGuiApplication app(argc, argv); 13 14 QtQuick2ApplicationViewer viewer; 15 viewer.setMainQmlFile(QStringLiteral("qml/callQml/main.qml")); 16 viewer.showExpanded(); 17 18 QQuickItem * rootItem = viewer.rootObject(); 19 new ChangeQmlColor(rootItem); 20 QObject * quitButton = rootItem->findChild<QObject*>("quitButton"); 21 if(quitButton) 22 { 23 QObject::connect(quitButton, SIGNAL(clicked()), &app, SLOT(quit())); 24 } 25 26 QObject *textLabel = rootItem->findChild<QObject*>("textLabel"); 27 if(textLabel) 28 { 29 //1. failed call 30 bool bRet = QMetaObject::invokeMethod(textLabel, "setText", Q_ARG(QString, "world hello")); 31 qDebug() << "call setText return - " << bRet; 32 textLabel->setProperty("color", QColor::fromRgb(255,0,0)); 33 bRet = QMetaObject::invokeMethod(textLabel, "doLayout"); 34 qDebug() << "call doLayout return - " << bRet; 35 } 36 37 return app.exec(); 38 }
1 Starting D:\projects\...\release\callQml.exe... 2 QMetaObject::invokeMethod: No such method QQuickText::setText(QString) 3 call setText return - false 4 call doLayout return - true
new ChangeQmlColor(rootItem);
1 #ifndef CHANGECOLOR_H 2 #define CHANGECOLOR_H 3 #include <QObject> 4 #include <QTimer> 5 6 class ChangeQmlColor : public QObject 7 { 8 Q_OBJECT 9 public: 10 ChangeQmlColor(QObject *target, QObject *parent = 0); 11 ~ChangeQmlColor(); 12 13 protected slots: 14 void onTimeout(); 15 16 private: 17 QTimer m_timer; 18 QObject *m_target; 19 }; 20 21 #endif
實現文件 changeColor.cpp :
1 #include "changeColor.h" 2 #include <QDateTime> 3 #include <QColor> 4 #include <QVariant> 5 6 ChangeQmlColor::ChangeQmlColor(QObject *target, QObject *parent) 7 : QObject(parent) 8 , m_timer(this) 9 , m_target(target) 10 { 11 qsrand(QDateTime::currentDateTime().toTime_t()); 12 connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeout())); 13 m_timer.start(1000); 14 } 15 16 ChangeQmlColor::~ChangeQmlColor() 17 {} 18 19 void ChangeQmlColor::onTimeout() 20 { 21 QColor color = QColor::fromRgb(qrand()%256, qrand()%256, qrand()%256); 22 m_target->setProperty("color", color); 23 }
很簡單,不說啦。