在 C++程序中使用 QML
QML API 是分为三个主类——QDeclarativeEngine QdeclarativeComponent 与 QDecl arativeContext。QDeclarativeEngine 提供 QML 运行的环境QdeclarativeComponent 封 装了 QML Documents 与 QDeclarativeCo


ntext 允许程序导出数据到 QML 组件实例。 QML 还包含了 API 的一个方便 通过 QDeclarativeView 应用程序只需要简单嵌入 QML 组 件到一个新的 QGraphicsView 就可以了。这有许多细节将在下面讨论。QDeclarativeView 主要是用于快速成型的应用程序里。 如果你是重新改进使用 QML 的 Qt 应用程序请参阅 整合 QML 到现有的 Qt UI 代码。 基本用法 每个应用程序至少需求一个 QDeclarativeEngine。QDeclarativeEngine 允许配置全局设置 应用到所有的 QML 组件实例中 例如 QNetworkAccessManager 是用于网络通信以及永久 储存的路径。如果应用程序需求在 QML 组件实例间需求不同的设置只需要多个 QDeclarati veEngine。 使用 QDeclarativeComponent 类载入 QML Documents。每个 QDeclarativeComponent 实例呈现单一 QML 文档。 QDeclarativeComponent 可以传递一个文档的地址或文档的原始 文本内容。该文档的 URL 可以是本地文件系统的地址或通过 QNetworkAccessManager 支 持的网络地址。 QML 组件实例通过调用 QDeclarativeComponent::create()模式来创建。在这里载入一个 Q ML 文档的示例并且从它这里创建一个对象。 QDeclarativeEngine *engine = new QDeclarativeEngine(parent); QDeclarativeComponent component(engine, QUrl::fromLocalFile(“main.qml”)); QObject *myObject = component.create();


导出数据 QML 组件是以 QDeclarativeContext 实例化的。context 允许应用程序导出数据到该 QML 组件实例中。 单个 QDeclarativeContext 可用于一应用程序的所有实例对象或针对每个实例 使用 QDeclarativeContext 可以创建更为精确的控制导出数据。如果不传递一个 context 给 QDeclarativeComponent::create()模式那么将使用 QDeclarativeEngine 的 root context。 数据导出通过该 root context 对所有对象实例是有效的。 简单数据 为了导出数据到一个 QML 组件实例应用程序设置 Context 属性然后由 QML 属性绑定 的名称与 JavaScrip 访问。下面的例子显示通过 QGraphicsView 如何导出一个背景颜色到

QML 文件中 //main.cpp #include <QApplication> #include <QDeclarativeView> #include <QDeclarativeContext>

int main(int argc, char *argv[]) { QApplication app(argc, argv);

QDeclarativeView view; QDeclarativeContext *context = view.rootContext(); context->setContextProperty(“backgroundColor”, QColor(Qt::yellow));

view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show();

return app.exec(); }

//main.qml import Qt 4.7

Rectangle { width: 300 height: 300

color: backgroundColor

Text { anchors.centerIn: parent text: “Hello Yellow World!” } } 或者 如果你需要 main.cpp 不需要在 QDeclarativeView 显示创建的组件 你就需要使用 Q DeclarativeEngine::rootContext()替代创建 QDeclarativeContext 实例。 QDeclarativeEngine engine; QDeclarativeContext *windowContext = new QDeclarativeContext(engine.rootContext ()); windowContext->setContextProperty(“backgroundColor”, QColor(Qt::yellow));

QDeclarativeComponent component(&engine, “main.qml”); QObject *window = component.create(windowContext);

Context 属性的操作像 QML 绑定的标准属性那样——在这个例子中的 backgroundColor C ontext 属性改变为红色那么该组件对象实例将自动更新。注意删除任意 QDeclarativeC

ontext 的构造是创建者的事情。当 window 组件实例撤消时不再需要 windowContext 时w indowContext 必须被消毁。最简单的方法是确保它设置 window 作为 windowContext 的父 级。 QDeclarativeContexts 是树形结构——除了 root context 每个 QDeclarativeContexts 都有 一个父级。子级 QDeclarativeContexts 有效的继承它们父级的 context 属性。这使应用程序 分隔不同数据导出到不同的 QML 对象实例有更多自由性。如果 QDeclarativeContext 设置 一 context 属性同样它父级也被影响新的 context 属性是父级的影子。如下例子中ba ckground context 属性是 Context 1也是 root context 里 background context 属性的影 子。

结构化数据 context 属性同样可用于输出结构化与写数据到 QML 对象。除了 QVariant 支持所有已经存 在的类型外 QObject 派生类型可以分配给 context 属性。 QObject context 属性允许数据 结构化输出并允许 QML 来设置值。 下例创建 CustomPalette 对象并设置它作为 palette context 属性。 class CustomPalette : public QObject { Q_OBJECT Q_PROPERTY(QColor background READ background WRITE setBackground NOTIF Y backgroundChanged) Q_PROPERTY(QColor text READ text WRITE setText NOTIFY textChanged)

public: CustomPalette() : m_background(Qt::white), m_text(Qt::black) {}

QColor background() const { return m_background; } void setBackground(const QColor &c) { if (c != m_background) { m_background = c; emit backgroundChanged(); } }

QColor text() const { return m_text; } void setText(const QColor &c) { if (c != m_text) { m_text = c; emit textChanged(); } }

signals: void textChanged();

void backgroundChanged();

private: QColor m_background; QColor m_text; };

int main(int argc, char *argv[]) { QApplication app(argc, argv);

QDeclarativeView view; view.rootContext()->setContextProperty(“palette”, new CustomPalette);

view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show();

return app.exec(); }

QML 引用 palette 对象以及它的属性为了设置背景与文本的颜色这里是当单击窗口时 面板的文本颜色将改变成蓝色。 import Qt 4.7

Rectangle { width: 240 height: 320 color: palette.background

Text { anchors.centerIn: parent color: palette.text text: “Click me to change color!” }

MouseArea { anchors.fill: parent onClicked: { palette.text = “blue”; } } }

可以检测一个 C++属性值——这种情况下的 CustomPalette 的文本属性改变该属性必须 有相应的 NOTIFY 信息。NOTIFY 信号是属性值改变时将指定一个信号发射。 实现者应该注意的是 只有值改变时才发射信号 以防止发生死循环。 访问一个绑定的属性

没有 NOTIFY 信号的话将导致 QML 在运行时发出警告信息。 动态结构化数据 如果应用程序对结构化过于动态编译 QObject 类型那么对动态结构化数据可在运行时使 用 QDeclarativePropertyMap 类构造。

从 QML 调用 C++ 通过 public slots 输出模式或 Q_INVOKABLE 标记模式使它可以调用 QObject 派生出的类 型。 C++模式同样可以有参数并且可以返回值。QML 支持如下类型 ?bool ?unsigned int, int ?float, double, qreal ?QString ?QUrl ?QColor ?QDate,QTime,QDateTime ?QPoint,QPointF ?QSize,QSizeF ?QRect,QRectF ?QVariant 下面例子演示了当 MouseArea 单击时控制“Stopwatch”对象的开关。 //main.cpp class Stopwatch : public QObject { Q_OBJECT public: Stopwatch();

Q_INVOKABLE bool isRunning() const;

public slots: void start(); void stop();

private: bool m_running; };

int main(int argc, char *argv[]) { QApplication app(argc, argv);

QDeclarativeView view; view.rootContext()->setContextProperty(“stopwatch”, new Stopwatch);

view.setSource(QUrl::fromLocalFile(“main.qml”)); view.show();

return app.exec(); }

//main.qml

import Qt 4.7

Rectangle { width: 300 height: 300

MouseArea { anchors.fill: parent onClicked: { if (stopwatch.isRunning()) stopwatch.stop() else stopwatch.start(); } } }

值得注意的是在这个特殊的例子里有更好的方法来达到同样的效果在 main.qml 有”run ning”属性这将会是一个非常优秀的 QML 代码 // main.qml import Qt 4.7

Rectangle {

MouseArea { anchors.fill: parent onClicked: stopwatch.running = !stopwatch.running } }

当然它同样可以调用 functions declared in QML from C++。

网络组件 如果 URL 传递给 QDeclarativeComponent 是一网络资源或者 QML 文档引用一网络资源 QDeclarativeComponent 要先获取网络数据然后才可以创建对象。在这种情况下 QDecl arativeComponent 将有 Loading status。直到组件调用 QDeclarativeComponent::create() 之前应用程序将一直等待。 下面的例子显示如何从一个网络资源载入 QML 文件。在创建 QDeclarativeComponent 之 后它测试组件是否加载。如果是它连接 QDeclarativeComponent::statusChanged()信 号否则直接调用 continueLoading()。这个测试是必要的甚至 URL 都可以是远程的只 是在这种情况下要防组件是被缓存的。 MyApplication::MyApplication() { // … component = new QDeclarativeComponent(engine, QUrl(“http://www.example.com/mai n.qml”)); if (component->isLoading()) QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Statu s)), this, SLOT(continueLoading())); else

continueLoading(); }

void MyApplication::continueLoading() { if (component->isError()) { qWarning() << component->errors(); } else { QObject *myObject = component->create(); } }

Qt 资源 QML 的内容可以使用 qrcURL 方案从 Qt 资源系统载入。例如 [project/example.qrc] <!DOCTYPE RCC> <RCC version=”1.0″>

<qresource prefix=”/”> <file>main.qml</file> <file>images/background.png</file> </qresource>

</RCC>

[project/project.pro] QT += declarative

SOURCES += main.cpp RESOURCES += example.qrc

[project/main.cpp] int main(int argc, char *argv[]) { QApplication app(argc, argv);

QDeclarativeView view; view.setSource(QUrl(“qrc:/main.qml”)); view.show();

return app.exec(); } [project/main.qml] import Qt 4.7

Image { source: “images/background.png” }

请注意资源系统是不能从 QML 直接访问的。如果主 QML 文件被加载作为资源所有的 文件指定在 QML 中做为相对路径从资源系统载入。 在 QML 层使用资源系统是完全透明的。 这也意味着如果主 QML 文件没有被加载作为资源那么从 QML 不能访问资源系统。
1.这里主要是介绍如何在 c++中调用 QML 中的函数和设置 QML 中的属性的问题 2.具体代码

// UICtest.qml import Qt 4.7 Rectangle { id: mainWidget; width: 640 height: 480 function callbyc(v) { mainWidget.color = v; return "finish"; } Rectangle{ id: secondRect; x: 100; y: 20; width: 400; height: 300; Rectangle{ x: 10; y: 20; width: 30; height: 40; color: "#FF035721" Text { objectName: "NeedFindObj"; anchors.fill: parent;

text: ""; } } } }

// main.cpp #include <QtGui/QApplication> #include <QtDeclarative/QDeclarativeView> #include <QtDeclarative/QDeclarativeEngine> #include <QtDeclarative/QDeclarativeComponent> #include <QtDeclarative/QDeclarativeContext> #include <QtDeclarative/QDeclarativeItem> #include <QMetaObject> int main(int argc, char *argv[]) { QApplication a(argc, argv); QDeclarativeView qmlView; qmlView.setSource(QUrl::fromLocalFile("../UICtest/UICtest.qml")); qmlView.show(); // 获取根节点就是 QML 中 id 是 mainWidget 的节点 QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(qmlView.rootObject()); item->setProperty("color", QVariant("blue")); // 查找到我们需要的节点根均 objectname NeedFindObj 来获得并设置他的文本属性 QDeclarativeItem *item1 = item->findChild<QDeclarativeItem *>("NeedFindObj"); if (item1) { item1->setProperty("text", QVariant("OK")); } // 调用 QML 中的函数, 分别是 函数所在的对象 函数名返回值 参数 QVariant returnVar; QVariant arg1 = "blue"; QMetaObject::invokeMethod(item, "callbyc", Q_RETURN_ARG(QVariant, returnVar),Q_ARG(QVariant, arg1)); qDebug(" %s",returnVar.toString().toLocal8Bit().data()); return a.exec(); }

说明

这里的根节点是 id 为 mainWidget 的矩形元素那么在 C++中获取根节点后就可以直接的设 置他的属性了。其他属性也可以同样,调用指定节点内的函数是通过 QMetaObject 中的 invokeMethod 来进行调用的。 最后所有关于 QML 和 c++交互部分就基本写完如果想要更多的东西或者一些其他方法强 烈看看 http://doc.qt.nokia.com/4.7-snapshot/qtbinding.html或者帮助文档究竟是不是我的文档里 面没有还是怎么的