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


目录(?)
[-]

  1. 核心模块类
  2. QML与C结合的方式
    1. 在C中加载QML组件
      1. 定位子对象
    2. 在QML组件中嵌入C对象
    3. 定义新的QML元素
  3. 在QML和C之间交换数据
    1. 调用函数
    2. 接收信号
    3. 修改属性
  4. 支持的数据类型
    1. JavaScript数组和对象
    2. 使用自定义枚举类型
    3. 枚举值作为信号参数
    4. 从字符串做自动类型转换
  5. 创建QML插件
  6. 使用Qt资源系统管理资源文件
原文地址:http://doc.qt.digia.com/4.7-snapshot/qtbinding.html
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 plain copy
  1. // Using QDeclarativeComponent  
  2.  QDeclarativeEngine engine;  
  3.  QDeclarativeComponent component(&engine,  
  4.          QUrl::fromLocalFile("MyItem.qml"));  
  5.  QObject *object = component.create();  
  6.  ...  
  7.  delete object; <span class="comment"></span>  
[cpp]  view plain copy
  1.  // Using QDeclarativeView  
  2.  QDeclarativeView view;  
  3.  view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
  4.  view.show();  
  5.  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 plain copy
  1. class ApplicationData : public QObject  
  2.  {  
  3.      Q_OBJECT  
  4.  public:  
  5.      Q_INVOKABLE QDateTime getCurrentDateTime() const {  
  6.          return QDateTime::currentDateTime();  
  7.      }  
  8.  };  
  9.   
  10.  int main(int argc, char *argv[]) {  
  11.      QApplication app(argc, argv);  
  12.   
  13.      QDeclarativeView view;  
  14.   
  15.      ApplicationData data;  
  16.      view.rootContext()->setContextProperty("applicationData", &data);  
  17.   
  18.      view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
  19.      view.show();  
  20.   
  21.      return app.exec();  
  22.  }   

// 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 plain copy
  1.  #include <QtCore>  
  2.  #include <QtDeclarative>  
  3.   
  4.  class ImageViewer : public QDeclarativeItem  
  5.  {  
  6.      Q_OBJECT  
  7.      Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)  
  8.   
  9.  public:  
  10.      void setImage(const QUrl &url);  
  11.      QUrl image() const;  
  12.   
  13.  signals:  
  14.      void imageChanged();  
  15.  };  

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

[cpp]  view plain copy
  1. 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 plain copy
  1.  // main.cpp  
  2.  QDeclarativeEngine engine;  
  3.  QDeclarativeComponent component(&engine, "MyItem.qml");  
  4.  QObject *object = component.create();  
  5.   
  6.  QVariant returnedValue;  
  7.  QVariant msg = "Hello from C++";  
  8.  QMetaObject::invokeMethod(object, "myQmlFunction",  
  9.          Q_RETURN_ARG(QVariant, returnedValue),  
  10.          Q_ARG(QVariant, msg));  
  11.   
  12.  qDebug() << "QML function returned:" << returnedValue.toString();  
  13.  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 plain copy
  1.  class MyClass : public QObject  
  2.  {  
  3.      Q_OBJECT  
  4.  public:  
  5.      Q_INVOKABLE void cppMethod(const QString &msg) {  
  6.          qDebug() << "Called the C++ method with" << msg;  
  7.      }  
  8.   
  9.  public slots:  
  10.      void cppSlot(int number) {  
  11.          qDebug() << "Called the C++ slot with" << number;  
  12.      }  
  13.  };  
  14.   
  15.  int main(int argc, char *argv[]) {  
  16.      QApplication app(argc, argv);  
  17.   
  18.      QDeclarativeView view;  
  19.      MyClass myClass;  
  20.      view.rootContext()->setContextProperty("myObject", &myClass);  
  21.   
  22.      view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
  23.      view.show();  
  24.   
  25.      return app.exec();  
  26.  }  

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 plain copy
  1.  class MyClass : public QObject  
  2.  {  
  3.      Q_OBJECT  
  4.  public slots:  
  5.      void cppSlot(const QString &msg) {  
  6.          qDebug() << "Called the C++ slot with message:" << msg;  
  7.      }  
  8.  };  
  9.   
  10.  int main(int argc, char *argv[]) {  
  11.      QApplication app(argc, argv);  
  12.   
  13.      QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));  
  14.      QObject *item = view.rootObject();  
  15.   
  16.      MyClass myClass;  
  17.      QObject::connect(item, SIGNAL(qmlSignal(QString)),  
  18.                       &myClass, SLOT(cppSlot(QString)));  
  19.   
  20.      view.show();  
  21.      return app.exec();  
  22.  }  

要在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 plain copy
  1.  class ImageViewer : public QDeclarativeItem  
  2.  {  
  3.      Q_OBJECT  
  4.      Q_PROPERTY(QUrl image READ image WRITE setImage NOTIFY imageChanged)  
  5.  public:  
  6.      ...  
  7.  signals:  
  8.      void imageChanged();  
  9.      void loadingError(const QString &errorMsg);  
  10.  };  

(注意如果信号被声明为属性的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 plain copy
  1.  ImageViewer viewer;  
  2.   
  3.  QDeclarativeView view;  
  4.  view.rootContext()->setContextProperty("imageViewer", &viewer);  
  5.   
  6.  view.setSource(QUrl::fromLocalFile("MyItem.qml"));  
  7.  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 plain copy
  1.  QDeclarativeEngine engine;  
  2.  QDeclarativeComponent component(&engine, "MyItem.qml");  
  3.  QObject *object = component.create();  
  4.   
  5.  qDebug() << "Property value:" << QDeclarativeProperty::read(object, "someNumber").toInt();  
  6.  QDeclarativeProperty::write(object, "someNumber", 5000);  
  7.   
  8.  qDebug() << "Property value:" << object->property("someNumber").toInt();  
  9.  object->setProperty("someNumber", 100);  

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

[cpp]  view plain copy
  1.  // BAD!  
  2.  QDeclarativeComponent component(engine, "MyButton.qml");  
  3.  PushButton *button = qobject_cast<PushButton*>(component.create());  
  4.  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 plain copy
  1.  class ApplicationData : public QObject  
  2.  {  
  3.      Q_OBJECT  
  4.      Q_PROPERTY(QColor backgroundColor  
  5.              READ backgroundColor  
  6.              WRITE setBackgroundColor  
  7.              NOTIFY backgroundColorChanged)  
  8.   
  9.  public:  
  10.      void setBackgroundColor(const QColor &c) {  
  11.          if (c != m_color) {  
  12.              m_color = c;  
  13.              emit backgroundColorChanged();  
  14.          }  
  15.      }  
  16.   
  17.      QColor backgroundColor() const {  
  18.          return m_color;  
  19.      }  
  20.   
  21.  signals:  
  22.      void backgroundColorChanged();  
  23.   
  24.  private:  
  25.      QColor m_color;  
  26.  };  
 

注意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 plain copy
  1.  // C++  
  2.  QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));  
  3.   
  4.  QVariantList list;  
  5.  list << 10 << Qt::green << "bottles";  
  6.   
  7.  QVariantMap map;  
  8.  map.insert("language""QML");  
  9.  map.insert("released", QDate(2010, 9, 21));  
  10.   
  11.  QMetaObject::invokeMethod(view.rootObject(), "readValues",  
  12.          Q_ARG(QVariant, QVariant::fromValue(list)),  
  13.          Q_ARG(QVariant, QVariant::fromValue(map)));  

This produces output like:

[cpp]  view plain copy
  1.  Array item: 10  
  2.  Array item: #00ff00  
  3.  Array item: bottles  
  4.  Object item: language = QML  
  5.  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 plain copy
  1.  class ImageViewer : public QDeclarativeItem  
  2.  {  
  3.      Q_OBJECT  
  4.      Q_ENUMS(Status)  
  5.      Q_PROPERTY(Status status READ status NOTIFY statusChanged)  
  6.  public:  
  7.      enum Status {  
  8.          Ready,  
  9.          Loading,  
  10.          Error  
  11.      };  
  12.   
  13.      Status status() const;  
  14.  signals:  
  15.      void statusChanged();  
  16.  };  

假设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 plain copy
  1.  project  
  2.      |- example.qrc  
  3.      |- main.qml  
  4.      |- images  
  5.          |- background.png  
  6.      |- main.cpp  
  7.      |- project.pro  

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

[cpp]  view plain copy
  1.  <!DOCTYPE RCC>  
  2.  <RCC version="1.0">  
  3.   
  4.  <qresource prefix="/">  
  5.      <file>main.qml</file>  
  6.      <file>images/background.png</file>  
  7.  </qresource>  
  8.   
  9.  </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 plain copy
  1.  int main(int argc, char *argv[])  
  2.  {  
  3.      QApplication app(argc, argv);  
  4.   
  5.      QDeclarativeView view;  
  6.      view.setSource(QUrl("qrc:/main.qml"));  
  7.      view.show();  
  8.   
  9.      return app.exec();  
  10.  }  

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

[cpp]  view plain copy
  1.  QT += declarative  
  2.   
  3.  SOURCES += main.cpp  
  4.  RESOURCES += example.qrc 

你可能感兴趣的:(C++,qt,qml)