QML和C++混合编程--在C++应用程序中使用QML

来源:互联网 发布:解不定积分软件 编辑:程序博客网 时间:2024/05/22 12:06

    QML很容易从C++进行扩展,在Qt Declarative模块的类允许从C++加载和操纵QML组件,而且通过Qt的元素对象系统,QML和C++对象是可以容易通过Qt信号和槽进行通信的。

    QML和C++混合编程原因:

    a. 使用定义在C++源中的功能(例如,当使用一个C++基于Qt的数据模型,或者调用第三方C++库中的函数)

    b. 访问Qt Declarative模块中的功能(例如,使用QDeclarativeImageProvider来动态创建图像)

    c. 编写自己的QML元素

    要使用Qt Declarative模块,必须先包含和连接合适的模块。Using QML in C++ Application。

1. 核心模块类

    Qt Declarative模块提供了一组C++接口来实现从C++扩展QML应用程序,将QML嵌入到C++应用程序中。Qt Declarative模块中有几个核心类提供了完成该任务的基本功能。

    a. QDeclarativeEngine: 一个QML引擎提供了执行QML代码的环境,每个应用程序至少需要一个引擎实例。

    b. QDeclarativeComponent: 一个组件封装了一个QML文件

    c. QDeclarativeContext: 上下文运行应用程序将数据暴露给引擎创建的QML组件

    QDeclarativeEngine允许将全局设置应用到它的所有QML组件实例上,例如,QNetworkAccessManager用于网络通信,一个文件路径用作永久性存储等。而QDeclarativeComponent用来加载QML文件,每一个QDeclarativeComponent实例表示一个独立的文件。一个QDeclarativeComponent可以从一个URL、一个QML文件的路径或者原始的QML代码创建,可以通过QDeclarativeComponent::create()函数进行实例化:

QDeclarativeEngine engine;QDeclarativeComponent component(&engine, QUrl::fromLoacalFile("MyRectangle.qml"));QObject * ractangleInstance = component.create();//...delete rectangleInstance;
   QML 文件也可以使用QDeclarativeView加载,这个类提供了一个方便的基于QWidget的视图,它将QML组件嵌入到一个基于QGraphicsView的应用程序。

2. 混合使用QML和C++的方法

    这里提供了几种方法来通过C++扩展QML应用程序,

    a. 加载一个QML组件,然后通过C++对其进行操作

    b. 直接将一个C++对象及其属性嵌入到QML组件;

    c. 定义一个新的QML元素(通过基于QObject的C++类)并在QML代码中创建它们

    这些方法不具有排他性,可以在应用程序中同时使用多种方法。

3. 从C++中加载一个QML组件

    一个QML文件可以使用QDeclarativeComponent或者QDeclarativeView加载。QDeclarativeComponent将一个QML组件加载为一个C++对象;QDeclarativeView直接将QML组件加载到一个QGraphicsView中。

    如果使用QDeclarativeComponent,需要调用QDeclarativeComponent::create()来创建该组件的一个新的实例:

QDeclarativeEngine engine;QDeclarativeComponent component(&engine, QUrl::fromLocalFile("MyIem.qml"));QObject * object = component.create(); ...delete object;
    如果使用QDeclarativeView,那么它会自动创建该组件的实例,后面可以通过函数QDeclarativeView::rootObject()来获取实例:
QDeclarativeView view;view.setSource(QUrl::formLoacalFile("MyItem.qml"));view.show();QObject * object = view.rootObject
    这里的object是新创建的MyItem.qml组件的实例,可以使用QObject::setProperty()或者QDeclarativeProperty来修改该项目的属性:

objec->setProperty("width", 200);QDeclarativeProperty(object, "width").write(500);
    另外,也可以先将对象转换为真实的类型,然后电泳编译时安全的函数。在这里MyItem.qml的基对象是一个Item,它由QDeclarativeItem类进行定义

QDeclarativeItem * item = qobject_cast<QDeclarativeItem * >(object);item->setWidth(500);
    QML组件本质上是一个包含孩子的对象树,而孩子对象可以拥有兄弟和它自己的孩子。可以使用QObject::objectNam属性和QObject::findChild()函数来定位QML组件的子对象。MyItem.qml中项目有一个Rectangle子项目:

import QtQuick 1.0Item {    width: 100; height: 100    Rectangle {        anchors.fill: parent        objectName: "rect"    }}
    定位孩子:

QObject * rect = object->findChild<QObject *>("rect");if(rect)    rect->setProperty("color", "red")
    如果objectName用在一个ListView或者Repeater的委托中或者其他一些使用 委托创建多个实例的元素中,那么会有多个孩子使用个相同的objectName。这种情况下使用QObject::findChildren()来查找所有匹配objectName的孩子。

4. 在QML组件中嵌入C++对象

    在加载一个QML场景到C++应用程序中时,直接嵌入C++数据到QML对象是很有用的。这个可以通过QDeclarativeContext来实现,它可以将数据暴露给QML组件的上下文,允许将数据从C++注入到QML中。例如,这里有一个QML项目引用了一个currentDateTime值,而该值并没有在当前的作用域:

//MyItem.qmlimport QtQuick 1.0Text { text: currentDateTime}
    这里的currentDateTime值可以直接在加载了该QML组件的C++应用程序中设置,这里需要使用QDeclarativeContext::setContextProperty():

QDeclarativeView view;view.rootContext()-> setContextProperty("currentDateTime", QDateTime::currentDateTime());view.setSource(QUrl::fromLoacalFile("MyItem.qml"));view.show();
    上下文属性可以使用QVariant或者QObject * 值,这也就意味着自定义的C++对象也可以使用这种方式进行注入,而且这些对象可以直接在QML中进行读取和修改。

如果在QML中调用一个C++函数,那么这个函数必须是一个Qt槽,或者该函数使用Q_INVOKEABLE宏进行标记,这里就是使用了该宏对函数进行了标记。

applicationdata.h文件:

#include <QObject>#include <QDateTime>class ApplicationData : public QObject{     Q_OBJECT     public:         Q_INVOKABLE QDateTime getCurrentData\eTime() const {              return QDateTime::currentDateTime();         }}
qml加载

QDeclarativeView view;Application data;view.rootContext()->setContextProperty("applicationData", &data);view.setSource(QUrl::fromLocalFile("../myDeclarativeContext/MyItem.qml"));view.show();

      使用了QDeclarativeContext::setContextProperty()设置了applicationData对象,可以在QML文件中直接访问它。最后向项目添加新的MyItem.qml文件。

import QtQuick 1.0Text{text:applicationData.getCurrentDateTime()}
    C++代码中调用QML组件中的函数,使用QMetaObject::invokeMethod()函数来实现。如MyItem.qml中有一个函数:

import QtQuick 1.0Item {    function myQmlFunction(msg) {        console.log("Got message: ", msg);        return "some return value"    }}
    C++应用程序使用该QML函数:

QDeclarativeEngine engine;QDeclarativeComponent component(&engine, "MyItem.qml");QObject * object = component.crate();QVariant msg = "Hello from C++";QMetaObject::invokeMethod(object, "myQmlFunction", Q_RETURN_ARG(QVarient, returnedVealue), Q_ARG(QVariant, msg));qDebug<<"QML function returned:" << returnedValue.toString();delete object;

     在QMetaObject::invokeMethod()函数的Q_RETURN_ARG()和Q_ARG()参数必须指定为QVariant类型,分别用于接收返回值和传递函数参数。

     如果QML需要从上下文属性中接收一个信号,可以使用Connection元素进行关联。如果ApplicationData拥有一个叫dataChanged()的信号,那么可以在Connection对象中使用一个onDataChanged处理器来关联该信号:

Text {    text:applicationData.getCurrentDateTime()    Connections {         target: applicationData         onDataChanged: console.log("The application data changed!")    }}
    而对于QML中的信号在C++中是自动可用的,可以像关联普通的Qt C++信号一样来对其进行关联。

import QtQuick 1.0Item{    id: item    width:100; heigth:100    signal qmlSignal(string msg)    Mouse {        anchors.fill:parent        onClicked: item.qmlSingal("Hello from QML")        }}
     假如在C++代码中有一个MyClass类,它有一个cppSlot()槽,那么可以这样进行关联:    
QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));QObject * item = view.rootObject();MyClass myClass;QObject::connect(item, SIGNAL(qmlSignal(QString)),&myClass, SLOT(cppSlot(QString)));

5. 定义一个新的QML元素

    新的QML元素可以在QML元素中进行定义,它们也可以使用C++类来定义。事实上,很多核心QML元素都是通过C++类实现的。当使用这些元素中的一个来创建一个QML对象时,也就是创建了一个基于QObject的C++类的实例并且设置了属性。例如,下面的ImageViewer类有一个image路径属性:

#include <QtCore>#include <QtDeclarative>class ImageViewer: public QdeclarativeItem//继承自QDeclarativeItem{    Q_OBJECT    Q_PROPERTY(QUrl Imge READ Imge WRITE setImge NOTIFY ImgeChanged)public:    void setImage(const QUrl &url);    QUrl image() const;signals:    void imageChanged();}
      使用qmlRegisterType()使用在QML引擎进行注册:

qmlRegisterType<ImageViewer>("MyLibrary", 1, 0, "ImageViewer");

    这时,任何在C++应用程序或者插件中加载的QML代码都可以创建一个ImageViewer对象:

    import MyLibrary 1.0

    ImageViewer{image: "simle.png"}

自定义的C++类型不一定非要继承自QDeclarativeItem,只有在其是一个可显示的项目时才是必须的。如果该项目时不可以显示的,那么它可以继承自QObject。

新的QML元素的内容:Qt 帮助--Writing QML extensions with C++关键字。



0 0
原创粉丝点击