QML基礎——在C++程序中使用QML


本文翻譯自Using QML in C++ Applications。歡迎大家編輯、修改此文章。

QML API有三個主要成員——QDeclarativeEngineQDeclarativeComponentQDeclarativeContext

QDeclarativeEngine提供了QML的運行環境。 QDeclarativeComponent封裝了QML Documents。 QDeclarativeContext允許程序使用QML組件顯示數據。

QML包含一個非常好用的API——QDeclarativeView。通過它,應用程序可以很方便的把QML組件嵌入到QGraphicsView中。QDeclarativeView主要用於在應用程序開發過程中進行快速原型開發,它的主要特性將在下面討論。

如果你正打算用QML改造現有的Qt應用程序,請參考QML與Qt UI代碼整合

基本用法

若想將QML與C++程序結合,程序中至少需要一個QDeclarativeEngine。只有程序中需要使用多個不同的QML組件實例時,才需要多個QDeclarativeEngine。為了使所有QML組件實例可以工作,QDeclarativeEngine為他們提供全局配置,QDeclarativeEngine對於C++中使用QML的作用如同QNetworkAccessManager對於網絡通信、路徑對於持久化存儲的作用。

可以使用QDeclarativeComponent加載QML Documents。每一個QDeclarativeComponent實例對應一個QML document。

可以傳遞一個Document URL或者表示Document內容的原始文本給QDeclarativeComponent。Document URL可以是本地文件系統URL,或者任何QNetworkAccessManager支持的網絡URL。

可以通過QDeclarativeComponent::create()方法創建QML組件實例。下面的代碼演示了如何加載一個QML Document並創建一個實例:

QDeclarativeEngine *engine = new QDeclarativeEngine(parent);
QDeclarativeComponent component(engine, QUrl::fromLocalFile("main.qml"));
QObject *myObject = component.create();

暴露數據(Exposing Data)

QML組件在QDeclarativeContext中實例化。一個上下文(context)允許程序暴露數據給QML組件實例。一個QDeclarativeContext可用於創建應用程序中用到的所有對象實例,如果需要精確控制為每個實例暴露的數據,可以創建多個QDeclarativeContex。如果上下文(context)沒有傳遞給QDeclarativeComponent::create()方法,將默認使用QDeclarativeEngine的根上下文(root context),這時數據通過跟上下文(root context)暴露給所有對象實例。

簡單數據(Simple Data)

向QML組件實例暴露數據,通過QML屬性綁定(Property Bindings)和JavaScript對象訪問應用程序設置上下文屬性(context properties)。下面的例子展示了如何通過QDeclarativeView暴露背景顏色給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 QtQuick 1.0

Rectangle {
width: 300
height: 300

color: backgroundColor

Text {
anchors.centerIn: parent
text: "Hello Yellow World!"
}
}

如果你只希望在main.cpp里創建組件,不想顯示在QDeclarativeView中,需要使用QDeclarativeEngine::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 Properties)的工作方式同QML綁定(QML bindings)中的普通屬性(normal properties)一樣,在上面的例子中,當上下文屬性(context poperty)backgroundColor變為紅色時,組件對象實例都會自動更新。請注意創建者有責任刪除它創建的QDeclarativeContext。當銷毀Window組件時,windowContext必須被顯式的銷毀(手動delete),或者用一種更簡單的方法——設置windowContext的父類為window(父對象釋放時,會自動釋放所有子對象)。

QDeclarativeContexts以樹狀結構組織,除了根上下文(root context)外,每個QDeclarativeContext都有一個父對象。QDeclarativeContexts子對象有效的繼承父對象的上下文屬性(context properties),這使得應用程序更加靈活的在不同的QML對象實例間暴露(exposed)數據。如果QDeclarativeContext設置了一個與父對象相同的上下文屬性(context property),父對象的這個屬性將被“隱藏”。如下面的例子所示,Context1中的上下文屬性(context property)background“隱藏”了根上下文(root context)中的background屬性。

結構化數據(Structed Data)

上下文屬性(context property)還可用於向QML對象暴露結構化可寫數據。除了QVariant已經支持的所有類型外,派生自QObject的類型也可分配給上下文屬性(context property)。QObject上下文屬性(context property)允許暴露結構化的數據,並允許QML對這些數據設值。 下面的例子創建了一個CustomPalette對象,並將它設為名為palette的上下文屬性(context property):

class CustomPalette : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor background READ background WRITE setBackground NOTIFY 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對象及它的屬性來設置背景和文字顏色。當窗口被點擊時,palette的顏色將被改變,窗口文本也會被相應的更新:

import QtQuick 1.0

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產生一個運行時的警告。

動態結構數據(Dynamic Structured Data)

如果一個應用程序在編譯期具有很多QObject類型的動態結構化數據,可以使用QDeclarativePropertyMap在運行期動態的創建這些結構化數據。

在QML中調用C++方法

QML中可以調用QObject及其派生類對象中的public slot的方法或標記為Q_INVOKABLE的方法。

上述的C++方法可以具有參數和返回值,QML支持下列數據類型:

   * bool
* unsigned int, int
* float, double, qreal
* QString
* QUrl
* QColor
* QDate,QTime,QDateTime
* QPoint,QPointF
* QSize, QSizeF
* QRect,QRectF
* QVariant

下面的例子演示了當MouseArea被點擊時,觸發“Stopwatch”對象的start()/stop():

// 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 QtQuick 1.0

Rectangle {
width: 300
height: 300

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

請注意,在這個特殊的例子中,有一個更好的方法來達到同樣的結果——main.qml中可以用一個“運行中“屬性(property),更漂亮的QML代碼如下:

// main.qml
import QtQuick 1.0

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

此外,還可以調用functions declared in QML from C++描述的方法。

網絡組件(Network Components)

如果傳遞給QDeclarativeComponent一個網絡資源,或者QML document中引用了網絡資源,QDeclarativeComponent會在創建對象之前先獲取網絡資源數據。在這種情況下QDeclarativeComponent將有一個加載狀態(Loading status)。應用程序將一直等待(在調用QDeclarativeComponent::create()之前),直到組件准備完畢。

下面的例子演示了如何加載一個網絡QML文件資源。QDeclarativeComponent創建后,先檢查組件是否正在加載,如果正在加載則連接QDeclarativeComponent::statusChanged()信號,否則直接調用continueLoading()方法。雖然在這個例子里已經知道URL在遠程,但這個檢查還是必須的,這樣可以在組件已經緩存了該URL的情況下,直接創建組該件。

MyApplication::MyApplication()
{
// ...
component = new QDeclarativeComponent(engine, QUrl("http://www.example.com/main.qml"));
if (component->isLoading())
QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
this, SLOT(continueLoading()));
else
continueLoading();
}

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

Qt資源(Qt Resources)

QML可以通過qrc:URL從Qt資源系統(Qt Resource System)中加載,例如:

[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 QtQuick 1.0

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

注意:QML中無法直接訪問資源系統。如果主QML文件作為資源加載,主QML文件中的所有指定相對路徑的文件都將從資源系統中加載。如果主QML文件沒有作為資源加載,那么資源系統中的文件無法在QML中訪問。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM