Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlapplicationengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Research In Motion.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#include <QtQml/qqmlfile.h>
6#include <QtCore/QCoreApplication>
7#include <QtCore/QTranslator>
8#include <QQmlComponent>
11#include <QtQml/private/qqmlcomponent_p.h>
12#include <QtQml/private/qqmldirdata_p.h>
13#include <QtQml/private/qqmlfileselector_p.h>
14
15using namespace Qt::Literals::StringLiterals;
16
17QT_BEGIN_NAMESPACE
18
19QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate()
20 : QQmlEnginePrivate()
21{
22 uiLanguage = QLocale().bcp47Name();
23}
24
25QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate()
26{
27}
28
29void QQmlApplicationEnginePrivate::ensureInitialized()
30{
31 if (!isInitialized) {
32 init();
33 isInitialized = true;
34 }
35}
36
37void QQmlApplicationEnginePrivate::cleanUp()
38{
39 Q_Q(QQmlApplicationEngine);
40 for (auto obj : std::as_const(objects))
41 obj->disconnect(q);
42
43 qDeleteAll(objects);
44}
45
46void QQmlApplicationEnginePrivate::init()
47{
48 Q_Q(QQmlApplicationEngine);
49 q->connect(q, &QQmlApplicationEngine::quit, QCoreApplication::instance(),
50 &QCoreApplication::quit, Qt::QueuedConnection);
51 q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(),
52 &QCoreApplication::exit, Qt::QueuedConnection);
53 QObject::connect(q, &QJSEngine::uiLanguageChanged, q, [this](){
54 _q_loadTranslations();
55 });
56#if QT_CONFIG(translation)
57 QTranslator* qtTranslator = new QTranslator(q);
58 if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::path(QLibraryInfo::TranslationsPath), QLatin1String(".qm")))
59 QCoreApplication::installTranslator(qtTranslator);
60 else
61 delete qtTranslator;
62#endif
63 auto *selector = new QQmlFileSelector(q,q);
64 selector->setExtraSelectors(extraFileSelectors);
65 QCoreApplication::instance()->setProperty("__qml_using_qqmlapplicationengine", QVariant(true));
66}
67
68void QQmlApplicationEnginePrivate::_q_loadTranslations()
69{
70#if QT_CONFIG(translation)
71 Q_Q(QQmlApplicationEngine);
72 if (translationsDirectory.isEmpty())
73 return;
74
75 auto translator = std::make_unique<QTranslator>();
76 if (!uiLanguage.value().isEmpty()) {
77 QLocale locale(uiLanguage);
78 if (translator->load(locale, QLatin1String("qml"), QLatin1String("_"), translationsDirectory, QLatin1String(".qm"))) {
79 if (activeTranslator)
80 QCoreApplication::removeTranslator(activeTranslator.get());
81 QCoreApplication::installTranslator(translator.get());
82 activeTranslator.swap(translator);
83 }
84 } else {
85 activeTranslator.reset();
86 }
87 q->retranslate();
88#endif
89}
90
92{
93 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
94 return fi.path() + "/i18n"_L1;
95}
96
97void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &data, bool dataFlag)
98{
99 Q_Q(QQmlApplicationEngine);
100
101 ensureInitialized();
102
103 updateTranslationDirectory(url);
104
105 _q_loadTranslations(); //Translations must be loaded before the QML file is
106 QQmlComponent *c = new QQmlComponent(q, q);
107
108 if (dataFlag)
109 c->setData(data,url);
110 else
111 c->loadUrl(url);
112
113 ensureLoadingFinishes(c);
114}
115
116void QQmlApplicationEnginePrivate::startLoad(QAnyStringView uri, QAnyStringView typeName)
117{
118 Q_Q(QQmlApplicationEngine);
119
120 QQmlComponent *c = new QQmlComponent(q, q);
121
122 ensureInitialized();
123
124 auto *componentPriv = QQmlComponentPrivate::get(c);
125 componentPriv->prepareLoadFromModule(uri, typeName, QQmlTypeLoader::Synchronous);
126
127 const QQmlType type = componentPriv->loadHelperType();
128
129 if (type.sourceUrl().isValid()) {
130 const auto qmlDirData = QQmlTypeLoader::get(q)->getQmldir(type.sourceUrl());
131 const QUrl url = qmlDirData->finalUrl();
132 // A QRC URL coming from a qmldir cannot contain a relative path
133 Q_ASSERT(url.scheme() != "qrc"_L1 || url.path().startsWith('/'_L1));
134 updateTranslationDirectory(url);
135 }
136
137 /* Translations must be loaded before the QML file. They require translationDirectory to
138 * already be resolved. But, in order to resolve the translationDirectory, the type of the
139 * module to load needs to be known. Therefore, loadFromModule is split into resolution and
140 * loading because the translation directory needs to be set in between.
141 */
142 _q_loadTranslations();
143 componentPriv->completeLoadFromModule(uri, typeName);
144
145 ensureLoadingFinishes(c);
146}
147
148void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c)
149{
150 Q_Q(QQmlApplicationEngine);
151 switch (c->status()) {
152 case QQmlComponent::Error:
153 qWarning() << "QQmlApplicationEngine failed to load component";
154 warning(c->errors());
155 q->objectCreated(nullptr, c->url());
156 q->objectCreationFailed(c->url());
157 break;
158 case QQmlComponent::Ready: {
159 auto newObj = initialProperties.empty() ? c->create() : c->createWithInitialProperties(initialProperties);
160
161 if (c->isError()) {
162 qWarning() << "QQmlApplicationEngine failed to create component";
163 warning(c->errors());
164 q->objectCreated(nullptr, c->url());
165 q->objectCreationFailed(c->url());
166 break;
167 }
168
169 objects << newObj;
170 QObject::connect(newObj, &QObject::destroyed, q, [&](QObject *obj) { objects.removeAll(obj); });
171 q->objectCreated(objects.constLast(), c->url());
172 }
173 break;
174 case QQmlComponent::Loading:
175 case QQmlComponent::Null:
176 return; //These cases just wait for the next status update
177 }
178
179 c->deleteLater();
180}
181
182void QQmlApplicationEnginePrivate::ensureLoadingFinishes(QQmlComponent *c)
183{
184 Q_Q(QQmlApplicationEngine);
185 if (!c->isLoading()) {
186 finishLoad(c);
187 return;
188 }
189 QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); });
190}
191
192void QQmlApplicationEnginePrivate::updateTranslationDirectory(const QUrl &url)
193{
194 const QString scheme = url.scheme();
195 if (scheme == "file"_L1) {
196 translationsDirectory = translationsDirectoryFromLocalUrl(url);
197 } else if (scheme == "qrc"_L1) {
198 translationsDirectory = translationsDirectoryFromLocalUrl(url);
199 } else {
200 translationsDirectory.clear();
201 }
202}
203
204/*!
205 \class QQmlApplicationEngine
206 \since 5.1
207 \inmodule QtQml
208 \brief QQmlApplicationEngine provides a convenient way to load an application from a single QML file.
209
210 This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++.
211
212 It can be used like so:
213
214 \code
215 #include <QGuiApplication>
216 #include <QQmlApplicationEngine>
217
218 int main(int argc, char *argv[])
219 {
220 QGuiApplication app(argc, argv);
221 QQmlApplicationEngine engine("main.qml");
222 return app.exec();
223 }
224 \endcode
225
226 Unlike QQuickView, QQmlApplicationEngine does not automatically create a root
227 window. If you are using visual items from Qt Quick, you will need to place
228 them inside of a \l [QML] {Window}.
229
230 You can also use QCoreApplication with QQmlApplicationEngine, if you are not using any QML modules which require a QGuiApplication (such as \c QtQuick).
231
232 List of configuration changes from a default QQmlEngine:
233
234 \list
235 \li Connecting Qt.quit() to QCoreApplication::quit()
236 \li Automatically loads translation files from an i18n directory adjacent to the main QML file.
237 \li Translations are reloaded when the \c QJSEngine::uiLanguage / \c Qt.uiLanguage property is changed.
238 \li Automatically sets an incubation controller if the scene contains a QQuickWindow.
239 \li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all
240 QML files and assets.
241 \endlist
242
243 The engine behavior can be further tweaked by using the inherited methods from QQmlEngine.
244
245 \note Translation files must have a \e qml_ prefix in order to be recognized,
246 e.g. \e{qml_ja_JP.qm}.
247
248 \note Placing translation files relative to the main QML file involves adding
249 a \e RESOURCE_PREFIX to the relevant \l qt_add_translations call. This
250 needs to include the resource prefix of the main file's QML module
251 (\e{/qt/qml} by default) and the module URI. For example, to provide
252 translation files for a module called "Translated":
253 \snippet cmake/qt_add_translations.cmake 0
254*/
255
256/*!
257 \fn QQmlApplicationEngine::objectCreated(QObject *object, const QUrl &url)
258
259 This signal is emitted when an object finishes loading. If loading was
260 successful, \a object contains a pointer to the loaded object, otherwise
261 the pointer is NULL.
262
263 The \a url to the component the \a object came from is also provided.
264
265 \note If the path to the component was provided as a QString containing a
266 relative path, the \a url will contain a fully resolved path to the file.
267*/
268
269/*!
270 \fn QQmlApplicationEngine::objectCreationFailed(const QUrl &url)
271 \since 6.4
272
273 This signal is emitted when loading finishes because an error occurred.
274
275 The \a url to the component that failed to load is provided as an argument.
276
277 \code
278 QGuiApplication app(argc, argv);
279 QQmlApplicationEngine engine;
280
281 // exit on error
282 QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
283 &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
284 engine.load(QUrl());
285 return app.exec();
286 \endcode
287
288 \note If the path to the component was provided as a QString containing a
289 relative path, the \a url will contain a fully resolved path to the file.
290
291 See also \l {QQmlApplicationEngine::objectCreated}, which will be emitted in
292 addition to this signal (even though creation failed).
293*/
294
295/*!
296 Create a new QQmlApplicationEngine with the given \a parent. You will have to call load() later in
297 order to load a QML file.
298*/
299QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent)
300: QQmlEngine(*(new QQmlApplicationEnginePrivate), parent)
301{
302 QJSEnginePrivate::addToDebugServer(this);
303}
304
305/*!
306 Create a new QQmlApplicationEngine and loads the QML file at the given \a url.
307 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
308*/
309QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent)
310 : QQmlApplicationEngine(parent)
311{
312 load(url);
313}
314
315/*!
316 Create a new QQmlApplicationEngine and loads the QML type specified by
317 \a uri and \a typeName
318 This is provided as a convenience, and is the same as using the empty constructor and calling
319 loadFromModule afterwards.
320
321 \since 6.5
322*/
323QQmlApplicationEngine::QQmlApplicationEngine(QAnyStringView uri, QAnyStringView typeName, QObject *parent)
324 : QQmlApplicationEngine(parent)
325{
326 loadFromModule(uri, typeName);
327}
328
329static QUrl urlFromFilePath(const QString &filePath)
330{
331 return filePath.startsWith(QLatin1Char(':'))
332 ? QUrl(QLatin1String("qrc") + filePath)
333 : QUrl::fromUserInput(filePath, QLatin1String("."), QUrl::AssumeLocalFile);
334}
335
336/*!
337 Create a new QQmlApplicationEngine and loads the QML file at the given
338 \a filePath, which must be a local file or qrc path. If a relative path is
339 given then it will be interpreted as relative to the working directory of the
340 application.
341
342 This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards.
343*/
344QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent)
345 : QQmlApplicationEngine(urlFromFilePath(filePath), parent)
346{
347}
348
349/*!
350 Destroys the QQmlApplicationEngine and all QML objects it loaded.
351*/
352QQmlApplicationEngine::~QQmlApplicationEngine()
353{
354 Q_D(QQmlApplicationEngine);
355 QJSEnginePrivate::removeFromDebugServer(this);
356 d->cleanUp();//Instantiated root objects must be deleted before the engine
357}
358
359/*!
360 Loads the root QML file located at \a url. The object tree defined by the file
361 is created immediately for local file urls. Remote urls are loaded asynchronously,
362 listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated} signal to
363 determine when the object tree is ready.
364
365 If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated}
366 signal is emitted with a null pointer as parameter and error messages are printed
367 with qWarning.
368*/
369void QQmlApplicationEngine::load(const QUrl &url)
370{
371 Q_D(QQmlApplicationEngine);
372 d->startLoad(url);
373}
374
375/*!
376 Loads the root QML file located at \a filePath. \a filePath must be a path to
377 a local file or a path to a file in the resource file system. If \a filePath
378 is a relative path, it is taken as relative to the application's working
379 directory. The object tree defined by the file is instantiated immediately.
380
381 If an error occurs, error messages are printed with qWarning.
382*/
383void QQmlApplicationEngine::load(const QString &filePath)
384{
385 Q_D(QQmlApplicationEngine);
386 d->startLoad(urlFromFilePath(filePath));
387}
388
389/*!
390 Loads the QML type \a typeName from the module specified by \a uri.
391 If the type originates from a QML file located at a remote url,
392 the type will be loaded asynchronously.
393 Listen to the \l {QQmlApplicationEngine::objectCreated()}{objectCreated}
394 signal to determine when the object tree is ready.
395
396 If an error occurs, the \l {QQmlApplicationEngine::objectCreated()}{objectCreated}
397 signal is emitted with a null pointer as parameter and error messages are printed
398 with qWarning.
399
400 \code
401 QQmlApplicationEngine engine;
402 engine.loadFromModule("QtQuick", "Rectangle");
403 \endcode
404
405 \note The module identified by \a uri is searched in the
406 \l {QML Import Path}{import path}, in the same way as if
407 you were doing \c{import uri} inside a QML file. If the
408 module cannot be located there, this function will fail.
409
410 \since 6.5
411 \sa QQmlComponent::loadFromModule
412 */
413void QQmlApplicationEngine::loadFromModule(QAnyStringView uri, QAnyStringView typeName)
414{
415 Q_D(QQmlApplicationEngine);
416 d->startLoad(uri, typeName);
417}
418
419/*!
420 Sets the \a initialProperties with which the QML component gets initialized after
421 it gets loaded.
422
423 \code
424 QQmlApplicationEngine engine;
425
426 EventDatabase eventDatabase;
427 EventMonitor eventMonitor;
428
429 engine.setInitialProperties({
430 { "eventDatabase", QVariant::fromValue(&eventDatabase) },
431 { "eventMonitor", QVariant::fromValue(&eventMonitor) }
432 });
433 \endcode
434
435 \sa QQmlComponent::setInitialProperties
436 \sa QQmlApplicationEngine::load
437 \sa QQmlApplicationEngine::loadData
438 \since 5.14
439*/
440void QQmlApplicationEngine::setInitialProperties(const QVariantMap &initialProperties)
441{
442 Q_D(QQmlApplicationEngine);
443 d->initialProperties = initialProperties;
444}
445
446/*!
447 Sets the \a extraFileSelectors to be passed to the internal QQmlFileSelector
448 used for resolving URLs to local files. The \a extraFileSelectors are applied
449 when the first QML file is loaded. Setting them afterwards has no effect.
450
451 \sa QQmlFileSelector
452 \sa QFileSelector::setExtraSelectors
453 \since 6.0
454*/
455void QQmlApplicationEngine::setExtraFileSelectors(const QStringList &extraFileSelectors)
456{
457 Q_D(QQmlApplicationEngine);
458 if (d->isInitialized) {
459 qWarning() << "QQmlApplicationEngine::setExtraFileSelectors()"
460 << "called after loading QML files. This has no effect.";
461 } else {
462 d->extraFileSelectors = extraFileSelectors;
463 }
464}
465
466/*!
467 Loads the QML given in \a data. The object tree defined by \a data is
468 instantiated immediately.
469
470 If a \a url is specified it is used as the base url of the component. This affects
471 relative paths within the data and error messages.
472
473 If an error occurs, error messages are printed with qWarning.
474*/
475void QQmlApplicationEngine::loadData(const QByteArray &data, const QUrl &url)
476{
477 Q_D(QQmlApplicationEngine);
478 d->startLoad(url, data, true);
479}
480
481/*!
482 Returns a list of all the root objects instantiated by the
483 QQmlApplicationEngine. This will only contain objects loaded via load() or a
484 convenience constructor.
485
486 \note In Qt versions prior to 5.9, this function is marked as non-\c{const}.
487*/
488
489QList<QObject *> QQmlApplicationEngine::rootObjects() const
490{
491 Q_D(const QQmlApplicationEngine);
492 return d->objects;
493}
494
495QT_END_NAMESPACE
496
497#include "moc_qqmlapplicationengine.cpp"
static QUrl urlFromFilePath(const QString &filePath)
static QString translationsDirectoryFromLocalUrl(const QUrl &url)