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