QML與指標

來源:互聯網
上載者:User

                 在QML剛剛出來不久,參加nokia的一個Qt Quick培訓的時候,QML就給我的印象是:解釋性指令碼語言,沒有記憶體操作的說法,更不用說指標了。當時也就是想想,也沒有具體去實踐探討。由於現在在用Qt做產品,UI方面不得不跟QML打交道。QML做UI可以說是又好又快,大大節省了開發週期,但是由於QML處理邏輯的能力較差,所以,對於大量的邏輯處理還是需要Qt C++支援。這就涉及到C++與QML解釋性語言之間資料交換。

                QML在和C++相互嵌入啟動並執行時候,就需要QML的engine: QDeclarativeEngine。通常我們不會直接與這個engine打交道。我們通常的做法是這樣的:

#include <QtGui/QApplication>#include "qmlapplicationviewer.h"#include <qdeclarative.h>#include <QDeclarativeView>#include <QDeclarativeContext>int main(int argc, char *argv[]){    QApplication app(argc, argv);    MyClass myObj;    QDeclarativeView view;    QDeclarativeContext *ctxt = view.rootContext();    ctxt->setContextProperty("obj", &myObj);    view.setSource(QUrl("main.qml"));    return app.exec();}

View已經實現了對engine的封裝。如果我們需要在QML中調用C++對象的話,只需要使用setContextProperty().

View和Context提供了QML運行環境。setContextProperty相當於把C++對象在這個Context中註冊,QML中只需要使用obj這個別名就可以訪問到註冊的對象myObj,這種訪問包括調用成員函數,當然,成員函數要使用Q_INVOKABLE修飾之後就可以調用了。看到這裡,似乎還沒看到QML與指標的任何關係。通常情況下,遇到QML需要和C++對象相互嵌入調用的時候,使用setContextProperty,將事先構造好的對象在QML運行環境中進行註冊,這樣QML就可以調用C++方法了。

並不是所有的對象都可以事先構造好,例如:QML  UI上有一個按鈕,每點擊這個按鈕一次就需要構造一個C++對象進行相應的操作,如果點擊100下,那豈不是事先要構造好100個對象?在這種情況下,動態建立C++對象無疑是最好的方法。如果對於純C++,當然簡單,new一個對象,並把對象的指標返回就可以對其進行操作了。當時這裡是QML,就沒那麼簡單了。i

對於這種需要在QML中動態建立C++對象,並需要對該對象進行操作,我們沒有辦法每次都調用setContextProperty("obj", &myObj)。因為setContextProperty的第一個參數實際上是在QML中的變數名,QML中就是通過第一個參數找到該對象的。所以該字元創必須唯一。第一次跟我同事討論方案的時候,我們想,不就是字串唯一嗎,我直接new一個對象,然後將對象的地址轉換成字串,然後調用setContextProperty,並將字串返回到QML中。這樣,QML豈不就是可以自由的操作該對象了。這個方法呢,能滿足要求,但是感覺很不專業。要是能操作指標就好了。
最後的方案是通過已經註冊的對象,調用  obj.createObject(),返回對象的指標到QML中去。QML通過擷取的指標做自己操作。由於QML中無法直接定義指標,所以使用variant這個萬能的類型定義變數。

先看代碼:

//由於只有一個inline函數,所以只貼出來了標頭檔,cpp檔案實際上什麼也沒做

//operation.h#ifndef OPERATION_H#define OPERATION_H#include <QObject>#include "target.h"class Operation : public QObject{    Q_OBJECTpublic:    explicit Operation(QObject *parent = 0);    Q_INVOKABLE inline Target* createObject() { return new Target; }//這裡我們就是為了返回指標給QMLsignals:public slots:};#endif // OPERATION_H

//target.h#ifndef TARGET_H#define TARGET_H#include <QObject>#include <QDebug>class Target : public QObject{    Q_OBJECTpublic:    explicit Target(QObject *parent = 0);    Q_INVOKABLE inline void getAction() { qDebug() << "Hi, I am Harlen"; }signals:public slots:};#endif // TARGET_H

//main.cpp#include <QtGui/QApplication>#include "qmlapplicationviewer.h"#include <qdeclarative.h>#include <QDeclarativeView>#include <QDeclarativeContext>#include "operation.h"#include "target.h"int main(int argc, char *argv[]){    QApplication app(argc, argv);    Operation myObj;    QDeclarativeView view;    QDeclarativeContext *ctxt = view.rootContext();    ctxt->setContextProperty("obj", &myObj);    view.setSource(QUrl("qml/QmlPointer/main.qml"));    view.show();    return app.exec();}

import QtQuick 1.0Rectangle {    width: 360    height: 360    property variant pointer    color: "skyblue"    Text {        text: "Harlen Tan"        font.bold: true        font.pointSize: 20        anchors.centerIn: parent    }    MouseArea {        anchors.fill: parent        onClicked: {           pointer = obj.createObject()            pointer.getAction()        }    }}

注意上面的注釋出createObject的簽名:

Target* Operation::createObject();,由於obj已經向QML註冊過了,所以在QML中直接使用obj.createObject()是絕對可行的。

那麼,只要createObject()一調用,QML拿到的絕對就是指標了,然後QML中使用指標操作,就遊刃有餘了。

調試運行,點擊對話方塊,發現有提示訊息: main.qml:19:TypeError: Result of expression 'pointer' [undefined] is not an object.

查看main.qml 的19行:pointer.getAction(),意思是說pointer不是個對象。但是QML中也沒有pointer->或者 *pointer這個文法。

怎麼辦?計量都用完了。只有回去去翻看SDK document. 沒有絕望,因為在我心裡總有那麼一點念頭這問題可以解決,只是還沒理解透徹。於是看到了協助文檔這麼一段話,讓我豁然開朗:

Any C++ data that is used from QML - whether as custom properties, or parameters for signals or functions - must be of a type that is recognizable by QML.

By default, QML recognizes the following data types:

bool
unsigned int, int
float, double, qreal
QString
QUrl
QColor
QDate, QTime, QDateTime
QPoint, QPointF
QSize, QSizeF
QRect, QRectF
QVariant
QVariantList, QVariantMap
QObject*
Enumerations declared with Q_ENUMS()
To allow a custom C++ type to be created or used in QML, the C++ class must be registered as a QML type using qmlRegisterType(), as shown in the Defining new QML elements section above.

我們並沒有向QML註冊我們的指標類型,上面紅色標出來的地方時:QObject*是可以識別的,那是不是意味著我們註冊下 Target就可以了呢?

於是修改main.cpp

#include <QtGui/QApplication>#include "qmlapplicationviewer.h"#include <qdeclarative.h>#include <QDeclarativeView>#include <QDeclarativeContext>#include "operation.h"#include "target.h"int main(int argc, char *argv[]){    QApplication app(argc, argv);    Operation myObj;    QDeclarativeView view;    QDeclarativeContext *ctxt = view.rootContext();    qmlRegisterType<Target>();//只是添加了這麼一句話,向QML環境註冊自訂類型    ctxt->setContextProperty("obj", &myObj);    view.setSource(QUrl("qml/QmlPointer/main.qml"));    view.show();    return app.exec();}

點擊運行,並點擊對話方塊,調試資訊輸出:Hi, I am Harlen

運行成功了。後來發現,由於Target是繼承自QObject,所以只需要將createObject傳回型別改為QObject *,也可以運行成功。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.