qml 3

C++程序中使用QML绑定机制

QML被定为一种可容易使用C++扩展,并可扩展C++的语言.使用Qt Declarative模块中的类可在C++中加载和操作QML中的组件,通过Qt的元对象系统,QML和C++对象可轻易的使用信号和槽机制进行通信.此外,QML插件可以创建发布可重用QML组件.

你可能有很多种理由要将QML和C++混合使用.如:

    使用C++源码中的函数/功能 (如使用基于Qt的C++数据模型,或调用三方C++库中的函数)
    访问Qt Declarative模块中的函数/功能 (如使用QDeclarativeImageProvider动态创建图像)
    创建QML组件(用于自己的项目或发布给其他人使用)

要使用Qt Declarative模块,必须包含和链接相应的模块,请见module index page.Qt Declarative UI Runtime 文档展示如何使用这个模块创建基于C++的应用程序.
核心模块类

Qt Declarative模块提供了一组C++ API用于在C++中扩展QML应用程序,并可将QML嵌入到C++应用程序中.Qt Declarative模块中有几个核心类为实现这个目标提供了必要的支持:

    QDeclarativeEngine: QML引擎提供了运行QML的环境.每个应用程序都需要至少一个引擎实例.
    QDeclarativeComponent:一个组件封装了一个QML文档(QML document).
    QDeclarativeContext: 上下文用来使应用程序向引擎创建的QML组件暴露数据.

 QDeclarativeEngine 用来为其中的所有QML组件实例配置全局选项:如用于网络通信的QNetworkAccessManager 和用于持久化存储的文件路径等.

QDeclarativeComponent 用于加载QML文档.每个QDeclarativeComponent 实例代表一个单一文档.组件可使用代表QML文档的URL或文件路径,QML代码来创建.组件实例化是通过QDeclarativeComponent::create()方法完成的,如下所示:

 QDeclarativeEngine engine;
 QDeclarativeComponent component(&engine, QUrl::fromLocalFile("MyRectangle.qml"));
 QObject *rectangleInstance = component.create();

 // ...
 delete rectangleInstance;

QML文档也可使用QDeclarativeView来加载.这个类为基于QWidget的视图加载QML组件提供了方便.(向基于QWidget的应用程序中整合QML的其他方法请见Integrating QML Code with existing Qt UI code)
QML与C++结合的方式

使用C++扩展QML应用程序有很多种方式.例如::

    在C++中加载QML组件并进行操作(可操作其子元素)
    直接将C++对象及其属性嵌入到QML组件中(例如,在QML中调用指定的C++对象,或使用数据集来模拟一个列表模型)
    定义新的QML元素(QObject继承)并可在QML代码中直接创建

这些方式在下面做展示.当然这些方式相互间不冲突,在应用程序中可根据需要组合使用.
在C++中加载QML组件

QML文档可使用QDeclarativeComponent 或QDeclarativeView来加载.QDeclarativeComponent 将QML组件作为一个C++对象加载;QDeclarativeView 也是这样的,但他将QML组件直接加载到一个QGraphicsView中. 可方便的将QML组件加载到一个基于QWidget应用程序中.

例如,有如下所示的一个MyItem.qml文件:

 import QtQuick 1.0

 Item {
     width: 100; height: 100
 }

下面的C++代码将这个QML文档加载到QDeclarativeComponent 或QDeclarativeView .使用QDeclarativeComponent 需要调用QDeclarativeComponent::create()来创建一个新的组件实例,而QDeclarativeView 自动创建组件实例,可通过QDeclarativeView::rootObject()来访问:
[cpp] view plaincopy

    // Using QDeclarativeComponent  
     QDeclarativeEngine engine;  
     QDeclarativeComponent component(&engine,  
             QUrl::fromLocalFile("MyItem.qml"));  
     QObject *object = component.create();  
     ...  
     delete object; <span class="comment"></span>  

    
[cpp] view plaincopy

     // Using QDeclarativeView  
     QDeclarativeView view;  
     view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
     view.show();  
     QObject *object = view.rootObject();  

这样就创建了一个MyItem.qml组件的实例--object.可使用QObject::setProperty() 或QDeclarativeProperty修改项目的属性:

 object->setProperty("width", 500);
 QDeclarativeProperty(object, "width").write(500);

当然,也可将对象转换为其实际类型,以便于在编译时期安全的调用方法.本例中基于MyItem.qml的对象是一个Item,由QDeclarativeItem 类来定义:

 QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(object);
 item->setWidth(500);

也可使用QMetaObject::invokeMethod() 和QObject::connect()来连接或调用定义在组件中的信号或函数.更多信息见Exchanging data between QML and C++ .
定位子对象

QML组件本质上是一个具有兄弟和子节点的对象树.可使用QObject::findChild()传递一个对象名称获取QML组件的子对象.例如MyItem.qml中的根对象具有一个Rectangle子元素:

 import QtQuick 1.0

 Item {
     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的子元素.

警告: 由于这种方法可以在C++中获取并操作对象树中内部的QML元素,除了测试和建立原型外我们不建议采用这种方式.QML和C++整合在一起的一个优势就是将QML的用户界面与C++逻辑和数据集相隔离,如果在C++中直接获取并操作QML对象中的内部组件会打破这个策略. 这将使开发变得困难,如更改了QML视图,新的组件中不含objectName子元素,会发生错误.最好的情况是C++实现对QML用户界面实现和内部组成QML对象树不做任何假设.
在QML组件中嵌入C++对象

当在C++应用程序中加载QML场景时,将C++数据嵌入到QML对象中是很有帮助的.QDeclarativeContext 可以向QML组件暴漏数据,将数据从C++注入到QML中.

例如,下面的QML项中有一个currentDateTime值,但并没有在这个上下文中声明:

 // MyItem.qml
 import QtQuick 1.0

 Text { text: currentDateTime }

这个currentDateTime值可以直接由加载QML组件的C++应用程序使用QDeclarativeContext::setContextProperty()进行设置:

 QDeclarativeView view;
 view.rootContext()->setContextProperty("currentDateTime", QDateTime::currentDateTime());
 view.setSource(QUrl::fromLocalFile("MyItem.qml"));
 view.show();

上下文属性可以存储为QVariant或者QObject*类型.这意味着自定义的C++对象也可以使用这种方式注入,而且可以直接在QML中读取或修改这些对象.我们将上例中的QDateTime值修改为一个嵌入的QObject实例,让QML代码调用对象实例的方法:
[cpp] view plaincopy

    class ApplicationData : public QObject  
     {  
         Q_OBJECT  
     public:  
         Q_INVOKABLE QDateTime getCurrentDateTime() const {  
             return QDateTime::currentDateTime();  
         }  
     };  
      
     int main(int argc, char *argv[]) {  
         QApplication app(argc, argv);  
      
         QDeclarativeView view;  
      
         ApplicationData data;  
         view.rootContext()->setContextProperty("applicationData", &data);  
      
         view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
         view.show();  
      
         return app.exec();  
     }   

    

// MyItem.qml
import QtQuick 1.0

Text { text: applicationData.getCurrentDateTime() }

(注意C++向QML返回的date/time值可使用Qt.formatDateTime() 及相关函数进行格式化.)

如果QML需要接收上下文的信号,可使用Connections元素进行连接.例如,如果ApplicationData有一个叫做dataChanged()的信号,这个信号可以使用Connections对象连接到一个信号处理器上:

 Text {
     text: applicationData.getCurrentDateTime()

     Connections {
         target: applicationData
         onDataChanged: console.log("The application data changed!")
     }
 }

上下文属性在QML视图中使用基于C++的数据模型时很有用.见String ListModel,Object ListModel 和 AbstractItemModel 模型,展示了在QML视图中使用QStringListModel模型,基于QObjectList的模型 和QAbstractItemModel模型 .

更多信息见QDeclarativeContext .
定义新的QML元素

QML中可以定义新的QML元素,同样也可在C++中定义;事实上很多QML元素都是通过C++类实现的.当使用这些元素创建一个QML对象时,只是简单的创建了这个基于QObject的C++类的实例,并设置了属性.

要创建与Qt Quick元素兼容的项,需要使用QDeclarativeItem作为基类.然后实现自绘和像其他QGraphicsObject一样的功能.注意在QDeclarativeItem中默认设置了QGraphicsItem::ItemHasNoContents,因为其不绘制任何东西;如果项目需要绘制则需要清除这个标志(相反的情况是只作为输入处理和逻辑分组的情况).

例如,下面是一个带有image属性的ImageViewer类:
[cpp] view plaincopy

     #include <QtCore>  
     #include <QtDeclarative>  
      
     class ImageViewer : public QDeclarativeItem  
     {  
         Q_OBJECT  
         Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)  
      
     public:  
         void setImage(const QUrl &url);  
         QUrl image() const;  
      
     signals:  
         void imageChanged();  
     };  

除了从QDeclarativeItem继承外,这都可作为与QML无关的常规类.然而,使用qmlRegisterType()注册到QML引擎后:

[cpp] view plaincopy

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

加载到C++应用程序或插件中的QML代码就可以操作ImageViewer对象:

import MyLibrary 1.0

ImageViewer { image: "smile.png" }

这里建议不要使用QDeclarativeItem文档指定属性之外的功能.这是因为GraphicsView后台依赖QML的实现细节,因此QtQuick项可再向底层移动,在QML角度上可以应用但不能修改.要最小化自定义可视项的可移植要求,就应尽量坚持使用QDeclarativeItem文档标记的属性.从QDeclarativeItem中继承但没有文档化的属性都是与实现细节相关的;他们不受官方支持可能在相关的发布版本中被去掉.

注意自定义的C++类不必从QDeclarativeItem继承;只有在需要显示时才是必须的.如果项不可见,可从QObject继承.

创建QML元素的更多信息,见Writing QML extensions with C++ 和Extending QML Functionalities using C++ .
在QML和C++之间交换数据

QML和C++对象之间可通过信号槽,属性修改等机制进行通信.对于一个C++对象,任何暴露在Qt的元对象系统中的数据--属性,信号,槽和使用Q_INVOKABLE标记的方法都可在QML中访问.在QML端,所有QML对象的数据都可在Qt元对象系统和C++中访问.
调用函数

QML函数可在C++中调用,反之亦然.

所有的QML函数都被暴漏在了元数据系统中,并可通过QMetaObject::invokeMethod()调用.C++应用程序调用QML函数:

// MyItem.qml
 import QtQuick 1.0

 Item {
     function myQmlFunction(msg) {
         console.log("Got message:", msg)
         return "some return value"
     }
 }


 

    
[cpp] view plaincopy

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

注意QMetaObject::invokeMethod()中Q_RETURN_ARG() 和Q_ARG()的参数必须指定为QVariant类型,这是QML函数和返回值的通用数据类型.

在QML中调用C++函数,函数必须是Qt的槽或标记了Q_INVOKABLE宏的函数,才能在QML中访问.下面范例中,QML代码调用了(使用QDeclarativeContext::setContextProperty()设置到QML中的)myObject对象的方法:

 // MyItem.qml
 import QtQuick 1.0

 Item {
     width: 100; height: 100

     MouseArea {
         anchors.fill: parent
         onClicked: {
             myObject.cppMethod("Hello from QML")
             myObject.cppSlot(12345)
         }
     }
 }


    
[cpp] view plaincopy

     class MyClass : public QObject  
     {  
         Q_OBJECT  
     public:  
         Q_INVOKABLE void cppMethod(const QString &msg) {  
             qDebug() << "Called the C++ method with" << msg;  
         }  
      
     public slots:  
         void cppSlot(int number) {  
             qDebug() << "Called the C++ slot with" << number;  
         }  
     };  
      
     int main(int argc, char *argv[]) {  
         QApplication app(argc, argv);  
      
         QDeclarativeView view;  
         MyClass myClass;  
         view.rootContext()->setContextProperty("myObject", &myClass);  
      
         view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
         view.show();  
      
         return app.exec();  
     }  

QML支持调用C++的重载函数.如果C++中有多个同名不同参的函数,将根据参数数量和类型调用正确的函数.
接收信号

所有QML信号都可在C++中访问,像任何标准的Qt C++信号一样可使用QObject::connect()进行连接.相反,任何C++信号都可被QML对象的信号处理函数接收.

下面的QML组件具有一个叫做qmlSignal的信号.这个信号使用QObject::connect()连接到了一个C++对象的槽上,当qmlSignal触发时会调用cppSlot()函数:

 // MyItem.qml
 import QtQuick 1.0

 Item {
     id: item
     width: 100; height: 100

     signal qmlSignal(string msg)

     MouseArea {
         anchors.fill: parent
         onClicked: item.qmlSignal("Hello from QML")
     }
 }


    
[cpp] view plaincopy

     class MyClass : public QObject  
     {  
         Q_OBJECT  
     public slots:  
         void cppSlot(const QString &msg) {  
             qDebug() << "Called the C++ slot with message:" << msg;  
         }  
     };  
      
     int main(int argc, char *argv[]) {  
         QApplication app(argc, argv);  
      
         QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));  
         QObject *item = view.rootObject();  
      
         MyClass myClass;  
         QObject::connect(item, SIGNAL(qmlSignal(QString)),  
                          &myClass, SLOT(cppSlot(QString)));  
      
         view.show();  
         return app.exec();  
     }  

要在QML中连接Qt C++的信号,使用on<SignalName>语法访问信号句柄.如果C++对象可直接在QML中创建(见上面的Defining new QML elements),信号处理函数可在对象定义时指定.在下面例子中,QML代码创建了一个ImageViewer对象,C++对象的imageChanged和loadingError信号连接到QML中的onImageChanged和onLoadingError信号处理函数:

 ImageViewer {
     onImageChanged: console.log("Image changed!")
     onLoadingError: console.log("Image failed to load:", errorMsg)
 }


[cpp] view plaincopy

     class ImageViewer : public QDeclarativeItem  
     {  
         Q_OBJECT  
         Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)  
     public:  
         ...  
     signals:  
         void imageChanged();  
         void loadingError(const QString &errorMsg);  
     };  

    

(注意如果信号被声明为属性的NOTIFY信号,QML就允许使用on<Property>Changed句柄访问这个信号,即使信号的名称不是<Property>Changed.上例中,如果将imageChanged信号改为imageModified,onImageChanged信号处理函数还是会被调用的.)

然而对于不是从QML中创建的对象,QML中的元素只能访问已创建的对象--例如如果对象是通过QDeclarativeContext::setContextProperty()设置的--就可使用Connections元素来设置信号处理函数了:

// MyItem.qml
 import QtQuick 1.0

 Item {
     Connections {
         target: imageViewer
         onImageChanged: console.log("Image has changed!")
     }
 }


[cpp] view plaincopy

     ImageViewer viewer;  
      
     QDeclarativeView view;  
     view.rootContext()->setContextProperty("imageViewer", &viewer);  
      
     view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
     view.show();  

    

 

C++信号可以使用枚举值作为参数,枚举定义在类中随信号触发而传递,这个枚举必须使用Q_ENUMS宏注册.见Using enumerations of a custom type.
修改属性

C ++中可以访问QML对象的所有属性.对如下QML对象:

 // MyItem.qml
 import QtQuick 1.0

 Item {
     property int someNumber: 100
 }

使用QDeclarativeProperty, 或QObject::setProperty() 和QObject::property()可以设置和读取someNumber属性:
[cpp] view plaincopy

     QDeclarativeEngine engine;  
     QDeclarativeComponent component(&engine, "MyItem.qml");  
     QObject *object = component.create();  
      
     qDebug() << "Property value:" << QDeclarativeProperty::read(object, "someNumber").toInt();  
     QDeclarativeProperty::write(object, "someNumber", 5000);  
      
     qDebug() << "Property value:" << object->property("someNumber").toInt();  
     object->setProperty("someNumber", 100);  

你应该总使用QObject::setProperty(),QDeclarativeProperty 或QMetaProperty::write()修改QML属性值,使QML引擎知道属性已经被修改.例如,假设有一个自定义的元素PushButton,带有一个buttonText属性,反映内部的m_buttonText成员变量值.直接修改成员变量值是不明智的:
[cpp] view plaincopy

     // BAD!  
     QDeclarativeComponent component(engine, "MyButton.qml");  
     PushButton *button = qobject_cast<PushButton*>(component.create());  
     button->m_buttonText = "Click me";  

由于直接修改了成员变量的值,越过了Qt的元对象系统,QML引擎就无法知道值被修改过.这样绑定到buttonText的属性就不会更新,任何onButtonTextChanged处理函数都不会被调用.

任何使用Q_PROPERTY宏声明的Qt属性都可在QML中访问.下面修改本文档前面例子,ApplicationData类具有一个backgroundColor属性.这个属性可在QML中进行读写:

// MyItem.qml
 import QtQuick 1.0

 Rectangle {
     width: 100; height: 100
     color: applicationData.backgroundColor

     MouseArea {
         anchors.fill: parent
         onClicked: applicationData.backgroundColor = "red"
     }
 }


[cpp] view plaincopy

     class ApplicationData : public QObject  
     {  
         Q_OBJECT  
         Q_PROPERTY(QColor backgroundColor  
                 READ backgroundColor  
                 WRITE setBackgroundColor  
                 NOTIFY backgroundColorChanged)  
      
     public:  
         void setBackgroundColor(const QColor &c) {  
             if (c != m_color) {  
                 m_color = c;  
                 emit backgroundColorChanged();  
             }  
         }  
      
         QColor backgroundColor() const {  
             return m_color;  
         }  
      
     signals:  
         void backgroundColorChanged();  
      
     private:  
         QColor m_color;  
     };  

    

 

注意backgroundColorChanged被标记为backgroundColor属性的NOTIFY信号.如果Qt属性没有相关的NOTIFY信号,属性就不能用于QML的属性绑定,因为当属性值被修改时QML引擎不会得到通知.如果在QML中使用自定义类型,确保属性具有NOTIFY信号,以便于用于属性绑定中.

在QML中使用QML属性的更多信息见Tutorial: Writing QML extensions with C++ .
支持的数据类型

用于QML中的任何C++数据--自定义属性,或信号和函数的参数,QML都必须支持其类型.

默认QML支持如下数据类型:

    bool
    unsigned int, int
    float, double, qreal
    QString
    QUrl
    QColor
    QDate, QTime, QDateTime
    QPoint, QPointF
    QSize, QSizeF
    QRect, QRectF
    QVariant
    QVariantList,QVariantMap
    QObject*
    由Q_ENUMS()声明的枚举类型

为了可以在QML创建和使用自定义C++类型,C++类必须使用qmlRegisterType()注册为QML类型,请见上面的Defining new QML elements 小节.
JavaScript数组和对象

QML内建支持在QVariantList和JavaScript数组之间,QVariantMap和JavaScript对象间的转换.

例如,如下定义在QML中的函数需要两个参数,一个数组一个对象,使用标准的JavaScript语法访问数组和对象输出其中的内容.C++代码调用了这个函数,传递QVariantList 和QVariantMap参数,将自动转换为JavaScript的数组和对象:

 // MyItem.qml
 Item {
     function readValues(anArray, anObject) {
         for (var i=0; i<anArray.length; i++)
             console.log("Array item:", anArray[i])

         for (var prop in anObject) {
             console.log("Object item:", prop, "=", anObject[prop])
         }
     }
 }


    
[cpp] view plaincopy

     // C++  
     QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));  
      
     QVariantList list;  
     list << 10 << Qt::green << "bottles";  
      
     QVariantMap map;  
     map.insert("language", "QML");  
     map.insert("released", QDate(2010, 9, 21));  
      
     QMetaObject::invokeMethod(view.rootObject(), "readValues",  
             Q_ARG(QVariant, QVariant::fromValue(list)),  
             Q_ARG(QVariant, QVariant::fromValue(map)));  

This produces output like:
[cpp] view plaincopy

     Array item: 10  
     Array item: #00ff00  
     Array item: bottles  
     Object item: language = QML  
     Object item: released = Tue Sep 21 2010 00:00:00 GMT+1000 (EST)  

同样,如果C++定义了QVariantList 或QVariantMap 类型的属性或函数参数,在QML访问时,可创建JavaScript的数组或对象,并自动被转换为QVariantList 或QVariantMap 传递给C++.
使用自定义枚举类型

要在自定义C++组件中使用枚举,枚举类型必须使用Q_ENUMS宏注册到Qt的元对象系统.例如,如下C++类型具有一个Status枚举类型:
[cpp] view plaincopy

     class ImageViewer : public QDeclarativeItem  
     {  
         Q_OBJECT  
         Q_ENUMS(Status)  
         Q_PROPERTY(Status status READ status NOTIFY statusChanged)  
     public:  
         enum Status {  
             Ready,  
             Loading,  
             Error  
         };  
      
         Status status() const;  
     signals:  
         void statusChanged();  
     };  

假设ImageViewer类已经使用qmlRegisterType()进行注册,现在其Status枚举可用在QML中:

 ImageViewer {
     onStatusChanged: {
         if (status == ImageViewer.Ready)
             console.log("Image viewer is ready!")
     }
 }

要使用内置的枚举,C++类必须注册到QML中.如果C++类型不可实例化,可使用qmlRegisterUncreatableType()注册.在QML中枚举值其首字母必须大写.

更多信息见Writing QML extensions with C++ 和Extending QML Functionalities using C++.
枚举值作为信号参数

C++信号可以向QML中传递一个枚举类型参数,假设枚举和信号定义在同一个类中,或枚举值定义在Qt命名空间(Qt Namespace)中.

此外,如果C++信号带有一个枚举参数,应该使用connect()函数与QML中的函数相关联,枚举类型必须使用qRegisterMetaType()注册.

对于QML信号,作为信号参数的枚举值使用int类型替代:

 ImageViewer {
     signal someOtherSignal(int statusValue)

     Component.onCompleted: {
         someOtherSignal(ImageViewer.Loading)
     }
 }

从字符串做自动类型转换

为了方便,在QML中一些基本类型的值可使用格式化字符串指定,便于在QML中向C++传递简单的值.
Type     String format     Example
QColor     颜色名称, "#RRGGBB", "#RRGGBBAA"     "red", "#ff0000", "#ff000000"
QDate     "YYYY-MM-DD"     "2010-05-31"
QPoint     "x,y"     "10,20"
QRect     "x,y,宽x高"     "50,50,100x100"
QSize     "宽x高"     "100x200"
QTime     "hh:mm:ss"     "14:22:55"
QUrl     URL字符串     "http://www.example.com"
QVector3D     "x,y,z"     "0,1,0"
枚举值     枚举值名称     "AlignRight"

(更多字符串格式和类型见basic type documentation.)

这些字符串格式用于设置QML属性值和向C++函数传递参数.本文档中有很多范例进行演示;在上面的范例中,ApplicationData类有一个QColor类型的backgroundColor属性,在QML中使用字符串"red"而不是一个QColor对象进行赋值.

如果喜欢使用显式类型赋值,Qt对象提供了便利的全局函数来创建对象的值.例如Qt.rgba()创建一个基于RGBA的QColor值.这个函数返回的QColor类型的值可用于设置QColor类型的属性,或调用需要QColor类型参数的C++函数.
创建QML插件

Qt Declarative模块包含一个QDeclarativeExtensionPlugin类,这个抽象类用于创建QML插件.可在QML应用程序中动态加载QML扩展类型.

更多信息见QDeclarativeExtensionPlugin 文档和How to Create Qt Plugins .
使用Qt资源系统管理资源文件

Qt resource system 可将资源文件存储在二进制可执行文件中.这对创建QML/C++联合的应用程序很有帮助,可通过资源系统的URI(像其他图片和声音资源文件一样)调度访问QML文件,而不是使用相对或绝对文件系统路径.注意如果使用资源系统,当QML资源文件被修改后必须重新编译可执行应用程序,以便于更新包中的资源.

要在QML/C++应用程序中使用资源系统:

    创建一个.qrc资源集合文件,以XML格式例举资源文件
    在C++中,使用:/prefix或qrc调度URL加载主QML文件资源

这样做后,QML中所有已相对路径指定的文件都从资源文件中加载.使用资源系统完全对QML层透明;即QML代码可以用相对路径来访问资源文件,而不带有qrc调度.这个调度(qrc)只用于在C++中引用资源文件.

这是使用Qt资源系统的应用程序包.目录结构如下:
[cpp] view plaincopy

     project  
         |- example.qrc  
         |- main.qml  
         |- images  
             |- background.png  
         |- main.cpp  
         |- project.pro  

main.qml 和 background.png 文件作为资源文件打包.这在example.qrc资源文件中指定:
[cpp] view plaincopy

     <!DOCTYPE RCC>  
     <RCC version="1.0">  
      
     <qresource prefix="/">  
         <file>main.qml</file>  
         <file>images/background.png</file>  
     </qresource>  
      
     </RCC>  

由于background.png 是一个资源文件,main.qml可在example.qrc中使用的相当路径引用它:

 // main.qml
 import QtQuick 1.0

 Image { source: "images/background.png" }

要使QML文件正确的定位资源文件,main.cpp加载主QML文件--main.qml,访问资源文件需要使用qrc调度(scheme):
[cpp] view plaincopy

     int main(int argc, char *argv[])  
     {  
         QApplication app(argc, argv);  
      
         QDeclarativeView view;  
         view.setSource(QUrl("qrc:/main.qml"));  
         view.show();  
      
         return app.exec();  
     }  

最后在project.pro中将RESOURCES 变量设置为用来构建应用程序资源的example.qrc 文件:
[cpp] view plaincopy

     QT += declarative  
      
     SOURCES += main.cpp  

     RESOURCES += example.qrc


QML代码与现有Qt UI代码整合


有很多方式可将QML整合到基于QWidget UI应用程序中,具体方案依赖于UI代码的特征.
与基于QWidget的UI整合

如果有一个基于QWidge的UI界面,可使用QDeclarativeView整合QML部件.QDeclarativeView继承于QWidget,因此可像其他QWidget部件一样添加到用户界面中. 使用 QDeclarativeView::setSource()向视图中加载QML文件,并将视图添加到UI中:

 QDeclarativeView *qmlView = new QDeclarativeView;
 qmlView->setSource(QUrl::fromLocalFile("myqml.qml"));

 QWidget *widget = myExistingWidget();
 QVBoxLayout *layout = new QVBoxLayout(widget);
 layout->addWidget(qmlView);

这种方式的缺点是 QDeclarativeView初始化速度慢,而且比QWidget占用更多内存,创建大量的 QDeclarativeView会使性能下降.这时最好在QML中重写部件,并使用主QML部件加载这些部件,替换掉 QDeclarativeView.

要知道QWidget相对于QML是不同的用户界面类型,因此有时将基于QWidget的应用程序转化为QML是不妥的.QWidget应该用于UI中有大量复杂且静止元素的情形,而QML则用于UI中有大量简单动态元素的情形.
与基于QGraphicsView的UI程序整合
向QGraphicScene中添加QML部件

如果有一个基于图形视图框架(Graphics View Framework)的UI程序,可直接将QML部件整合到 QGraphicsScene.使用QDeclarativeComponent 从QML文件创建一个 QGraphicsObject,再使用QGraphicsScene::addItem()将这个图像对象添加到场景,或替换场景中已存的项目.

例如:

 QGraphicsScene* scene = myExistingGraphicsScene();
 QDeclarativeEngine *engine = new QDeclarativeEngine;
 QDeclarativeComponent component(engine, QUrl::fromLocalFile("myqml.qml"));
 QGraphicsObject *object =
     qobject_cast<QGraphicsObject *>(component.create());
 scene->addItem(object);

如下QGraphicView选项用来优化QML界面效率:

    QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState)
    QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate)
    QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex)

在QML中加载QGraphicsWidget对象

另一种方式是在QML中应用已存的QGraphicWidget对象,现在需要构建一个基于QML的场景.在范例graphics layouts example中展示如何在QML中加载Qt图像布局类型,以便于在配合QGraphicsLinearLayout 和QGraphicsGridLayout使用QGraphicsWidget .

在QML中显示QGraphicsWidget 类,需要使用qmlRegisterType().范例Extending QML Functionalities using C++展示更多在QML中使用C++的信息.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值