QML与指针

                 在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_OBJECT
public:
    explicit Operation(QObject *parent = 0);
    Q_INVOKABLE inline Target* createObject() { return new Target; }//这里我们就是为了返回指针给QML

signals:

public slots:

};

#endif // OPERATION_H


//target.h
#ifndef TARGET_H
#define TARGET_H

#include <QObject>
#include <QDebug>

class Target : public QObject
{
    Q_OBJECT
public:
    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.0

Rectangle {
    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 *,也可以运行成功。





  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值