2#include "qgc_version.h"
4#include <QtCore/QEvent>
6#include <QtCore/QMetaMethod>
7#include <QtCore/QMetaObject>
8#include <QtCore/QRegularExpression>
9#include <QtGui/QFontDatabase>
12#include <QtQml/QQmlApplicationEngine>
13#include <QtQml/QQmlContext>
14#include <QtQuick/QQuickImageProvider>
15#include <QtQuick/QQuickWindow>
16#include <QtQuickControls2/QQuickStyle>
17#include <QtSvg/QSvgRenderer>
19#include <QtCore/private/qthread_p.h>
44#ifndef QGC_NO_SERIAL_LINK
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))
58 _msecsElapsedTime.start();
63 bool fClearSettingsOptions = cli.clearSettingsOptions;
64 const bool fClearCache = cli.clearCache;
65 const QString loggingOptions = cli.loggingOptions.value_or(QString(
""));
68 _missingParamsDelayedDisplayTimer.setSingleShot(
true);
69 _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
70 (void) connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout,
this, &QGCApplication::_missingParamsDisplay);
73 QString applicationName;
74 if (_runningUnitTests || _simpleBootTest) {
78 if (!cli.unitTests.isEmpty()) {
79 applicationName = QStringLiteral(
"%1_unittest_%2").arg(QGC_APP_NAME, cli.unitTests.first());
81 applicationName = QStringLiteral(
"%1_unittest_%2").arg(QGC_APP_NAME).arg(QCoreApplication::applicationPid());
87 applicationName = QStringLiteral(
"%1 Daily").arg(QGC_APP_NAME);
89 applicationName = QGC_APP_NAME;
92 setApplicationName(applicationName);
93 setOrganizationName(QGC_ORG_NAME);
94 setOrganizationDomain(QGC_ORG_DOMAIN);
95 setApplicationVersion(QString(QGC_APP_VERSION_STR));
98 QSettings::setDefaultFormat(QSettings::IniFormat);
100 qCDebug(QGCApplicationLog) <<
"Settings location" << settings.fileName() <<
"Is writable?:" << settings.isWritable();
102 if (!settings.isWritable()) {
103 qCWarning(QGCApplicationLog) <<
"Setings location is not writable";
107 fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
109 if (_runningUnitTests || _simpleBootTest) {
111 fClearSettingsOptions =
true;
114 if (fClearSettingsOptions) {
120 paramDir.removeRecursively();
121 paramDir.mkpath(paramDir.absolutePath());
125 if (settings.contains(_settingsVersionKey)) {
126 if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
128 _settingsUpgraded =
true;
132 settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
136 dir.removeRecursively();
137 QFile airframe(cachedAirframeMetaDataFile());
139 QFile parameter(cachedParameterMetaDataFile());
150 QSvgRenderer::setDefaultOptions(QtSvg::Tiny12FeaturesOnly);
152#ifndef QGC_DAILY_BUILD
153 _checkForNewVersion();
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");
162 QLocale::Language possibleLocale = AppSettings::_qLocaleLanguageEarlyAccess();
163 if (possibleLocale != QLocale::AnyLanguage) {
164 _locale = QLocale(possibleLocale);
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";
172 if(QFontDatabase::addApplicationFont(
":/fonts/NanumGothic-Bold") < 0) {
173 qCWarning(QGCApplicationLog) <<
"Could not load /fonts/NanumGothic-Bold font";
176 qCDebug(QGCApplicationLog) <<
"Loading localizations for" << _locale.name();
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);
185 qCWarning(QGCApplicationLog) <<
"Qt lib localization for" << _locale.name() <<
"is not present";
187 if (_qgcTranslatorSourceCode.load(_locale, QLatin1String(
"qgc_source_"),
"",
":/i18n")) {
188 installTranslator(&_qgcTranslatorSourceCode);
190 qCWarning(QGCApplicationLog) <<
"Error loading source localization for" << _locale.name();
195 qCWarning(QGCApplicationLog) <<
"Error loading json localization for" << _locale.name();
200 _qmlAppEngine->retranslate();
213 SettingsManager::instance()->init();
215 qCDebug(QGCApplicationLog) <<
"Setting MAVLink System ID to:" << _systemId;
216 SettingsManager::instance()->mavlinkSettings()->
gcsMavlinkSystemID()->setRawValue(_systemId);
220 if (QFontDatabase::addApplicationFont(
":/fonts/opensans") < 0) {
221 qCWarning(QGCApplicationLog) <<
"Could not load /fonts/opensans font";
224 if (QFontDatabase::addApplicationFont(
":/fonts/opensans-demibold") < 0) {
225 qCWarning(QGCApplicationLog) <<
"Could not load /fonts/opensans-demibold font";
228 if (_simpleBootTest) {
232 }
else if (!_runningUnitTests) {
233 _initForNormalAppBoot();
237void QGCApplication::_initVideo()
239#ifdef QGC_GST_STREAMING
241 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
244 QGCCorePlugin::instance();
245 VideoManager::instance();
246 _videoManagerInitialized =
true;
249void QGCApplication::_initForNormalAppBoot()
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);
263 QGCPositionManager::instance()->init();
264 LinkManager::instance()->init();
268 _qmlAppEngine->addImageProvider(_qgcImageProviderId,
new QGCImageProvider());
272 QUrl windowIcon = QUrl(
"qrc:/res/qgroundcontrol.ico");
273 windowIcon = _qmlAppEngine->interceptUrl(windowIcon, QQmlAbstractUrlInterceptor::UrlString);
275 setWindowIcon(QIcon(
":" + windowIcon.path()));
279 _showErrorsInToolbar =
true;
283 #ifndef QGC_NO_SERIAL_LINK
284 if (!_runningUnitTests) {
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"))) {
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>"));
312 LinkManager::instance()->loadLinkConfigurationList();
315 JoystickManager::instance()->
init();
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()));
323 LinkManager::instance()->startAutoConnectedLinks();
329 settings.setValue(_deleteAllSettingsKey,
true);
335 settings.remove(_deleteAllSettingsKey);
340 const QPair<int, QString> missingParam(componentId, name);
342 if (!_missingParams.contains(missingParam)) {
343 _missingParams.append(missingParam);
345 _missingParamsDelayedDisplayTimer.start();
348void QGCApplication::_missingParamsDisplay()
350 if (_missingParams.isEmpty()) {
355 for (QPair<int, QString>& missingParam: _missingParams) {
356 const QString param = QStringLiteral(
"%1:%2").arg(missingParam.first).arg(missingParam.second);
357 if (params.isEmpty()) {
360 params += QStringLiteral(
", %1").arg(param);
364 _missingParams.clear();
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));
369QObject *QGCApplication::_rootQmlObject()
371 if (_qmlAppEngine && _qmlAppEngine->rootObjects().size()) {
372 return _qmlAppEngine->rootObjects()[0];
381 if (message.startsWith(QStringLiteral(
"PreArm")) || message.startsWith(QStringLiteral(
"preflight"), Qt::CaseInsensitive)) {
385 QObject *
const rootQmlObject = _rootQmlObject();
386 if (rootQmlObject && _showErrorsInToolbar) {
388 QVariant varMessage = QVariant::fromValue(message);
389 QMetaObject::invokeMethod(rootQmlObject,
"showCriticalVehicleMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
392 qCDebug(QGCApplicationLog) <<
"QGCApplication::showCriticalVehicleMessage unittest" << message;
394 qCWarning(QGCApplicationLog) <<
"Internal error";
400 const QString dialogTitle = title.isEmpty() ? applicationName() : title;
402 QObject *
const rootQmlObject = _rootQmlObject();
405 QVariant varMessage = QVariant::fromValue(message);
406 QMetaObject::invokeMethod(rootQmlObject,
"_showMessageDialog", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, dialogTitle), Q_ARG(QVariant, varMessage));
410 qDebug() <<
"QGCApplication::showAppMessage unittest title:message" << dialogTitle << message;
413 _delayedAppMessages.append(QPair<QString, QString>(dialogTitle, message));
414 QTimer::singleShot(200,
this, &QGCApplication::_showDelayedAppMessages);
420 static QTime lastRebootMessage;
422 const QTime currentTime = QTime::currentTime();
423 const QTime previousTime = lastRebootMessage;
424 lastRebootMessage = currentTime;
426 if (previousTime.isValid() && (previousTime.msecsTo(currentTime) < (60 * 1000 * 2))) {
434void QGCApplication::_showDelayedAppMessages()
436 if (_rootQmlObject()) {
437 for (
const QPair<QString, QString>& appMsg: _delayedAppMessages) {
440 _delayedAppMessages.clear();
442 QTimer::singleShot(200,
this, &QGCApplication::_showDelayedAppMessages);
448 if (!_mainRootWindow) {
449 _mainRootWindow = qobject_cast<QQuickWindow*>(_rootQmlObject());
452 return _mainRootWindow;
457 if (_rootQmlObject()) {
458 QMetaObject::invokeMethod(_rootQmlObject(),
"showVehicleConfig");
464 if (_rootQmlObject()) {
465 QMetaObject::invokeMethod(_rootQmlObject(),
"attemptWindowClose");
469void QGCApplication::_checkForNewVersion()
471 if (_runningUnitTests) {
475 if (!_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
479 const QString versionCheckFile = QGCCorePlugin::instance()->stableVersionCheckFileUrl();
480 if (!versionCheckFile.isEmpty()) {
483 if (!download->
start(versionCheckFile)) {
484 qCDebug(QGCApplicationLog) <<
"Download QGC stable version failed to start" << download->errorString();
485 download->deleteLater();
490void QGCApplication::_qgcCurrentStableVersionDownloadComplete(
bool success,
const QString &localFile,
const QString &errorMsg)
493 QFile versionFile(localFile);
494 if (versionFile.open(QIODevice::ReadOnly)) {
495 QTextStream textStream(&versionFile);
496 const QString version = textStream.readLine();
498 qCDebug(QGCApplicationLog) << version;
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"));
509 }
else if (!errorMsg.isEmpty()) {
510 qCDebug(QGCApplicationLog) <<
"Download QGC stable version failed" << errorMsg;
513 sender()->deleteLater();
516bool QGCApplication::_parseVersionText(
const QString &versionString,
int &majorVersion,
int &minorVersion,
int &buildVersion)
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();
533 const QDir parameterDir = QFileInfo(settings.fileName()).dir();
534 return parameterDir.filePath(QStringLiteral(
"ParameterFactMetaData.xml"));
540 const QDir airframeDir = QFileInfo(settings.fileName()).dir();
541 return airframeDir.filePath(QStringLiteral(
"PX4AirframeFactMetaData.xml"));
544int QGCApplication::CompressedSignalList::_signalIndex(
const QMetaMethod &method)
546 if (method.methodType() != QMetaMethod::Signal) {
547 qCWarning(QGCApplicationLog) <<
"Internal error:" << Q_FUNC_INFO <<
"not a signal" << method.methodType();
552 const QMetaObject *metaObject = method.enclosingMetaObject();
553 for (
int i=0; i<=method.methodIndex(); i++) {
554 if (metaObject->method(i).methodType() != QMetaMethod::Signal) {
563void QGCApplication::CompressedSignalList::add(
const QMetaMethod &method)
565 const QMetaObject *metaObject = method.enclosingMetaObject();
566 const int signalIndex = _signalIndex(method);
568 if (signalIndex != -1 && !contains(metaObject, signalIndex)) {
569 _signalMap[method.enclosingMetaObject()].insert(signalIndex);
573void QGCApplication::CompressedSignalList::remove(
const QMetaMethod &method)
575 const int signalIndex = _signalIndex(method);
576 const QMetaObject *
const metaObject = method.enclosingMetaObject();
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);
586bool QGCApplication::CompressedSignalList::contains(
const QMetaObject *metaObject,
int signalIndex)
588 return _signalMap.contains(metaObject) && _signalMap[metaObject].contains(signalIndex);
593 _compressedSignals.add(method);
598 _compressedSignals.remove(method);
602QT_WARNING_DISABLE_DEPRECATED
603bool QGCApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents)
605 if (
event->type() != QEvent::MetaCall) {
606 return QApplication::compressEvent(
event, receiver, postedEvents);
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);
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()) {
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()) {
628 struct EventHelper :
private QEvent {
629 static void clearPostedFlag(QEvent * ev) {
630 (&
static_cast<EventHelper*
>(ev)->t)[1] &= ~0x8001;
633 EventHelper::clearPostedFlag(cur.event);
645 if (e->type() == QEvent::Quit) {
649 const bool forceClose = _mainRootWindow->property(
"_forceClose").toBool();
650 qCDebug(QGCApplicationLog) <<
"Quit event" << forceClose;
656 _mainRootWindow->close();
662 return QApplication::event(e);
667 return dynamic_cast<QGCImageProvider*
>(_qmlAppEngine->imageProvider(_qgcImageProviderId));
672 qCDebug(QGCApplicationLog) <<
"Exit";
674 if (_videoManagerInitialized) {
675 VideoManager::instance()->cleanup();
678 QGCCorePlugin::instance()->cleanup();
681 delete _qmlAppEngine;
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";
702 result = kLocale.toString(
static_cast<double>(size) / pow(1024, 4),
'f', 1) +
"TB";
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";
716 result = kLocale.toString(
static_cast<double>(size_MB) / pow(1024, 2),
'f', 2) +
" TB";
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()
void checkForLostLogFiles()
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()
void initializeProxySupport()