diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2022-11-22 11:35:25 +0100 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2022-12-02 16:55:21 +0100 |
commit | 3f4856dbca0114b8f354df482fec437dc6b04d23 (patch) | |
tree | 7f3b6542f20c2c331a21e395a3937db30b507a8d | |
parent | 90da0220e77cb6d71df460bd0e30358d734346d1 (diff) |
Introduce QQmlApplicationEngine::loadFromModule
This allow instantiating QML types via their module and typename. It
simply exposes the underlying functionality of
QQmlComponent::loadFromModule.
Also add the corresponding convenience constructor.
This commit also does some minor refactoring so that functionality can
be shared between load and loadFromModule.
[ChangeLog][QtQml][QQmlApplicationEngine] It is now possible to load QML
types via their module and type name.
Fixes: QTBUG-97156
Change-Id: Iee812db269d27e4db4b10117ed2570272a7cc40c
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.cpp | 77 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlapplicationengine_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlfileselector.cpp | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp | 10 |
5 files changed, 81 insertions, 16 deletions
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 08564e1cc2..45f7043336 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -23,6 +23,14 @@ QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate() { } +void QQmlApplicationEnginePrivate::ensureInitialized() +{ + if (!isInitialized) { + init(); + isInitialized = true; + } +} + void QQmlApplicationEnginePrivate::cleanUp() { Q_Q(QQmlApplicationEngine); @@ -81,10 +89,7 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & { Q_Q(QQmlApplicationEngine); - if (!isInitialized) { - init(); - isInitialized = true; - } + ensureInitialized(); if (url.scheme() == QLatin1String("file") || url.scheme() == QLatin1String("qrc")) { QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url)); @@ -101,11 +106,19 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & else c->loadUrl(url); - if (!c->isLoading()) { - finishLoad(c); - return; - } - QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); + ensureLoadingFinishes(c); +} + +void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView type) +{ + Q_Q(QQmlApplicationEngine); + + _q_loadTranslations(); //Translations must be loaded before the QML file is + QQmlComponent *c = new QQmlComponent(q, q); + + ensureInitialized(); + c->loadFromModule(uri, type); + ensureLoadingFinishes(c); } void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) @@ -142,6 +155,16 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) c->deleteLater(); } +void QQmlApplicationEnginePrivate::ensureLoadingFinishes(QQmlComponent *c) +{ + Q_Q(QQmlApplicationEngine); + if (!c->isLoading()) { + finishLoad(c); + return; + } + QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); +} + /*! \class QQmlApplicationEngine \since 5.1 @@ -249,6 +272,18 @@ QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent) } /*! + Create a new QQmlApplicationEngine and loads the QML type specified by + \a uri and \a typeName + This is provided as a convenience, and is the same as using the empty constructor and calling + loadFromModule afterwards. +*/ +QQmlApplicationEngine::QQmlApplicationEngine(QAnyStringView uri, QAnyStringView typeName, QObject *parent) + : QQmlApplicationEngine(parent) +{ + loadFromModule(uri, typeName); +} + +/*! Create a new QQmlApplicationEngine and loads the QML file at the given \a filePath, which must be a local file path. If a relative path is given then it will be interpreted as relative to the working directory of the @@ -302,6 +337,30 @@ void QQmlApplicationEngine::load(const QString &filePath) } /*! + Loads the QML type \a typeName from the module specified by \a uri. + If the type originates from a QML file located at a remote url, + the type will be loaded asynchronously. + Listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} + signal to determine when the object tree is ready. + + If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} + signal is emitted with a null pointer as parameter and error messages are printed + with qWarning. + + \code + QQmlApplicationEngine engine; + engine.loadFromModule("QtQuick", "Rectangle"); + \endcode + + \sa QQmlComponent::loadFromModule + */ +void QQmlApplicationEngine::loadFromModule(QAnyStringView uri, QAnyStringView typeName) +{ + Q_D(QQmlApplicationEngine); + d->startLoad(uri, typeName); +} + +/*! Sets the \a initialProperties with which the QML component gets initialized after it gets loaded. diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index 828629602e..afbb76dd06 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -19,6 +19,7 @@ class Q_QML_EXPORT QQmlApplicationEngine : public QQmlEngine public: QQmlApplicationEngine(QObject *parent = nullptr); QQmlApplicationEngine(const QUrl &url, QObject *parent = nullptr); + QQmlApplicationEngine(QAnyStringView uri, QAnyStringView typeName, QObject *parent = nullptr); QQmlApplicationEngine(const QString &filePath, QObject *parent = nullptr); ~QQmlApplicationEngine() override; @@ -27,6 +28,7 @@ public: public Q_SLOTS: void load(const QUrl &url); void load(const QString &filePath); + void loadFromModule(QAnyStringView uri, QAnyStringView typeName); void setInitialProperties(const QVariantMap &initialProperties); void setExtraFileSelectors(const QStringList &extraFileSelectors); void loadData(const QByteArray &data, const QUrl &url = QUrl()); diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 7ff7a954b1..4cf21bba6d 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -30,12 +30,15 @@ class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePriva public: QQmlApplicationEnginePrivate(QQmlEngine *e); ~QQmlApplicationEnginePrivate(); + void ensureInitialized(); void init(); void cleanUp(); void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false); + void startLoad(QAnyStringView uri, QAnyStringView type); void _q_loadTranslations(); void finishLoad(QQmlComponent *component); + void ensureLoadingFinishes(QQmlComponent *component); QList<QObject *> objects; QVariantMap initialProperties; QStringList extraFileSelectors; diff --git a/src/qml/qml/qqmlfileselector.cpp b/src/qml/qml/qqmlfileselector.cpp index e13dbba819..1704e2d994 100644 --- a/src/qml/qml/qqmlfileselector.cpp +++ b/src/qml/qml/qqmlfileselector.cpp @@ -163,10 +163,7 @@ QQmlFileSelector* QQmlFileSelector::get(QQmlEngine* engine) if (qobject_cast<QQmlApplicationEngine *>(engine)) { auto *appEnginePrivate = static_cast<QQmlApplicationEnginePrivate *>(enginePrivate); - if (!appEnginePrivate->isInitialized) { - appEnginePrivate->init(); - appEnginePrivate->isInitialized = true; - } + appEnginePrivate->ensureInitialized(); } const QUrl nonEmptyInvalid(QLatin1String(":")); diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index d0f580cf5f..5774b68c32 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -50,12 +50,12 @@ void tst_qqmlapplicationengine::basicLoading() { int size = 0; - QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("basicTest.qml")); + auto test = std::make_unique<QQmlApplicationEngine>(testFileUrl("basicTest.qml")); QCOMPARE(test->rootObjects().size(), ++size); QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); - QSignalSpy objectCreated(test, SIGNAL(objectCreated(QObject*,QUrl))); + QSignalSpy objectCreated(test.get(), &QQmlApplicationEngine::objectCreated); test->load(testFileUrl("basicTest.qml")); QCOMPARE(objectCreated.size(), size);//one less than rootObjects().size() because we missed the first one QCOMPARE(test->rootObjects().size(), ++size); @@ -69,7 +69,11 @@ void tst_qqmlapplicationengine::basicLoading() QVERIFY(test->rootObjects()[size -1]); QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); - delete test; + test->loadFromModule("QtQuick", "Rectangle"); + QCOMPARE(objectCreated.size(), size); + QCOMPARE(test->rootObjects().size(), ++size); + QVERIFY(test->rootObjects()[size -1]); + QCOMPARE(test->rootObjects()[size -1]->metaObject()->className(), "QQuickRectangle"); } // make sure we resolve a relative URL to an absolute one, otherwise things |