转http://blog.csdn.net/henreash/article/details/7991278
QML被定为一种可容易使用C++扩展,并可扩展C++的语言.使用Qt Declarative模块中的类可在C++中加载和操作QML中的组件,通过Qt的元对象系统,QML和C++对象可轻易的使用信号和槽机制进行通信.此外,QML插件可以创建发布可重用QML组件.你可能有很多种理由要将QML和C++混合使用.如:
[list]
[]使用C++源码中的函数/功能 (如使用基于Qt的C++数据模型,或调用三方C++库中的函数)
[]访问Qt Declarative模块中的函数/功能 (如使用QDeclarativeImageProvider动态创建图像)
[]创建QML组件(用于自己的项目或发布给其他人使用)
[/list]要使用Qt Declarative模块,必须包含和链接相应的模块,请见module index page.Qt Declarative UI Runtime 文档展示如何使用这个模块创建基于C++的应用程序.
核心模块类Qt Declarative模块提供了一组C++ API用于在C++中扩展QML应用程序,并可将QML嵌入到C++应用程序中.Qt Declarative模块中有几个核心类为实现这个目标提供了必要的支持:
[list]
[]QDeclarativeEngine: QML引擎提供了运行QML的环境.每个应用程序都需要至少一个引擎实例.
[]QDeclarativeComponent:一个组件封装了一个QML文档(QML document).
[]QDeclarativeContext: 上下文用来使应用程序向引擎创建的QML组件暴露数据.
[/list] 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应用程序有很多种方式.例如::
[list]
[]在C++中加载QML组件并进行操作(可操作其子元素)
[]直接将C++对象及其属性嵌入到QML组件中(例如,在QML中调用指定的C++对象,或使用数据集来模拟一个列表模型)
[*]定义新的QML元素(QObject继承)并可在QML代码中直接创建
[/list]这些方式在下面做展示.当然这些方式相互间不冲突,在应用程序中可根据需要组合使用.
在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()来访问:
[table]
[tr][td][cpp] view plaincopy
[list=1]
[]// Using QDeclarativeComponent
[] QDeclarativeEngine engine;
[] QDeclarativeComponent component(&engine,
[] QUrl::fromLocalFile(“MyItem.qml”));
[] QObject object = component.create();
[] …
[] delete object;
[/list]
[/td][td][cpp] view plaincopy
[list=1]
[] // Using QDeclarativeView
[] QDeclarativeView view;
[] view.setSource(QUrl::fromLocalFile(“MyItem.qml”));
[] view.show();
[*] QObject *object = view.rootObject();
[/list]
[/td][/tr]
[/table]这样就创建了一个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代码调用对象实例的方法:
[table=436]
[tr][td][cpp] view plaincopy
[list=1]
[]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();
[*] }
[/list]
[/td][td]
[/td][/tr]
[/table]// MyItem.qmlimport QtQuick 1.0Text { 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
[list=1]
[] #include
[] #include
[]
[] 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();
[*] };
[/list]
除了从QDeclarativeItem继承外,这都可作为与QML无关的常规类.然而,使用qmlRegisterType()注册到QML引擎后:
[cpp] view plaincopy
[list=1]
[*]qmlRegisterType(“MyLibrary”, 1, 0, “ImageViewer”);
[/list]
加载到C++应用程序或插件中的QML代码就可以操作ImageViewer对象:
import MyLibrary 1.0ImageViewer { image: “smile.png” }这里建议不要使用QDeclarativeItem文档指定属性之外的功能.这是因为GraphicsView后台依赖QML的实现细节,因此QtQuick项可再向底层移动,在QML角度上可以应用但不能修改.要最小化自定义可视项的可移植要求,就应尽量坚持使用QDeclarativeItem文档标记的属性.从QDeclarativeItem中继承但没有文档化的属性都是与实现细节相关的;他们不受官方支持可能在相关的发布版本中被去掉.
注意自定义的C++类不必从QDeclarativeItem继承;只有在需要显示时才是必须的.如果项不可见,可从QObject继承.
创建QML元素的更多信息,见Writing QML extensions with C++ 和Extending QML Functionalities using C++ .