作者:billy
版權聲明:著作權歸作者所有,商業轉載請聯繫作者獲得授權,非商業轉載請註明出處
前言
上一章節我們介紹了在 Qt中如何註冊一個C++類到qml,也就是在qml中使用C++的類。那麼這一章節我們要倒過來,看看在C++中是如何去訪問qml的,包括修改qml中控件的屬性,以及動態創建qml組件。
Qt Quick 加載QML文檔過程解析
先來看一下 Qt Quick 中常用的一些類的繼承關係:
介紹幾個重要的成員:
-
QQmlEngine
每個QML組件都在 QQmlContext 中實例化。QQmlContext 對於向QML組件傳遞數據至關重要。在QML中上下文按層次結構排列,此層次結構由 QQmlEngine 管理。在創建任何QML組件之前,應用程序必須已創建 QQmlEngine 才能訪問QML上下文。 -
QQmlContext
上下文允許數據暴露給由QML引擎實例化的QML組件。每個 QQmlContext 都包含一組不同於其QObject屬性的屬性,這些屬性允許按名稱將數據顯式綁定到上下文。通過調用 QQmlContext::setContextProperty() 來定義和更新上下文屬性。 -
QQmlComponent
組件是可重用的、封裝的QML類型,具有定義良好的接口。可以從QML文件創建QQmlComponent實例。 -
QQmlExpression
動態執行表達式 QQmlExpression, 允許客戶端在C++中利用一個特定的QML上下文執行JavaScript表達式,表達式執行的結果以QVariant的形式返回,並且遵守QML引擎確定的轉換規則。 -
QQuickWindow
QQuickWindow 提供與 QQuickItems 的場景交互和顯示所需的圖形場景管理。QQuickWindow總是有一個不可見的根項。若要將項添加到此窗口,請將項重新分配到根項或場景中的現有項。 -
QQuickView
這是QQuickWindow的一個子類,當給定主源文件的URL時,它將自動加載和顯示QML場景。或者,您可以使用QQmlComponent實例化自己的對象,並將它們放置在手動設置的QQuickWindow中。
Qt Quick 加載一個 QML 對象的條件:
- 唯一的 一個 QQmlEngine 引擎用於加載QML文件;
- 唯一的 一個 QQmlContext 用於QML對象實例化和表達式執行的上下文環境;
- 一個 QQmlComponent 組件用於實例化第一個QML對象;
第一種加載方式:QQmlEngine + QQmlComponent
#include <QApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent *component = new QQmlComponent(&engine);
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
component->loadUrl(QUrl("qrc:/main.qml"));
if (!component->isReady() ) {
qWarning("%s", qPrintable(component->errorString()));
return -1;
}
QObject *topLevel = component->create();
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
QSurfaceFormat surfaceFormat = window->requestedFormat();
window->setFormat(surfaceFormat);
window->show();
return app.exec();
}
使用 QQmlComponent 來實例化 QML 對象的注意事項:
- 要在 QQmlEngine 實例不可用的代碼中創建組件實例,可以使用qmlContext() 或qmlEngine();
- 必須通過 component.create() 來創建實例;
- 創建完成之後需要把對象放入窗口下才能顯示;
第二種加載方式:QQmlApplicationEngine + Window
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
}
QQmlApplicationEngine 是Qt中創建 Qt Quick 工程默認使用的引擎。
QQmlApplicationEngine 聯合了 QQmlEngine 和 QmlComponent 去加載單獨的QML文件,QQmlApplicationEngine 加載以 Window 爲根對象的QML文檔,QML文檔擁有窗口的完整控制權。
第三種加載方式:QQuickView + Item
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQuickView *view = new QQuickView;
view->setSource(QUrl("qrc:/main.qml"));
view->show();
return app.exec();
}
import QtQuick 2.12
Item {
visible: true
width: 640
height: 480
}
不同於 QQmlApplicationEngine 的是,使用 QQuickView 顯示QML文檔,對窗口的控制權全部在C++代碼。
C++中各種加載方式通過 object name 修改qml中的屬性
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQuickWindow>
#include <QQuickView>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
// 1. 使用 QQmlApplicationEngine
// QQmlApplicationEngine engine;
// const QUrl url(QStringLiteral("qrc:/main.qml"));
// engine.load(url);
// QObject *rect = engine.rootObjects().at(0)->findChild<QObject*>("rect");
// if (rect)
// rect->setProperty("color", "red");
// 2. 使用 QQmlEngine + QQmlComponent
// QQmlEngine engine;
// QQmlComponent *component = new QQmlComponent(&engine);
// component->loadUrl(QUrl("qrc:/main.qml"));
// QObject *topLevel = component->create();
// QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
// window->show();
// QObject *rect = topLevel->findChild<QObject*>("rect");
// if (rect)
// rect->setProperty("color", "red");
// 3. 使用 QQuickView
QQuickView *view = new QQuickView;
view->setSource(QUrl("qrc:/main.qml"));
view->show();
QObject *rect = view->findChild<QObject*>("rect");
if (rect)
rect->setProperty("color", "red");
return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12
Item {
visible: true
width: 640
height: 480
Rectangle {
objectName: "rect"
anchors.fill: parent
color: "yellow"
}
}
Qt中的對象是一種樹形的結構,對Qt對象樹模型不瞭解的同學可以參考 Qt 對象樹。所以我們只要知道了一個節點,就可以通過 findChild 來找到他的子節點。以上三種方式都可以通過 findChild 來找到object然後修改屬性。前面兩種方式,qml文件的根對象是Window,而最後一種是Item。
C++ 調用qml中的 function
import QtQuick 2.12
Item {
function myQmlFunction(msg) {
console.log("Got message:", msg)
return "some return value"
}
}
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlEngine engine;
QQmlComponent component(&engine, "qrc:/main.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;
return app.exec();
}
運行結果:
qml: Got message: Hello from C++
QML function returned: "some return value"
C++ 連接qml中的信號
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public:
explicit MyClass(QObject *parent = nullptr);
signals:
public slots:
void cppSlot(const QString &msg) {
qDebug() << "Called the C++ slot with message:" << msg;
}
};
#endif // MYCLASS_H
main.qml
import QtQuick 2.0
Item {
width: 100; height: 100
signal qmlSignal(string msg)
MouseArea {
anchors.fill: parent
onClicked: qmlSignal("Hello from QML")
}
}
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickView>
#include <QQuickItem>
#include "myclass.h"
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQuickView view(QUrl("qrc:/main.qml"));
QObject *item = view.rootObject();
MyClass myClass;
QObject::connect(item, SIGNAL(qmlSignal(QString)),
&myClass, SLOT(cppSlot(QString)));
view.show();
return app.exec();
}
運行結果:
Called the C++ slot with message: "Hello from QML"