作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
上一章节我们介绍了在 Qt中如何注册一个C++类到qml,也就是在qml中使用C++的类。那么这一章节我们要倒过来,看看在C++中是如何去访问qml的,包括修改qml中控件的属性,以及动态创建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 对象的条件:
#include
#include
#include
#include
#include
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(topLevel);
QSurfaceFormat surfaceFormat = window->requestedFormat();
window->setFormat(surfaceFormat);
window->show();
return app.exec();
}
使用 QQmlComponent 来实例化 QML 对象的注意事项:
#include
#include
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文档拥有窗口的完整控制权。
#include
#include
#include
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++代码。
#include
#include
#include
#include
#include
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("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(topLevel);
// window->show();
// QObject *rect = topLevel->findChild("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("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。
import QtQuick 2.12
Item {
function myQmlFunction(msg) {
console.log("Got message:", msg)
return "some return value"
}
}
#include
#include
#include
#include
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"
myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include
#include
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
#include
#include
#include
#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"