QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCApplication.cc
Go to the documentation of this file.
1#include "QGCApplication.h"
2#include "qgc_version.h"
3
4#include <QtCore/QEvent>
5#include <QtCore/QFile>
6#include <QtCore/QMetaMethod>
7#include <QtCore/QMetaObject>
8#include <QtCore/QRegularExpression>
9#include <QtGui/QFontDatabase>
10#include <QtGui/QIcon>
11#include "QGCNetworkHelper.h"
12#include <QtQml/QQmlApplicationEngine>
13#include <QtQml/QQmlContext>
14#include <QtQuick/QQuickImageProvider>
15#include <QtQuick/QQuickWindow>
16#include <QtQuickControls2/QQuickStyle>
17#include <QtSvg/QSvgRenderer>
18
19#include <QtCore/private/qthread_p.h>
20
21#include "QGCLogging.h"
22#include "AudioOutput.h"
23#include "FollowMe.h"
24#include "JoystickManager.h"
25#include "JsonHelper.h"
26#include "LinkManager.h"
27#include "MAVLinkProtocol.h"
28#include "MultiVehicleManager.h"
29#include "ParameterManager.h"
30#include "PositionManager.h"
32#include "QGCCorePlugin.h"
33#include "QGCFileDownload.h"
34#include "QGCImageProvider.h"
35#include "QGCLoggingCategory.h"
36#include "SettingsManager.h"
37#include "MavlinkSettings.h"
38#include "AppSettings.h"
39#include "UDPLink.h"
40#include "Vehicle.h"
41#include "VehicleComponent.h"
42#include "VideoManager.h"
43
44#ifndef QGC_NO_SERIAL_LINK
45#include "SerialLink.h"
46#endif
47
48QGC_LOGGING_CATEGORY(QGCApplicationLog, "API.QGCApplication")
49
50QGCApplication::QGCApplication(int &argc, char *argv[], const QGCCommandLineParser::CommandLineParseResult &cli)
51 : QApplication(argc, argv)
52 , _runningUnitTests(cli.runningUnitTests)
53 , _simpleBootTest(cli.simpleBootTest)
54 , _fakeMobile(cli.fakeMobile)
55 , _logOutput(cli.logOutput)
56 , _systemId(cli.systemId.value_or(0))
57{
58 _msecsElapsedTime.start();
59
60 // Setup for network proxy support
62
63 bool fClearSettingsOptions = cli.clearSettingsOptions; // Clear stored settings
64 const bool fClearCache = cli.clearCache; // Clear parameter/airframe caches
65 const QString loggingOptions = cli.loggingOptions.value_or(QString(""));
66
67 // Set up timer for delayed missing fact display
68 _missingParamsDelayedDisplayTimer.setSingleShot(true);
69 _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
70 (void) connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
71
72 // Set application information
73 QString applicationName;
74 if (_runningUnitTests || _simpleBootTest) {
75 // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
76 // name. Also we want to run unit tests with clean settings every time.
77 // Include test name or PID to prevent settings file conflicts when tests run in parallel
78 if (!cli.unitTests.isEmpty()) {
79 applicationName = QStringLiteral("%1_unittest_%2").arg(QGC_APP_NAME, cli.unitTests.first());
80 } else {
81 applicationName = QStringLiteral("%1_unittest_%2").arg(QGC_APP_NAME).arg(QCoreApplication::applicationPid());
82 }
83 } else {
84#ifdef QGC_DAILY_BUILD
85 // This gives daily builds their own separate settings space. Allowing you to use daily and stable builds
86 // side by side without daily screwing up your stable settings.
87 applicationName = QStringLiteral("%1 Daily").arg(QGC_APP_NAME);
88#else
89 applicationName = QGC_APP_NAME;
90#endif
91 }
92 setApplicationName(applicationName);
93 setOrganizationName(QGC_ORG_NAME);
94 setOrganizationDomain(QGC_ORG_DOMAIN);
95 setApplicationVersion(QString(QGC_APP_VERSION_STR));
96
97 // Set settings format
98 QSettings::setDefaultFormat(QSettings::IniFormat);
99 QSettings settings;
100 qCDebug(QGCApplicationLog) << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
101
102 if (!settings.isWritable()) {
103 qCWarning(QGCApplicationLog) << "Setings location is not writable";
104 }
105
106 // The setting will delete all settings on this boot
107 fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
108
109 if (_runningUnitTests || _simpleBootTest) {
110 // Unit tests run with clean settings
111 fClearSettingsOptions = true;
112 }
113
114 if (fClearSettingsOptions) {
115 // User requested settings to be cleared on command line
116 settings.clear();
117
118 // Clear parameter cache
120 paramDir.removeRecursively();
121 paramDir.mkpath(paramDir.absolutePath());
122 } else {
123 // Determine if upgrade message for settings version bump is required. Check and clear must happen before toolbox is started since
124 // that will write some settings.
125 if (settings.contains(_settingsVersionKey)) {
126 if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
127 settings.clear();
128 _settingsUpgraded = true;
129 }
130 }
131 }
132 settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
133
134 if (fClearCache) {
136 dir.removeRecursively();
137 QFile airframe(cachedAirframeMetaDataFile());
138 airframe.remove();
139 QFile parameter(cachedParameterMetaDataFile());
140 parameter.remove();
141 }
142
143 // Set up our logging filters
145
146 // We need to set language as early as possible prior to loading on JSON files.
147 setLanguage();
148
149 // Force old SVG Tiny 1.2 behavior for compatibility
150 QSvgRenderer::setDefaultOptions(QtSvg::Tiny12FeaturesOnly);
151
152#ifndef QGC_DAILY_BUILD
153 _checkForNewVersion();
154#endif
155}
156
158{
159 _locale = QLocale::system();
160 qCDebug(QGCApplicationLog) << "System reported locale:" << _locale << "; Name" << _locale.name() << "; Preffered (used in maps): " << (QLocale::system().uiLanguages().length() > 0 ? QLocale::system().uiLanguages()[0] : "None");
161
162 QLocale::Language possibleLocale = AppSettings::_qLocaleLanguageEarlyAccess();
163 if (possibleLocale != QLocale::AnyLanguage) {
164 _locale = QLocale(possibleLocale);
165 }
166 //-- We have specific fonts for Korean
167 if (_locale == QLocale::Korean) {
168 qCDebug(QGCApplicationLog) << "Loading Korean fonts" << _locale.name();
169 if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
170 qCWarning(QGCApplicationLog) << "Could not load /fonts/NanumGothic-Regular font";
171 }
172 if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
173 qCWarning(QGCApplicationLog) << "Could not load /fonts/NanumGothic-Bold font";
174 }
175 }
176 qCDebug(QGCApplicationLog) << "Loading localizations for" << _locale.name();
177 removeTranslator(JsonHelper::translator());
178 removeTranslator(&_qgcTranslatorSourceCode);
179 removeTranslator(&_qgcTranslatorQtLibs);
180 if (_locale.name() != "en_US") {
181 QLocale::setDefault(_locale);
182 if (_qgcTranslatorQtLibs.load("qt_" + _locale.name(), QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
183 installTranslator(&_qgcTranslatorQtLibs);
184 } else {
185 qCWarning(QGCApplicationLog) << "Qt lib localization for" << _locale.name() << "is not present";
186 }
187 if (_qgcTranslatorSourceCode.load(_locale, QLatin1String("qgc_source_"), "", ":/i18n")) {
188 installTranslator(&_qgcTranslatorSourceCode);
189 } else {
190 qCWarning(QGCApplicationLog) << "Error loading source localization for" << _locale.name();
191 }
192 if (JsonHelper::translator()->load(_locale, QLatin1String("qgc_json_"), "", ":/i18n")) {
193 installTranslator(JsonHelper::translator());
194 } else {
195 qCWarning(QGCApplicationLog) << "Error loading json localization for" << _locale.name();
196 }
197 }
198
199 if (_qmlAppEngine) {
200 _qmlAppEngine->retranslate();
201 }
202
203 emit languageChanged(_locale);
204}
205
210
212{
213 SettingsManager::instance()->init();
214 if (_systemId > 0) {
215 qCDebug(QGCApplicationLog) << "Setting MAVLink System ID to:" << _systemId;
216 SettingsManager::instance()->mavlinkSettings()->gcsMavlinkSystemID()->setRawValue(_systemId);
217 }
218
219 // Although this should really be in _initForNormalAppBoot putting it here allowws us to create unit tests which pop up more easily
220 if (QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) {
221 qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans font";
222 }
223
224 if (QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) {
225 qCWarning(QGCApplicationLog) << "Could not load /fonts/opensans-demibold font";
226 }
227
228 if (_simpleBootTest) {
229 // Since GStream builds are so problematic we initialize video during the simple boot test
230 // to make sure it works and verfies plugin availability.
231 _initVideo();
232 } else if (!_runningUnitTests) {
233 _initForNormalAppBoot();
234 }
235}
236
237void QGCApplication::_initVideo()
238{
239#ifdef QGC_GST_STREAMING
240 // Gstreamer video playback requires OpenGL
241 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
242#endif
243
244 QGCCorePlugin::instance(); // CorePlugin must be initialized before VideoManager for Video Cleanup
245 VideoManager::instance();
246 _videoManagerInitialized = true;
247}
248
249void QGCApplication::_initForNormalAppBoot()
250{
251 _initVideo(); // GStreamer must be initialized before QmlEngine
252
253 QQuickStyle::setStyle("Basic");
254 QGCCorePlugin::instance()->init();
256 MultiVehicleManager::instance()->init();
257 _qmlAppEngine = QGCCorePlugin::instance()->createQmlApplicationEngine(this);
258 QObject::connect(_qmlAppEngine, &QQmlApplicationEngine::objectCreationFailed, this, QCoreApplication::quit, Qt::QueuedConnection);
259 QGCCorePlugin::instance()->createRootWindow(_qmlAppEngine);
260
261 AudioOutput::instance()->init(SettingsManager::instance()->appSettings()->audioMuted());
263 QGCPositionManager::instance()->init();
264 LinkManager::instance()->init();
265 VideoManager::instance()->init(mainRootWindow());
266
267 // Image provider for Optical Flow
268 _qmlAppEngine->addImageProvider(_qgcImageProviderId, new QGCImageProvider());
269
270 // Set the window icon now that custom plugin has a chance to override it
271#ifdef Q_OS_LINUX
272 QUrl windowIcon = QUrl("qrc:/res/qgroundcontrol.ico");
273 windowIcon = _qmlAppEngine->interceptUrl(windowIcon, QQmlAbstractUrlInterceptor::UrlString);
274 // The interceptor needs "qrc:/path" but QIcon expects ":/path"
275 setWindowIcon(QIcon(":" + windowIcon.path()));
276#endif
277
278 // Safe to show popup error messages now that main window is created
279 _showErrorsInToolbar = true;
280
281 #ifdef Q_OS_LINUX
282 #ifndef Q_OS_ANDROID
283 #ifndef QGC_NO_SERIAL_LINK
284 if (!_runningUnitTests) {
285 // Determine if we have the correct permissions to access USB serial devices
286 QFile permFile("/etc/group");
287 if(permFile.open(QIODevice::ReadOnly)) {
288 while(!permFile.atEnd()) {
289 const QString line = permFile.readLine();
290 if (line.contains("dialout") && !line.contains(getenv("USER"))) {
291 permFile.close();
293 "The current user does not have the correct permissions to access serial devices. "
294 "You should also remove modemmanager since it also interferes.<br/><br/>"
295 "If you are using Ubuntu, execute the following commands to fix these issues:<br/>"
296 "<pre>sudo usermod -a -G dialout $USER<br/>"
297 "sudo apt-get remove modemmanager</pre>"));
298 break;
299 }
300 }
301 permFile.close();
302 }
303 }
304 #endif
305 #endif
306 #endif
307
308 // Now that main window is up check for lost log files
310
311 // Load known link configurations
312 LinkManager::instance()->loadLinkConfigurationList();
313
314 // Probe for joysticks
315 JoystickManager::instance()->init();
316
317 if (_settingsUpgraded) {
318 showAppMessage(tr("The format for %1 saved settings has been modified. "
319 "Your saved settings have been reset to defaults.").arg(applicationName()));
320 }
321
322 // Connect links with flag AutoconnectLink
323 LinkManager::instance()->startAutoConnectedLinks();
324}
325
327{
328 QSettings settings;
329 settings.setValue(_deleteAllSettingsKey, true);
330}
331
333{
334 QSettings settings;
335 settings.remove(_deleteAllSettingsKey);
336}
337
338void QGCApplication::reportMissingParameter(int componentId, const QString &name)
339{
340 const QPair<int, QString> missingParam(componentId, name);
341
342 if (!_missingParams.contains(missingParam)) {
343 _missingParams.append(missingParam);
344 }
345 _missingParamsDelayedDisplayTimer.start();
346}
347
348void QGCApplication::_missingParamsDisplay()
349{
350 if (_missingParams.isEmpty()) {
351 return;
352 }
353
354 QString params;
355 for (QPair<int, QString>& missingParam: _missingParams) {
356 const QString param = QStringLiteral("%1:%2").arg(missingParam.first).arg(missingParam.second);
357 if (params.isEmpty()) {
358 params += param;
359 } else {
360 params += QStringLiteral(", %1").arg(param);
361 }
362
363 }
364 _missingParams.clear();
365
366 showAppMessage(tr("Parameters are missing from firmware. You may be running a version of firmware which is not fully supported or your firmware has a bug in it. Missing params: %1").arg(params));
367}
368
369QObject *QGCApplication::_rootQmlObject()
370{
371 if (_qmlAppEngine && _qmlAppEngine->rootObjects().size()) {
372 return _qmlAppEngine->rootObjects()[0];
373 }
374
375 return nullptr;
376}
377
379{
380 // PreArm messages are handled by Vehicle and shown in Map
381 if (message.startsWith(QStringLiteral("PreArm")) || message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
382 return;
383 }
384
385 QObject *const rootQmlObject = _rootQmlObject();
386 if (rootQmlObject && _showErrorsInToolbar) {
387 QVariant varReturn;
388 QVariant varMessage = QVariant::fromValue(message);
389 QMetaObject::invokeMethod(rootQmlObject, "showCriticalVehicleMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
390 } else if (runningUnitTests() || !_showErrorsInToolbar) {
391 // Unit tests can run without UI
392 qCDebug(QGCApplicationLog) << "QGCApplication::showCriticalVehicleMessage unittest" << message;
393 } else {
394 qCWarning(QGCApplicationLog) << "Internal error";
395 }
396}
397
398void QGCApplication::showAppMessage(const QString &message, const QString &title)
399{
400 const QString dialogTitle = title.isEmpty() ? applicationName() : title;
401
402 QObject *const rootQmlObject = _rootQmlObject();
403 if (rootQmlObject) {
404 QVariant varReturn;
405 QVariant varMessage = QVariant::fromValue(message);
406 QMetaObject::invokeMethod(rootQmlObject, "_showMessageDialog", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, dialogTitle), Q_ARG(QVariant, varMessage));
407 } else if (runningUnitTests()) {
408 // Unit tests can run without UI
409 // We don't use a logging category to make it easier to debug unit tests
410 qDebug() << "QGCApplication::showAppMessage unittest title:message" << dialogTitle << message;
411 } else {
412 // UI isn't ready yet
413 _delayedAppMessages.append(QPair<QString, QString>(dialogTitle, message));
414 QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
415 }
416}
417
418void QGCApplication::showRebootAppMessage(const QString &message, const QString &title)
419{
420 static QTime lastRebootMessage;
421
422 const QTime currentTime = QTime::currentTime();
423 const QTime previousTime = lastRebootMessage;
424 lastRebootMessage = currentTime;
425
426 if (previousTime.isValid() && (previousTime.msecsTo(currentTime) < (60 * 1000 * 2))) {
427 // Debounce reboot messages
428 return;
429 }
430
431 showAppMessage(message, title);
432}
433
434void QGCApplication::_showDelayedAppMessages()
435{
436 if (_rootQmlObject()) {
437 for (const QPair<QString, QString>& appMsg: _delayedAppMessages) {
438 showAppMessage(appMsg.second, appMsg.first);
439 }
440 _delayedAppMessages.clear();
441 } else {
442 QTimer::singleShot(200, this, &QGCApplication::_showDelayedAppMessages);
443 }
444}
445
447{
448 if (!_mainRootWindow) {
449 _mainRootWindow = qobject_cast<QQuickWindow*>(_rootQmlObject());
450 }
451
452 return _mainRootWindow;
453}
454
456{
457 if (_rootQmlObject()) {
458 QMetaObject::invokeMethod(_rootQmlObject(), "showVehicleConfig");
459 }
460}
461
463{
464 if (_rootQmlObject()) {
465 QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
466 }
467}
468
469void QGCApplication::_checkForNewVersion()
470{
471 if (_runningUnitTests) {
472 return;
473 }
474
475 if (!_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
476 return;
477 }
478
479 const QString versionCheckFile = QGCCorePlugin::instance()->stableVersionCheckFileUrl();
480 if (!versionCheckFile.isEmpty()) {
481 QGCFileDownload *const download = new QGCFileDownload(this);
482 (void) connect(download, &QGCFileDownload::finished, this, &QGCApplication::_qgcCurrentStableVersionDownloadComplete);
483 if (!download->start(versionCheckFile)) {
484 qCDebug(QGCApplicationLog) << "Download QGC stable version failed to start" << download->errorString();
485 download->deleteLater();
486 }
487 }
488}
489
490void QGCApplication::_qgcCurrentStableVersionDownloadComplete(bool success, const QString &localFile, const QString &errorMsg)
491{
492 if (success) {
493 QFile versionFile(localFile);
494 if (versionFile.open(QIODevice::ReadOnly)) {
495 QTextStream textStream(&versionFile);
496 const QString version = textStream.readLine();
497
498 qCDebug(QGCApplicationLog) << version;
499
500 int majorVersion, minorVersion, buildVersion;
501 if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
502 if (_majorVersion < majorVersion ||
503 ((_majorVersion == majorVersion) && (_minorVersion < minorVersion)) ||
504 ((_majorVersion == majorVersion) && (_minorVersion == minorVersion) && (_buildVersion < buildVersion))) {
505 showAppMessage(tr("There is a newer version of %1 available. You can download it from %2.").arg(applicationName()).arg(QGCCorePlugin::instance()->stableDownloadLocation()), tr("New Version Available"));
506 }
507 }
508 }
509 } else if (!errorMsg.isEmpty()) {
510 qCDebug(QGCApplicationLog) << "Download QGC stable version failed" << errorMsg;
511 }
512
513 sender()->deleteLater();
514}
515
516bool QGCApplication::_parseVersionText(const QString &versionString, int &majorVersion, int &minorVersion, int &buildVersion)
517{
518 static const QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
519 const QRegularExpressionMatch match = regExp.match(versionString);
520 if (match.hasMatch() && match.lastCapturedIndex() == 3) {
521 majorVersion = match.captured(1).toInt();
522 minorVersion = match.captured(2).toInt();
523 buildVersion = match.captured(3).toInt();
524 return true;
525 }
526
527 return false;
528}
529
531{
532 QSettings settings;
533 const QDir parameterDir = QFileInfo(settings.fileName()).dir();
534 return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
535}
536
538{
539 QSettings settings;
540 const QDir airframeDir = QFileInfo(settings.fileName()).dir();
541 return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
542}
543
544int QGCApplication::CompressedSignalList::_signalIndex(const QMetaMethod &method)
545{
546 if (method.methodType() != QMetaMethod::Signal) {
547 qCWarning(QGCApplicationLog) << "Internal error:" << Q_FUNC_INFO << "not a signal" << method.methodType();
548 return -1;
549 }
550
551 int index = -1;
552 const QMetaObject *metaObject = method.enclosingMetaObject();
553 for (int i=0; i<=method.methodIndex(); i++) {
554 if (metaObject->method(i).methodType() != QMetaMethod::Signal) {
555 continue;
556 }
557 index++;
558 }
559
560 return index;
561}
562
563void QGCApplication::CompressedSignalList::add(const QMetaMethod &method)
564{
565 const QMetaObject *metaObject = method.enclosingMetaObject();
566 const int signalIndex = _signalIndex(method);
567
568 if (signalIndex != -1 && !contains(metaObject, signalIndex)) {
569 _signalMap[method.enclosingMetaObject()].insert(signalIndex);
570 }
571}
572
573void QGCApplication::CompressedSignalList::remove(const QMetaMethod &method)
574{
575 const int signalIndex = _signalIndex(method);
576 const QMetaObject *const metaObject = method.enclosingMetaObject();
577
578 if (signalIndex != -1 && _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex)) {
579 _signalMap[metaObject].remove(signalIndex);
580 if (_signalMap[metaObject].count() == 0) {
581 _signalMap.remove(metaObject);
582 }
583 }
584}
585
586bool QGCApplication::CompressedSignalList::contains(const QMetaObject *metaObject, int signalIndex)
587{
588 return _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex);
589}
590
591void QGCApplication::addCompressedSignal(const QMetaMethod &method)
592{
593 _compressedSignals.add(method);
594}
595
596void QGCApplication::removeCompressedSignal(const QMetaMethod &method)
597{
598 _compressedSignals.remove(method);
599}
600
601QT_WARNING_PUSH
602QT_WARNING_DISABLE_DEPRECATED
603bool QGCApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents)
604{
605 if (event->type() != QEvent::MetaCall) {
606 return QApplication::compressEvent(event, receiver, postedEvents);
607 }
608
609 const QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(event);
610 if (!mce->sender() || !_compressedSignals.contains(mce->sender()->metaObject(), mce->signalId())) {
611 return QApplication::compressEvent(event, receiver, postedEvents);
612 }
613
614 for (QPostEventList::iterator it = postedEvents->begin(); it != postedEvents->end(); ++it) {
615 QPostEvent &cur = *it;
616 if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type()) {
617 continue;
618 }
619 const QMetaCallEvent *cur_mce = static_cast<QMetaCallEvent*>(cur.event);
620 if (cur_mce->sender() != mce->sender() || cur_mce->signalId() != mce->signalId() || cur_mce->id() != mce->id()) {
621 continue;
622 }
623 /* Keep The Newest Call */
624 // We can't merely qSwap the existing posted event with the new one, since QEvent
625 // keeps track of whether it has been posted. Deletion of a formerly posted event
626 // takes the posted event list mutex and does a useless search of the posted event
627 // list upon deletion. We thus clear the QEvent::posted flag before deletion.
628 struct EventHelper : private QEvent {
629 static void clearPostedFlag(QEvent * ev) {
630 (&static_cast<EventHelper*>(ev)->t)[1] &= ~0x8001; // Hack to clear QEvent::posted
631 }
632 };
633 EventHelper::clearPostedFlag(cur.event);
634 delete cur.event;
635 cur.event = event;
636 return true;
637 }
638
639 return false;
640}
641QT_WARNING_POP
642
644{
645 if (e->type() == QEvent::Quit) {
646 // On OSX if the user selects Quit from the menu (or Command-Q) the ApplicationWindow does not signal closing. Instead you get a Quit event here only.
647 // This in turn causes the standard QGC shutdown sequence to not run. So in this case we close the window ourselves such that the
648 // signal is sent and the normal shutdown sequence runs.
649 const bool forceClose = _mainRootWindow->property("_forceClose").toBool();
650 qCDebug(QGCApplicationLog) << "Quit event" << forceClose;
651 // forceClose
652 // true: Standard QGC shutdown sequence is complete. Let the app quit normally by falling through to the base class processing.
653 // false: QGC shutdown sequence has not been run yet. Don't let this event close the app yet. Close the main window to kick off the normal shutdown.
654 if (!forceClose) {
655 //
656 _mainRootWindow->close();
657 e->ignore();
658 return true;
659 }
660 }
661
662 return QApplication::event(e);
663}
664
666{
667 return dynamic_cast<QGCImageProvider*>(_qmlAppEngine->imageProvider(_qgcImageProviderId));
668}
669
671{
672 qCDebug(QGCApplicationLog) << "Exit";
673
674 if (_videoManagerInitialized) {
675 VideoManager::instance()->cleanup();
676 }
677
678 QGCCorePlugin::instance()->cleanup();
679
680 // This is bad, but currently qobject inheritances are incorrect and cause crashes on exit without
681 delete _qmlAppEngine;
682}
683
684QString QGCApplication::numberToString(quint64 number)
685{
686 return getCurrentLanguage().toString(number);
687}
688
690{
691 QString result;
692 const QLocale kLocale = getCurrentLanguage();
693 if (size < 1024) {
694 result = kLocale.toString(size) + "B";
695 } else if (size < pow(1024, 2)) {
696 result = kLocale.toString(static_cast<double>(size) / 1024.0, 'f', 1) + "KB";
697 } else if (size < pow(1024, 3)) {
698 result = kLocale.toString(static_cast<double>(size) / pow(1024, 2), 'f', 1) + "MB";
699 } else if (size < pow(1024, 4)) {
700 result = kLocale.toString(static_cast<double>(size) / pow(1024, 3), 'f', 1) + "GB";
701 } else {
702 result = kLocale.toString(static_cast<double>(size) / pow(1024, 4), 'f', 1) + "TB";
703 }
704 return result;
705}
706
707QString QGCApplication::bigSizeMBToString(quint64 size_MB)
708{
709 QString result;
710 const QLocale kLocale = getCurrentLanguage();
711 if (size_MB < 1024) {
712 result = kLocale.toString(static_cast<double>(size_MB) , 'f', 0) + " MB";
713 } else if(size_MB < pow(1024, 2)) {
714 result = kLocale.toString(static_cast<double>(size_MB) / 1024.0, 'f', 1) + " GB";
715 } else {
716 result = kLocale.toString(static_cast<double>(size_MB) / pow(1024, 2), 'f', 2) + " TB";
717 }
718 return result;
719}
Unified file download utility with decompression, verification, and QML support.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static AudioOutput * instance()
void init(Fact *mutedFact)
Initialize the Singleton.
static FollowMe * instance()
Definition FollowMe.cc:34
void init()
Definition FollowMe.cc:39
static MAVLinkProtocol * instance()
Fact *gcsMavlinkSystemID READ gcsMavlinkSystemID CONSTANT Fact * gcsMavlinkSystemID()
static QDir parameterCacheDir()
QString bigSizeToString(quint64 size)
void qmlAttemptWindowClose()
void reportMissingParameter(int componentId, const QString &name)
void showRebootAppMessage(const QString &message, const QString &title=QString())
void init()
Perform initialize which is common to both normal application running and unit tests.
static QString cachedParameterMetaDataFile()
QQuickWindow * mainRootWindow()
void removeCompressedSignal(const QMetaMethod &method)
void languageChanged(const QLocale &locale)
static QString cachedAirframeMetaDataFile()
void addCompressedSignal(const QMetaMethod &method)
Registers the signal such that only the last duplicate signal added is left in the queue.
static void clearDeleteAllSettingsNextBoot()
Clears the persistent flag to delete all settings the next time QGroundControl is started.
static void deleteAllSettingsNextBoot()
Sets the persistent flag to delete all settings the next time QGroundControl is started.
void showCriticalVehicleMessage(const QString &message)
Show non-modal vehicle message to the user.
bool runningUnitTests() const
QGCImageProvider * qgcImageProvider()
QString bigSizeMBToString(quint64 size_MB)
void showAppMessage(const QString &message, const QString &title=QString())
Show modal application message to the user.
QLocale getCurrentLanguage() const
Get current language.
bool event(QEvent *e) final
QString numberToString(quint64 number)
void finished(bool success, const QString &localPath, const QString &errorMessage)
bool start(const QString &remoteUrl)
This is used to expose images from ImageProtocolHandler.
static QGCLoggingCategoryManager * instance()
void setFilterRulesFromSettings(const QString &commandLineLoggingOptions)
QTranslator * translator()