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