QML与指针的互相访问

在QML刚刚出来不久,参加nokia的一个Qt Quick培训的时候,QML就给我的印象是:解释性脚本语言,没有内存操作的说法,更不用说指针了。当时也就是想想,也没有具体去实践探讨。由于现在在用Qt做产品,UI方面不得不跟QML打交道。QML做UI可以说是又好又快,大大节省了开发周期,但是由于QML处理逻辑的能力较差,所以,对于大量的逻辑处理还是需要Qt C++支持。这就涉及到C++与QML解释性语言之间数据交换。

QML在和C++相互嵌入运行的时候,就需要QML的engine: QDeclarativeEngine。通常我们不会直接与这个engine打交道。我们通常的做法是这样的:

#include   
#include "qmlapplicationviewer.h"  
#include   
#include   
#include   
  
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   

#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   

#include   


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   

#include "qmlapplicationviewer.h"  

#include   

#include   

#include   

#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注册我们的指针类型,上面文档中的类型types:QObject*是可以识别的,那是不是意味着我们注册下 Target就可以了呢?

于是修改main.cpp

#include   

#include "qmlapplicationviewer.h"  

#include   

#include   

#include   

#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();//只是添加了这么一句话,向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 *,也可以运行成功。


QML中你所不知道的state

       最后一次写QML已经是2010年了,最近由于产品需要,重拾QML。之前nokia给我们培训QML的时候,对于state这个概念理解的不是很透彻。最近在做产品前期的QML热身,发现QML中的state有一种神奇的功能:历史记忆效应

       state核心就是体现了一个状态机的原理,处在某一状态去改变某些属性以达到目的。关于state如何使用的我这里就不说了,看看nokia的QML文档就知道state如何使用。我这里主要讲讲state的历史记忆效应。

从代码开始入手说起:

import QtQuick 1.0  


Rectangle {  

    width: 360  

    height: 360  

    color: "#E4F3F9"  


    Rectangle{  

        id: innerRec  

        width: 60  

        height: 30  

        color: "#59A72C"  

        state: "pre"  

        anchors.centerIn: parent  

        Text {  

            id: txt  

            anchors.centerIn: parent  

            text: "Hello QML"  

        }  


        MouseArea{  

            anchors.fill: parent  

  

            onClicked: if (innerRec.state == "pre")innerRec.state = "tag";else innerRec.state = "pre"  
  
        }  


        states: [  

            State {  

                name: "tag"  

                PropertyChanges {  

                    target: innerRec;  

                    color: "#5CB4DA"  

                }  

                PropertyChanges {  

                    target: txt;  

                    text: "Hello Qt"  

                }  
            }  
        ]  
    }  
}  

 

当我们第一次点击巨型区域的时候,该区域会变色,并字体变为“Hello Qt”。按照设计在点击一次恢复到原始状态,也就是“Hello QML”包括颜色。

比较正常的思维就是在点击一次通过priorityChange把颜色以及字体更改回去。

其实,不用。只需要把state属性更改为原始的“pri”就可以。因为每个state,包括原始的state,都会将当前QML对象的属性记录下来,这些属性包括颜色,文字,大小等等。

所以,上面的代码就能实现不断点击不断交替切换的效果。

效果如下:

 

QML与指针的互相访问_第1张图片

 

QML与指针的互相访问_第2张图片

参考于:https://blog.csdn.net/feiyinzilgd/article/details/6680116

你可能感兴趣的:(qml与指针,qml接收指针类型,QML)