From 4d4c024d8e54ad280f2bfe4679d4bd2751401b12 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 21 Jun 2019 23:27:25 -0300 Subject: [PATCH] Initial example of custom build --- custom-example/custom.pri | 92 +++ custom-example/custom.qrc | 43 ++ .../Widgets/CustomArtificialHorizon.qml | 68 ++ .../Custom/Widgets/CustomAttitudeWidget.qml | 138 ++++ .../res/Custom/Widgets/CustomComboBox.qml | 252 ++++++ .../res/Custom/Widgets/CustomIconButton.qml | 63 ++ .../res/Custom/Widgets/CustomOnOffSwitch.qml | 68 ++ .../Custom/Widgets/CustomSignalStrength.qml | 48 ++ .../Custom/Widgets/CustomToolBarButton.qml | 61 ++ .../Custom/Widgets/CustomVehicleButton.qml | 98 +++ custom-example/res/Custom/Widgets/qmldir | 10 + custom-example/res/CustomCameraControl.qml | 717 ++++++++++++++++++ custom-example/res/CustomFlyView.qml | 555 ++++++++++++++ custom-example/res/Images/altitude.svg | 3 + .../res/Images/attitude_crosshair.svg | 14 + custom-example/res/Images/attitude_dial.svg | 23 + .../res/Images/attitude_pointer.svg | 9 + custom-example/res/Images/camera_photo.svg | 15 + custom-example/res/Images/camera_settings.svg | 12 + custom-example/res/Images/camera_video.svg | 10 + custom-example/res/Images/chronometer.svg | 14 + custom-example/res/Images/compass_needle.svg | 16 + custom-example/res/Images/compass_pointer.svg | 9 + custom-example/res/Images/distance.svg | 14 + .../res/Images/horizontal_speed.svg | 12 + custom-example/res/Images/microSD.svg | 3 + custom-example/res/Images/odometer.svg | 23 + custom-example/res/Images/vertical_speed.svg | 12 + custom-example/res/Images/void.png | Bin 0 -> 1639 bytes .../res/MainToolbar/CustomArmedIndicator.qml | 56 ++ .../MainToolbar/CustomBatteryIndicator.qml | 207 +++++ .../res/MainToolbar/CustomGPSIndicator.qml | 120 +++ .../res/MainToolbar/CustomMainToolBar.qml | 285 +++++++ .../CustomMainToolBarIndicators.qml | 82 ++ .../res/MainToolbar/CustomModeIndicator.qml | 85 +++ .../CustomMultiVehicleSelector.qml | 104 +++ .../res/MainToolbar/CustomRCRSSIIndicator.qml | 101 +++ .../AutoPilotPlugin/CustomAutoPilotPlugin.cc | 107 +++ .../AutoPilotPlugin/CustomAutoPilotPlugin.h | 30 + custom-example/src/CustomPlugin.cc | 427 +++++++++++ custom-example/src/CustomPlugin.h | 101 +++ custom-example/src/CustomQuickInterface.cc | 44 ++ custom-example/src/CustomQuickInterface.h | 32 + .../src/FirmwarePlugin/CustomCameraControl.cc | 135 ++++ .../src/FirmwarePlugin/CustomCameraControl.h | 69 ++ .../src/FirmwarePlugin/CustomCameraManager.cc | 21 + .../src/FirmwarePlugin/CustomCameraManager.h | 24 + .../FirmwarePlugin/CustomFirmwarePlugin.cc | 57 ++ .../src/FirmwarePlugin/CustomFirmwarePlugin.h | 33 + .../CustomFirmwarePluginFactory.cc | 41 + .../CustomFirmwarePluginFactory.h | 31 + 51 files changed, 4594 insertions(+) create mode 100644 custom-example/custom.pri create mode 100644 custom-example/custom.qrc create mode 100644 custom-example/res/Custom/Widgets/CustomArtificialHorizon.qml create mode 100644 custom-example/res/Custom/Widgets/CustomAttitudeWidget.qml create mode 100644 custom-example/res/Custom/Widgets/CustomComboBox.qml create mode 100644 custom-example/res/Custom/Widgets/CustomIconButton.qml create mode 100644 custom-example/res/Custom/Widgets/CustomOnOffSwitch.qml create mode 100644 custom-example/res/Custom/Widgets/CustomSignalStrength.qml create mode 100644 custom-example/res/Custom/Widgets/CustomToolBarButton.qml create mode 100644 custom-example/res/Custom/Widgets/CustomVehicleButton.qml create mode 100644 custom-example/res/Custom/Widgets/qmldir create mode 100644 custom-example/res/CustomCameraControl.qml create mode 100644 custom-example/res/CustomFlyView.qml create mode 100644 custom-example/res/Images/altitude.svg create mode 100644 custom-example/res/Images/attitude_crosshair.svg create mode 100644 custom-example/res/Images/attitude_dial.svg create mode 100644 custom-example/res/Images/attitude_pointer.svg create mode 100644 custom-example/res/Images/camera_photo.svg create mode 100644 custom-example/res/Images/camera_settings.svg create mode 100644 custom-example/res/Images/camera_video.svg create mode 100644 custom-example/res/Images/chronometer.svg create mode 100644 custom-example/res/Images/compass_needle.svg create mode 100644 custom-example/res/Images/compass_pointer.svg create mode 100644 custom-example/res/Images/distance.svg create mode 100644 custom-example/res/Images/horizontal_speed.svg create mode 100644 custom-example/res/Images/microSD.svg create mode 100644 custom-example/res/Images/odometer.svg create mode 100644 custom-example/res/Images/vertical_speed.svg create mode 100644 custom-example/res/Images/void.png create mode 100644 custom-example/res/MainToolbar/CustomArmedIndicator.qml create mode 100644 custom-example/res/MainToolbar/CustomBatteryIndicator.qml create mode 100644 custom-example/res/MainToolbar/CustomGPSIndicator.qml create mode 100644 custom-example/res/MainToolbar/CustomMainToolBar.qml create mode 100644 custom-example/res/MainToolbar/CustomMainToolBarIndicators.qml create mode 100644 custom-example/res/MainToolbar/CustomModeIndicator.qml create mode 100644 custom-example/res/MainToolbar/CustomMultiVehicleSelector.qml create mode 100644 custom-example/res/MainToolbar/CustomRCRSSIIndicator.qml create mode 100644 custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.cc create mode 100644 custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.h create mode 100644 custom-example/src/CustomPlugin.cc create mode 100644 custom-example/src/CustomPlugin.h create mode 100644 custom-example/src/CustomQuickInterface.cc create mode 100644 custom-example/src/CustomQuickInterface.h create mode 100644 custom-example/src/FirmwarePlugin/CustomCameraControl.cc create mode 100644 custom-example/src/FirmwarePlugin/CustomCameraControl.h create mode 100644 custom-example/src/FirmwarePlugin/CustomCameraManager.cc create mode 100644 custom-example/src/FirmwarePlugin/CustomCameraManager.h create mode 100644 custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc create mode 100644 custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h create mode 100644 custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc create mode 100644 custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.h diff --git a/custom-example/custom.pri b/custom-example/custom.pri new file mode 100644 index 000000000..c56952964 --- /dev/null +++ b/custom-example/custom.pri @@ -0,0 +1,92 @@ +message("Adding Custom Plugin") + +#-- Version control +# Major and minor versions are defined here (manually) + +CUSTOM_QGC_VER_MAJOR = 0 +CUSTOM_QGC_VER_MINOR = 0 +CUSTOM_QGC_VER_FIRST_BUILD = 0 + +# Build number is automatic +# Uses the current branch. This way it works on any branch including build-server's PR branches +CUSTOM_QGC_VER_BUILD = $$system(git --git-dir ../.git rev-list $$GIT_BRANCH --first-parent --count) +win32 { + CUSTOM_QGC_VER_BUILD = $$system("set /a $$CUSTOM_QGC_VER_BUILD - $$CUSTOM_QGC_VER_FIRST_BUILD") +} else { + CUSTOM_QGC_VER_BUILD = $$system("echo $(($$CUSTOM_QGC_VER_BUILD - $$CUSTOM_QGC_VER_FIRST_BUILD))") +} +CUSTOM_QGC_VERSION = $${CUSTOM_QGC_VER_MAJOR}.$${CUSTOM_QGC_VER_MINOR}.$${CUSTOM_QGC_VER_BUILD} + +DEFINES -= GIT_VERSION=\"\\\"$$GIT_VERSION\\\"\" +DEFINES += GIT_VERSION=\"\\\"$$CUSTOM_QGC_VERSION\\\"\" + +message(Custom QGC Version: $${CUSTOM_QGC_VERSION}) + +# Disable APM support +MAVLINK_CONF = common +CONFIG += QGC_DISABLE_APM_MAVLINK +CONFIG += QGC_DISABLE_APM_PLUGIN QGC_DISABLE_APM_PLUGIN_FACTORY QGC_DISABLE_PX4_PLUGIN_FACTORY + +# MAVLink Development +exists($$PWD/mavlink_dev) { + MAVLINKPATH_REL = $$PWD/mavlink_dev + MAVLINKPATH = $$PWD/mavlink_dev + message($$sprintf("Using user-supplied mavlink development path '%1'", $$MAVLINKPATH)) +} + +# Branding + +DEFINES += CUSTOMHEADER=\"\\\"CustomPlugin.h\\\"\" +DEFINES += CUSTOMCLASS=CustomPlugin + +TARGET = CustomQGC +DEFINES += QGC_APPLICATION_NAME=\"\\\"CustomQGC\\\"\" + +DEFINES += QGC_ORG_NAME=\"\\\"qgroundcontrol.org\\\"\" +DEFINES += QGC_ORG_DOMAIN=\"\\\"org.qgroundcontrol\\\"\" + +RESOURCES += \ + $$QGCROOT/custom/custom.qrc + +QGC_APP_NAME = "Custom GS" +QGC_BINARY_NAME = "CustomQGC" +QGC_ORG_NAME = "Custom" +QGC_ORG_DOMAIN = "org.qgroundcontrol" +QGC_APP_DESCRIPTION = "Custom QGC Ground Station" +QGC_APP_COPYRIGHT = "Copyright (C) 2019 QGroundControl Development Team. All rights reserved." + +QML_IMPORT_PATH += \ + $$QGCROOT/custom/res + +SOURCES += \ + $$PWD/src/CustomPlugin.cc \ + $$PWD/src/CustomQuickInterface.cc + +HEADERS += \ + $$PWD/src/CustomPlugin.h \ + $$PWD/src/CustomQuickInterface.h + +INCLUDEPATH += \ + $$PWD/src \ + +#------------------------------------------------------------------------------------- +# Custom Firmware/AutoPilot Plugin + +INCLUDEPATH += \ + $$QGCROOT/custom/src/FirmwarePlugin \ + $$QGCROOT/custom/src/AutoPilotPlugin + +HEADERS+= \ + $$QGCROOT/custom/src/AutoPilotPlugin/CustomAutoPilotPlugin.h \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomCameraControl.h \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomCameraManager.h \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomFirmwarePlugin.h \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomFirmwarePluginFactory.h \ + +SOURCES += \ + $$QGCROOT/custom/src/AutoPilotPlugin/CustomAutoPilotPlugin.cc \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomCameraControl.cc \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomCameraManager.cc \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomFirmwarePlugin.cc \ + $$QGCROOT/custom/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc \ + diff --git a/custom-example/custom.qrc b/custom-example/custom.qrc new file mode 100644 index 000000000..29e58bcc4 --- /dev/null +++ b/custom-example/custom.qrc @@ -0,0 +1,43 @@ + + + res/MainToolbar/CustomArmedIndicator.qml + res/MainToolbar/CustomBatteryIndicator.qml + res/CustomCameraControl.qml + res/CustomFlyView.qml + res/MainToolbar/CustomGPSIndicator.qml + res/MainToolbar/CustomMainToolBar.qml + res/MainToolbar/CustomMainToolBarIndicators.qml + res/MainToolbar/CustomModeIndicator.qml + res/MainToolbar/CustomMultiVehicleSelector.qml + res/MainToolbar/CustomRCRSSIIndicator.qml + + + res/Images/altitude.svg + res/Images/attitude_crosshair.svg + res/Images/attitude_dial.svg + res/Images/attitude_pointer.svg + res/Images/camera_photo.svg + res/Images/camera_settings.svg + res/Images/camera_video.svg + res/Images/chronometer.svg + res/Images/compass_needle.svg + res/Images/compass_pointer.svg + res/Images/distance.svg + res/Images/horizontal_speed.svg + res/Images/microSD.svg + res/Images/odometer.svg + res/Images/vertical_speed.svg + res/Images/void.png + + + res/Custom/Widgets/CustomArtificialHorizon.qml + res/Custom/Widgets/CustomAttitudeWidget.qml + res/Custom/Widgets/CustomComboBox.qml + res/Custom/Widgets/CustomIconButton.qml + res/Custom/Widgets/CustomOnOffSwitch.qml + res/Custom/Widgets/CustomSignalStrength.qml + res/Custom/Widgets/CustomToolBarButton.qml + res/Custom/Widgets/CustomVehicleButton.qml + res/Custom/Widgets/qmldir + + diff --git a/custom-example/res/Custom/Widgets/CustomArtificialHorizon.qml b/custom-example/res/Custom/Widgets/CustomArtificialHorizon.qml new file mode 100644 index 000000000..cb024582b --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomArtificialHorizon.qml @@ -0,0 +1,68 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 + +Item { + id: root + property real rollAngle : 0 + property real pitchAngle: 0 + property color skyColor1: Qt.hsla(0.6, 1.0, 0.25) + property color skyColor2: Qt.hsla(0.6, 0.5, 0.55) + property color groundColor1: Qt.hsla(0.25, 0.5, 0.45) + property color groundColor2: Qt.hsla(0.25, 0.75, 0.25) + + clip: true + anchors.fill: parent + + property real angularScale: pitchAngle * root.height / 45 + + Item { + id: artificialHorizon + width: root.width * 4 + height: root.height * 8 + anchors.centerIn: parent + Rectangle { + id: sky + anchors.fill: parent + smooth: true + antialiasing: true + gradient: Gradient { + GradientStop { position: 0.25; color: root.skyColor1 } + GradientStop { position: 0.5; color: root.skyColor2 } + } + } + Rectangle { + id: ground + height: sky.height / 2 + anchors { + left: sky.left; + right: sky.right; + bottom: sky.bottom + } + smooth: true + antialiasing: true + gradient: Gradient { + GradientStop { position: 0.0; color: root.groundColor1 } + GradientStop { position: 0.25; color: root.groundColor2 } + } + } + transform: [ + Translate { + y: angularScale + }, + Rotation { + origin.x: artificialHorizon.width / 2 + origin.y: artificialHorizon.height / 2 + angle: -rollAngle + }] + } +} diff --git a/custom-example/res/Custom/Widgets/CustomAttitudeWidget.qml b/custom-example/res/Custom/Widgets/CustomAttitudeWidget.qml new file mode 100644 index 000000000..c8ab5ed31 --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomAttitudeWidget.qml @@ -0,0 +1,138 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtGraphicalEffects 1.0 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.FlightMap 1.0 + +Item { + id: root + + property bool showPitch: true + property var vehicle: null + property real size + property bool showHeading: false + + property real _rollAngle: vehicle ? vehicle.roll.rawValue : 0 + property real _pitchAngle: vehicle ? vehicle.pitch.rawValue : 0 + + width: size + height: size + + Item { + id: instrument + anchors.fill: parent + visible: false + + //---------------------------------------------------- + //-- Artificial Horizon + CustomArtificialHorizon { + rollAngle: _rollAngle + pitchAngle: _pitchAngle + skyColor1: "#0a2e50" + skyColor2: "#2f85d4" + groundColor1: "#897459" + groundColor2: "#4b3820" + anchors.fill: parent + } + //---------------------------------------------------- + //-- Instrument Dial + Image { + id: instrumentDial + source: "/custom/img/attitude_dial.svg" + mipmap: true + fillMode: Image.PreserveAspectFit + anchors.fill: parent + sourceSize.height: parent.height + transform: Rotation { + origin.x: root.width / 2 + origin.y: root.height / 2 + angle: -_rollAngle + } + } + //---------------------------------------------------- + //-- Pointer + Image { + id: pointer + height: size * 0.0625 + width: height + source: "/custom/img/attitude_pointer.svg" + antialiasing: true + fillMode: Image.PreserveAspectFit + sourceSize.height: height + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + } + //---------------------------------------------------- + //-- Pitch + QGCPitchIndicator { + id: pitchWidget + visible: root.showPitch + size: root.size * 0.5 + anchors.verticalCenter: parent.verticalCenter + pitchAngle: _pitchAngle + rollAngle: _rollAngle + color: Qt.rgba(0,0,0,0) + } + //---------------------------------------------------- + //-- Cross Hair + Image { + id: crossHair + anchors.centerIn: parent + source: "/custom/img/attitude_crosshair.svg" + mipmap: true + width: size * 0.75 + sourceSize.width: width + fillMode: Image.PreserveAspectFit + } + } + + Rectangle { + id: mask + anchors.fill: instrument + radius: width / 2 + color: "black" + visible: false + } + + OpacityMask { + anchors.fill: instrument + source: instrument + maskSource: mask + } + + Rectangle { + id: borderRect + anchors.fill: parent + radius: width / 2 + color: Qt.rgba(0,0,0,0) + border.color: "#000" + border.width: 1 + } + + QGCLabel { + anchors.bottomMargin: Math.round(ScreenTools.defaultFontPixelHeight * 0.5) + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: _headingString3 + color: "white" + visible: showHeading + font.pointSize: ScreenTools.smallFontPointSize + property string _headingString: vehicle ? vehicle.heading.rawValue.toFixed(0) : "OFF" + property string _headingString2: _headingString.length === 1 ? "0" + _headingString : _headingString + property string _headingString3: _headingString2.length === 2 ? "0" + _headingString2 : _headingString2 + } +} diff --git a/custom-example/res/Custom/Widgets/CustomComboBox.qml b/custom-example/res/Custom/Widgets/CustomComboBox.qml new file mode 100644 index 000000000..8eeb161ec --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomComboBox.qml @@ -0,0 +1,252 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Button { + id: combo + + property real pointSize: ScreenTools.defaultFontPointSize ///< Point size for button text + property bool centeredLabel: false + property alias model: popupItems.model + property alias textRole: popup.textRole + property alias currentIndex: popup.__selectedIndex + + readonly property alias count: popupItems.count + readonly property alias currentText: popup.currentText + + property var _qgcPal: QGCPalette { colorGroupEnabled: enabled } + property int _verticalPadding: Math.round(ScreenTools.defaultFontPixelHeight * 0.5) + property real _dropImageWidth: ScreenTools.defaultFontPixelHeight * 0.5 + property real _dropImageMargin: _dropImageWidth * 0.5 + property var __popup: popup + + signal activated(int index) + + style: ButtonStyle { + /*! This defines the background of the button. */ + background: Rectangle { + implicitWidth: Math.round(ScreenTools.defaultFontPixelWidth * 6) + implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight) + color: Qt.rgba(0,0,0,0) + /* + Image { + id: image + width: _dropImageWidth + height: _dropImageWidth + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: _dropImageMargin + anchors.right: parent.right + source: "/custom/img/menu_dropdown.svg" + } + */ + } + + /*! This defines the label of the button. */ + label: Item { + implicitWidth: text.implicitWidth + implicitHeight: text.implicitHeight + baselineOffset: text.y + text.baselineOffset + QGCLabel { + id: text + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: centeredLabel ? parent.horizontalCenter : undefined + text: control.currentText + color: "#FFF" + font.pointSize: combo.pointSize + } + } + } + + onClicked: { + combo.focus = true + popup.toggleShow() + } + + Component.onCompleted: { + if (currentIndex === -1) { + currentIndex = 0 + } + popup.resolveTextValue(textRole) + } + + function textAt (index) { + if (index >= count || index < 0) + return null; + return popupItems.objectAt(index).text; + } + + function find (text) { + for (var i = 0 ; i < popupItems.count ; ++i) { + var currentString = popupItems.objectAt(i).text + if (text === currentString) { + return i + } + } + return -1 + } + + ExclusiveGroup { id: eg } + + Menu { + id: popup + __minimumWidth: combo.width + __visualItem: combo + + style: MenuStyle { + font.pointSize: combo.pointSize + font.family: ScreenTools.normalFontFamily + __labelColor: combo._qgcPal.buttonText + __selectedLabelColor: combo._qgcPal.buttonHighlightText + __selectedBackgroundColor: combo._qgcPal.buttonHighlight + __backgroundColor: combo._qgcPal.button + __maxPopupHeight: 600 + __menuItemType: "comboboxitem" + __scrollerStyle: ScrollViewStyle { } + } + + property string textRole: "" + property bool showing: false + + property string currentText: selectedText + property string selectedText + + onSelectedTextChanged: popup.currentText = selectedText + + on__SelectedIndexChanged: { + if (__selectedIndex === -1) + popup.currentText = "" + else + updateSelectedText() + } + + property int _y: combo.height + property bool _modelIsArray: false + + onAboutToShow: showing = true + onAboutToHide: showing = false + + function toggleShow() { + if (popup._popupVisible) { + popup.__dismissAndDestroy() + } else { + __popup(Qt.rect(0, _y, 0, 0), 0) + } + } + + function resolveTextValue(initialTextRole) { + if (!model) { + return + } + + var get = model['get']; + if (!get && popup._modelIsArray && !!model[0]) { + if (model[0].constructor !== String && model[0].constructor !== Number) + get = function(i) { return model[i]; } + } + + var modelMayHaveRoles = get !== undefined + textRole = initialTextRole + if (textRole === "" && modelMayHaveRoles && get(0)) { + // No text role set, check whether model has a suitable role + // If 'text' is found, or there's only one role, pick that. + var listElement = get(0) + var roleName = "" + var roleCount = 0 + for (var role in listElement) { + if (listElement[role].constructor === Function) + continue; + if (role === "text") { + roleName = role + break + } else if (!roleName) { + roleName = role + } + ++roleCount + } + if (roleCount > 1 && roleName !== "text") { + console.warn("No suitable 'textRole' found for ComboBox.") + } else { + textRole = roleName + } + } + updateSelectedText() + } + + function updateSelectedText() { + var selectedItem + if (__selectedIndex !== -1 && (selectedItem = items[__selectedIndex])) { + selectedText = Qt.binding(function () { return selectedItem.text }) + if (currentText !== selectedText) // __selectedIndex went form -1 to 0 + selectedTextChanged() + } + } + + Instantiator { + id: popupItems + + onModelChanged: { + popup._modelIsArray = !!model ? model.constructor === Array : false + popup.resolveTextValue(popup.textRole) + } + + onObjectAdded: { + // There is a bug in Instantiator which can cause objects to be added out of order from an index standpoint. + // If not handled correctly this will cause menu items to be added incorrectly due to the way Menu.insertItem works. + //console.log("menu add", index, object.text) + if (index === popup.__selectedIndex) { + popup.selectedText = object["text"] + } + + // Find the correct place for menu item. We can't just add at index, due to possible bad ordering + var insertIndex = -1 + for (var i=0; i index) { + insertIndex = i + break + } + } + if (insertIndex === -1) { + popup.insertItem(popup.items.length, object) + } else { + //console.log("out of order menu add", index, insertIndex) + popup.insertItem(insertIndex, object) + } + } + + onObjectRemoved: popup.removeItem(object) + + MenuItem { + text: popup.textRole === '' ? modelData : ((popup._modelIsArray ? modelData[popup.textRole] : model[popup.textRole]) || '') + checked: index == currentIndex + checkable: true + exclusiveGroup: eg + + property int itemIndex: index + + onTriggered: { + //console.log("onTriggered", index, currentIndex) + if (index !== currentIndex) { + //console.log("activated", index) + activated(index) + } + } + } + } + } +} diff --git a/custom-example/res/Custom/Widgets/CustomIconButton.qml b/custom-example/res/Custom/Widgets/CustomIconButton.qml new file mode 100644 index 000000000..c6c906d60 --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomIconButton.qml @@ -0,0 +1,63 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 +import QtGraphicalEffects 1.0 + +Button { + id: _rootButton + width: parent.height * 1.25 + height: parent.height + flat: true + contentItem: Item { + id: _content + anchors.fill: _rootButton + Row { + id: _edge + spacing: ScreenTools.defaultFontPixelWidth * 0.25 + anchors.left: parent.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: parent.verticalCenter + Repeater { + model: [1,2,3] + Rectangle { + height: ScreenTools.defaultFontPixelHeight + width: ScreenTools.defaultFontPixelWidth * 0.25 + color: qgcPal.text + opacity: 0.75 + } + } + } + Image { + id: _icon + height: _rootButton.height * 0.75 + width: height + smooth: true + mipmap: true + antialiasing: true + fillMode: Image.PreserveAspectFit + source: "/res/QGCLogoWhite" + sourceSize.height: height + anchors.left: _edge.right + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: parent.verticalCenter + } + } + background: Item { + anchors.fill: parent + } +} diff --git a/custom-example/res/Custom/Widgets/CustomOnOffSwitch.qml b/custom-example/res/Custom/Widgets/CustomOnOffSwitch.qml new file mode 100644 index 000000000..9c9a86693 --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomOnOffSwitch.qml @@ -0,0 +1,68 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.3 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 + +Rectangle { + id: _root + height: Math.round(ScreenTools.defaultFontPixelHeight * 2) + width: ScreenTools.defaultFontPixelWidth * 10 + color: qgcPal.button + border.color: qgcPal.text + border.width: 1 + + property bool checked: true + + signal clicked + + QGCPalette { id: qgcPal; colorGroupEnabled: true } + + Rectangle { + width: parent.width * 0.5 + height: parent.height + color: qgcPal.windowShade + visible: !checked + border.color: qgcPal.text + border.width: 1 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + QGCLabel { + text: qsTr("Off") + anchors.centerIn: parent + } + } + Rectangle { + width: parent.width * 0.5 + height: parent.height * 0.95 + color: qgcPal.buttonHighlight + visible: checked + border.color: qgcPal.text + border.width: 1 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + QGCLabel { + text: qsTr("On") + color: qgcPal.buttonHighlightText + anchors.centerIn: parent + } + } + MouseArea { + anchors.fill: parent + onClicked: { + checked = !checked + _root.clicked() + } + } +} diff --git a/custom-example/res/Custom/Widgets/CustomSignalStrength.qml b/custom-example/res/Custom/Widgets/CustomSignalStrength.qml new file mode 100644 index 000000000..e290f4a68 --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomSignalStrength.qml @@ -0,0 +1,48 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Item { + width: size + height: size + + property real size: 50 + property real percent: 0 + + QGCPalette { id: qgcPal } + + function getIcon() { + if (percent < 20) + return "/custom/img/menu_signal_0.svg" + if (percent < 40) + return "/custom/img/menu_signal_25.svg" + if (percent < 60) + return "/custom/img/menu_signal_50.svg" + if (percent < 90) + return "/custom/img/menu_signal_75.svg" + return "/custom/img/menu_signal_100.svg" + } + + QGCColoredImage { + source: getIcon() + fillMode: Image.PreserveAspectFit + anchors.fill: parent + sourceSize.height: size + color: qgcPal.text + } +} diff --git a/custom-example/res/Custom/Widgets/CustomToolBarButton.qml b/custom-example/res/Custom/Widgets/CustomToolBarButton.qml new file mode 100644 index 000000000..e46b4e2b9 --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomToolBarButton.qml @@ -0,0 +1,61 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Button { + id: button + autoExclusive: true + + background: Rectangle { + anchors.fill: parent + color: mouseArea.pressed ? qgcPal.buttonHighlight : Qt.rgba(0,0,0,0) + } + + contentItem: Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.left: button.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: button.verticalCenter + Item { + height: ScreenTools.defaultFontPixelHeight * 3 + width: 1 + } + QGCColoredImage { + id: _icon + height: ScreenTools.defaultFontPixelHeight + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + color: (mouseArea.pressed || button.checked) ? qgcPal.buttonHighlightText : qgcPal.buttonText + source: button.icon.source + anchors.verticalCenter: parent.verticalCenter + } + Label { + id: _label + visible: text !== "" + text: button.text + color: (mouseArea.pressed || button.checked) ? qgcPal.buttonHighlightText : qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + } + } + // Process hover events + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: button.clicked() + } +} diff --git a/custom-example/res/Custom/Widgets/CustomVehicleButton.qml b/custom-example/res/Custom/Widgets/CustomVehicleButton.qml new file mode 100644 index 000000000..bc1d9609e --- /dev/null +++ b/custom-example/res/Custom/Widgets/CustomVehicleButton.qml @@ -0,0 +1,98 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Button { + id: button + height: _infoCol.height * 1.25 + autoExclusive: true + + property var vehicle: null + + function getBatteryColor() { + if(vehicle) { + if(vehicle.battery.percentRemaining.value > 75) { + return qgcPal.colorGreen + } + if(vehicle.battery.percentRemaining.value > 50) { + return qgcPal.colorOrange + } + if(vehicle.battery.percentRemaining.value > 0.1) { + return qgcPal.colorRed + } + } + return qgcPal.colorGrey + } + + function getBatteryPercentage() { + if(vehicle) { + return vehicle.battery.percentRemaining.value / 100.0 + } + return 1 + } + + background: Rectangle { + anchors.fill: parent + color: button.checked ? qgcPal.buttonHighlight : qgcPal.button + radius: ScreenTools.defaultFontPixelWidth * 0.5 + } + + contentItem: Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: button.verticalCenter + QGCColoredImage { + id: _icon + height: ScreenTools.defaultFontPixelHeight * 1.5 + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + color: qgcPal.buttonText + source: "/qmlimages/PaperPlane.svg" + anchors.verticalCenter: parent.verticalCenter + } + Column { + id: _infoCol + spacing: ScreenTools.defaultFontPixelHeight * 0.25 + QGCLabel { + text: qsTr("Vehicle ") + (vehicle ? vehicle.id : qsTr("None")) + font.family: ScreenTools.demiboldFontFamily + color: qgcPal.buttonText + } + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: vehicle ? vehicle.flightMode : qsTr("None") + color: qgcPal.buttonText + } + Rectangle { + height: ScreenTools.defaultFontPixelHeight * 0.5 + width: ScreenTools.defaultFontPixelWidth * 3 + color: Qt.rgba(0,0,0,0) + anchors.verticalCenter: parent.verticalCenter + Rectangle { + height: parent.height + width: parent.width * getBatteryPercentage() + color: getBatteryColor() + anchors.right: parent.right + } + } + } + } + } + +} diff --git a/custom-example/res/Custom/Widgets/qmldir b/custom-example/res/Custom/Widgets/qmldir new file mode 100644 index 000000000..9c838de0b --- /dev/null +++ b/custom-example/res/Custom/Widgets/qmldir @@ -0,0 +1,10 @@ +Module Custom.Widgets + +CustomArtificialHorizon 1.0 CustomArtificialHorizon.qml +CustomAttitudeWidget 1.0 CustomAttitudeWidget.qml +CustomComboBox 1.0 CustomComboBox.qml +CustomIconButton 1.0 CustomIconButton.qml +CustomOnOffSwitch 1.0 CustomOnOffSwitch.qml +CustomSignalStrength 1.0 CustomSignalStrength.qml +CustomToolBarButton 1.0 CustomToolBarButton.qml +CustomVehicleButton 1.0 CustomVehicleButton.qml \ No newline at end of file diff --git a/custom-example/res/CustomCameraControl.qml b/custom-example/res/CustomCameraControl.qml new file mode 100644 index 000000000..b4c6d03c1 --- /dev/null +++ b/custom-example/res/CustomCameraControl.qml @@ -0,0 +1,717 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Dialogs 1.3 + +import QtMultimedia 5.9 +import QtPositioning 5.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FlightMap 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 + +import CustomQuickInterface 1.0 +import Custom.Widgets 1.0 + +Item { + height: cameraRect.height + width: cameraRect.width + (ScreenTools.defaultFontPixelWidth * 2) + visible: !QGroundControl.videoManager.fullScreen + + readonly property string _commLostStr: qsTr("NO CAMERA") + + property real _spacers: ScreenTools.defaultFontPixelHeight + property real _labelFieldWidth: ScreenTools.defaultFontPixelWidth * 28 + property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 + + property var _dynamicCameras: activeVehicle ? activeVehicle.dynamicCameras : null + property bool _isCamera: _dynamicCameras ? _dynamicCameras.cameras.count > 0 : false + property int _curCameraIndex: _dynamicCameras ? _dynamicCameras.currentCamera : 0 + property var _camera: _isCamera ? _dynamicCameras.cameras.get(_curCameraIndex) : null + property bool _communicationLost: activeVehicle ? activeVehicle.connectionLost : false + property bool _noSdCard: _camera && _camera.storageTotal === 0 + property bool _fullSD: _camera && _camera.storageTotal !== 0 && _camera.storageFree > 0 && _camera.storageFree < 250 // We get kiB from the camera + property bool _cameraVideoMode: !_communicationLost && (_noSdCard ? false : _camera && _camera.cameraMode === QGCCameraControl.CAM_MODE_VIDEO) + property bool _cameraPhotoMode: !_communicationLost && (_noSdCard ? false : _camera && (_camera.cameraMode === QGCCameraControl.CAM_MODE_PHOTO || _camera.cameraMode === QGCCameraControl.CAM_MODE_SURVEY)) + property bool _cameraPhotoIdle: !_communicationLost && (_noSdCard ? false : _camera && _camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_IDLE) + property bool _cameraElapsedMode: !_communicationLost && (_noSdCard ? false : _camera && _camera.cameraMode === QGCCameraControl.CAM_MODE_PHOTO && _camera.photoMode === QGCCameraControl.PHOTO_CAPTURE_TIMELAPSE) + property bool _cameraModeUndefined: !_cameraPhotoMode && !_cameraVideoMode + property bool _recordingVideo: _cameraVideoMode && _camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING + property bool _settingsEnabled: !_communicationLost && _camera && _camera.cameraMode !== QGCCameraControl.CAM_MODE_UNDEFINED && _camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_IDLE && !_recordingVideo + property bool _hasZoom: _camera && _camera.hasZoom + property Fact _evFact: _camera ? _camera.ev : null + property Fact _irPaletteFact: _camera ? _camera.irPalette : null + + Connections { + target: QGroundControl.multiVehicleManager.activeVehicle + onConnectionLostChanged: { + if(_communicationLost && cameraSettings.visible) { + cameraSettings.close() + } + } + } + + DeadMouseArea { + anchors.fill: parent + } + + Rectangle { + id: cameraRect + height: cameraCol.height + width: cameraCol.width + (ScreenTools.defaultFontPixelWidth * 4) + color: qgcPal.windowShade + radius: ScreenTools.defaultFontPixelWidth * 0.5 + Column { + id: cameraCol + spacing: _spacers + anchors.centerIn: parent + Item { + height: 1 + width: 1 + } + //----------------------------------------------------------------- + //-- Camera Name + QGCLabel { + text: activeVehicle ? (_camera && _camera.modelName !== "" ? _camera.modelName : _commLostStr) : _commLostStr + font.pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + //----------------------------------------------------------------- + //-- Camera Mode + Item { + width: modeCol.width + height: modeCol.height + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: modeCol + spacing: _spacers * 0.5 + QGCColoredImage { + height: ScreenTools.defaultFontPixelHeight * 1.25 + width: height + source: (_cameraModeUndefined || _cameraPhotoMode) ? "/custom/img/camera_photo.svg" : "/custom/img/camera_video.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + sourceSize.height: height + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: _cameraVideoMode ? qsTr("Video") : qsTr("Photo") + font.pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + } + MouseArea { + anchors.fill: parent + enabled: !_cameraModeUndefined && _camera && _camera.videoStatus !== QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING && _cameraPhotoIdle + onClicked: { + _camera.toggleMode() + } + } + } + //----------------------------------------------------------------- + //-- Shutter + Rectangle { + color: Qt.rgba(0,0,0,0) + width: height + height: ScreenTools.defaultFontPixelHeight * 4 + radius: width * 0.5 + border.color: qgcPal.buttonText + border.width: 2 + anchors.horizontalCenter: parent.horizontalCenter + Rectangle { + width: parent.width * 0.75 + height: width + radius: width * 0.5 + color: _cameraModeUndefined ? qgcPal.colorGrey : ( _cameraVideoMode ? qgcPal.colorRed : qgcPal.text ) + visible: !pauseVideo.visible + anchors.centerIn: parent + QGCColoredImage { + id: busyIndicator + height: parent.height * 0.75 + width: height + source: "/qmlimages/MapSync.svg" + sourceSize.height: height + fillMode: Image.PreserveAspectFit + mipmap: true + smooth: true + color: qgcPal.windowShade + visible: { + if(_cameraPhotoMode && !_cameraPhotoIdle && !_cameraElapsedMode) { + return true + } + return false + } + anchors.centerIn: parent + RotationAnimation on rotation { + loops: Animation.Infinite + from: 360 + to: 0 + duration: 740 + running: busyIndicator.visible + } + } + QGCLabel { + text: _camera ? _camera.photoLapse.toFixed(0) + 's' : qsTr('N/A') + font.family: ScreenTools.demiboldFontFamily + color: qgcPal.colorBlue + visible: _cameraElapsedMode + anchors.centerIn: parent + } + } + Rectangle { + id: pauseVideo + width: parent.width * 0.5 + height: width + color: _cameraModeUndefined ? qgcPal.colorGrey : qgcPal.colorRed + visible: { + if(_cameraVideoMode && _camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) { + return true + } + if(_cameraPhotoMode) { + if(_camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_INTERVAL_IDLE || _camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_INTERVAL_IN_PROGRESS) { + return true + } + } + return false + } + anchors.centerIn: parent + } + MouseArea { + anchors.fill: parent + enabled: !_noSdCard + onClicked: { + if(_cameraVideoMode) { + if(_camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) { + _camera.stopVideo() + } else { + if(!_fullSD) { + _camera.startVideo() + } + } + } else { + if(_camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_INTERVAL_IDLE || _camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_INTERVAL_IN_PROGRESS) { + _camera.stopTakePhoto() + } else { + if(!_fullSD) { + _camera.takePhoto() + } + } + } + } + } + } + //----------------------------------------------------------------- + //-- Settings + Item { + width: settingsCol.width + height: settingsCol.height + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: settingsCol + spacing: _spacers * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + QGCColoredImage { + width: ScreenTools.defaultFontPixelHeight * 1.25 + height: width + sourceSize.width: width + source: "qrc:/custom/img/camera_settings.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + opacity: _settingsEnabled ? 1 : 0.5 + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: qsTr("Settings") + font.pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + } + MouseArea { + anchors.fill: parent + enabled: _settingsEnabled + onClicked: { + cameraSettings.open() + } + } + } + //----------------------------------------------------------------- + //-- microSD Card + Column { + spacing: _spacers * 0.5 + anchors.horizontalCenter: parent.horizontalCenter + QGCColoredImage { + width: ScreenTools.defaultFontPixelHeight * 1.25 + height: width + sourceSize.width: width + source: "qrc:/custom/img/microSD.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + opacity: _settingsEnabled ? 1 : 0.5 + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: { + if(_noSdCard) return qsTr("NONE") + if(_fullSD) return qsTr("FULL") + return _camera ? _camera.storageFreeStr : "" + } + color: (_noSdCard || _fullSD) ? qgcPal.colorOrange : qgcPal.text + font.pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + } + /* + //----------------------------------------------------------------- + //-- Recording Time / Images Captured + CustomLabel { + text: (_cameraVideoMode && _camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) ? _camera.recordTimeStr : "00:00:00" + visible: _cameraVideoMode + pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + CustomLabel { + text: activeVehicle && _cameraPhotoMode ? ('00000' + activeVehicle.cameraTriggerPoints.count).slice(-5) : "00000" + visible: _cameraPhotoMode + pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + */ + Item { + height: 1 + width: 1 + } + } + } + + //------------------------------------------------------------------------- + //-- Camera Settings + Popup { + id: cameraSettings + width: Math.min(mainWindow.width * 0.666, ScreenTools.defaultFontPixelWidth * 80) + height: mainWindow.height * 0.666 + modal: true + focus: true + parent: Overlay.overlay + x: Math.round((mainWindow.width - width) * 0.5) + y: Math.round((mainWindow.height - height) * 0.5) + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + background: Rectangle { + anchors.fill: parent + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(1,1,1,0.95) : Qt.rgba(0,0,0,0.75) + border.color: qgcPal.text + radius: ScreenTools.defaultFontPixelWidth + } + Item { + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelHeight + function showEditFact(fact) { + factEditor.text = fact.valueString + factEdit.fact = fact + factEdit.visible = true + } + function hideEditFact() { + factEdit.visible = false + factEdit.fact = null + } + QGCLabel { + id: cameraSettingsLabel + text: _noSdCard ? qsTr("Settings") : (_cameraVideoMode ? qsTr("Video Settings") : qsTr("Photo Settings")) + font.family: ScreenTools.demiboldFontFamily + font.pointSize: ScreenTools.mediumFontPointSize + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.top: parent.top + anchors.left: parent.left + } + QGCFlickable { + clip: true + anchors.top: cameraSettingsLabel.bottom + anchors.bottom: parent.bottom + anchors.margins: ScreenTools.defaultFontPixelWidth + width: cameraSettingsCol.width + (ScreenTools.defaultFontPixelWidth * 2) + contentHeight: cameraSettingsCol.height + contentWidth: cameraSettingsCol.width + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: cameraSettingsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.horizontalCenter: parent.horizontalCenter + //------------------------------------------- + //-- Camera Selector + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: _isCamera && _dynamicCameras.cameraLabels.length > 1 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: qsTr("Camera Selector:") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCComboBox { + model: _isCamera ? _dynamicCameras.cameraLabels : [] + width: _editFieldWidth + onActivated: _dynamicCameras.currentCamera = index + currentIndex: _dynamicCameras ? _dynamicCameras.currentCamera : 0 + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _isCamera && _dynamicCameras.cameraLabels.length > 1 + } + //------------------------------------------- + //-- Stream Selector + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: _isCamera && _camera.streamLabels.length > 1 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: qsTr("Stream Selector:") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCComboBox { + model: _camera ? _camera.streamLabels : [] + width: _editFieldWidth + onActivated: _camera.currentStream = index + currentIndex: _camera ? _camera.currentStream : 0 + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _isCamera && _camera.streamLabels.length > 1 + } + //------------------------------------------- + //-- Thermal Modes + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.videoManager.hasThermal + property var thermalModes: [qsTr("Off"), qsTr("Blend"), qsTr("Full"), qsTr("Picture In Picture")] + QGCLabel { + text: qsTr("Thermal View Mode") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCComboBox { + width: _editFieldWidth + model: parent.thermalModes + currentIndex: _camera ? _camera.thermalMode : 0 + onActivated: _camera.thermalMode = index + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: QGroundControl.videoManager.hasThermal + } + //------------------------------------------- + //-- Thermal Video Opacity + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.videoManager.hasThermal && _camera.thermalMode === QGCCameraControl.THERMAL_BLEND + QGCLabel { + text: qsTr("Blend Opacity") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + Slider { + width: _editFieldWidth + to: 100 + from: 0 + value: _camera ? _camera.thermalOpacity : 0 + live: true + onValueChanged: { + _camera.thermalOpacity = value + } + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: QGroundControl.videoManager.hasThermal && _camera.thermalMode === QGCCameraControl.THERMAL_BLEND + } + //------------------------------------------- + //-- Settings from Camera Definition File + Repeater { + model: _camera ? _camera.activeSettings : [] + Item { + width: repCol.width + height: repCol.height + Column { + id: repCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + property var _fact: _camera.getFact(modelData) + Row { + height: visible ? undefined : 0 + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + property bool _isBool: parent._fact.typeIsBool + property bool _isCombo: !_isBool && parent._fact.enumStrings.length > 0 + property bool _isSlider: parent._fact && !isNaN(parent._fact.increment) + property bool _isEdit: !_isBool && !_isSlider && parent._fact.enumStrings.length < 1 + QGCLabel { + text: parent.parent._fact.shortDescription + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + FactComboBox { + width: parent._isCombo ? _editFieldWidth : 0 + fact: parent.parent._fact + indexModel: false + visible: parent._isCombo + anchors.verticalCenter: parent.verticalCenter + } + QGCButton { + visible: parent._isEdit + width: parent._isEdit ? _editFieldWidth : 0 + text: parent.parent._fact.valueString + onClicked: { + showEditFact(parent.parent._fact) + } + } + QGCSlider { + width: parent._isSlider ? _editFieldWidth : 0 + maximumValue: parent.parent._fact.max + minimumValue: parent.parent._fact.min + stepSize: parent.parent._fact.increment + visible: parent._isSlider + updateValueWhileDragging: false + anchors.verticalCenter: parent.verticalCenter + Component.onCompleted: { + value = parent.parent._fact.value + } + onValueChanged: { + parent.parent._fact.value = value + } + } + CustomOnOffSwitch { + width: parent._isBool ? _editFieldWidth : 0 + checked: parent.parent._fact ? parent.parent._fact.value : false + onClicked: parent.parent._fact.value = checked ? 1 : 0 + visible: parent._isBool + anchors.verticalCenter: parent.verticalCenter + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + } + } + } + } + //------------------------------------------- + //-- Time Lapse + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _cameraPhotoMode && !_noSdCard + property var photoModes: [qsTr("Single"), qsTr("Time Lapse")] + QGCLabel { + text: qsTr("Photo Mode") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCComboBox { + width: _editFieldWidth + model: parent.photoModes + currentIndex: _camera ? _camera.photoMode : 0 + onActivated: _camera.photoMode = index + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _cameraPhotoMode && !_noSdCard + } + //------------------------------------------- + //-- Time Lapse Interval + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _cameraPhotoMode && _camera.photoMode === QGCCameraControl.PHOTO_CAPTURE_TIMELAPSE && !_noSdCard + QGCLabel { + text: qsTr("Photo Interval (seconds)") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCSlider { + width: _editFieldWidth + maximumValue: 60 + minimumValue: _camera ? (_camera.isE90 ? 3 : 5) : 5 + stepSize: 1 + value: _camera ? _camera.photoLapse : 5 + updateValueWhileDragging: true + anchors.verticalCenter: parent.verticalCenter + onValueChanged: { + if(_camera) { + _camera.photoLapse = value + } + } + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _cameraPhotoMode && _camera.photoMode === QGCCameraControl.PHOTO_CAPTURE_TIMELAPSE && !_noSdCard + } + //------------------------------------------- + //-- Screen Grid + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: _camera && !_camera.isThermal + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: qsTr("Screen Grid") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + CustomOnOffSwitch { + checked: QGroundControl.settingsManager.videoSettings.gridLines.rawValue + width: _editFieldWidth + anchors.verticalCenter: parent.verticalCenter + onClicked: QGroundControl.settingsManager.videoSettings.gridLines.rawValue = checked + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _camera && !_camera.isThermal + } + //------------------------------------------- + //-- Video Fit + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: _camera + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: qsTr("Video Screen Fit") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + FactComboBox { + width: _editFieldWidth + fact: QGroundControl.settingsManager.videoSettings.videoFit + indexModel: false + anchors.verticalCenter: parent.verticalCenter + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + visible: _camera && !_camera.isThermal + } + //------------------------------------------- + //-- Reset Camera + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: qsTr("Reset Camera Defaults") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCButton { + text: qsTr("Reset") + onClicked: resetPrompt.open() + width: _editFieldWidth + enabled: !_recordingVideo + anchors.verticalCenter: parent.verticalCenter + MessageDialog { + id: resetPrompt + title: qsTr("Reset Camera to Factory Settings") + text: qsTr("Confirm resetting all settings?") + standardButtons: StandardButton.Yes | StandardButton.No + onNo: resetPrompt.close() + onYes: { + _camera.resetSettings() + QGroundControl.settingsManager.videoSettings.gridLines.rawValue = false + _camera.photoMode = QGCCameraControl.PHOTO_CAPTURE_SINGLE + _camera.photoLapse = 5.0 + _camera.photoLapseCount = 0 + resetPrompt.close() + } + } + } + } + Rectangle { + color: qgcPal.button + height: 1 + width: cameraSettingsCol.width + } + } + } + Rectangle { + id: factEdit + visible: false + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(1,1,1,0.5) : Qt.rgba(0,0,0,0.5) + anchors.fill: parent + property var fact: null + DeadMouseArea { + anchors.fill: parent + } + Rectangle { + width: factEditCol.width * 1.25 + height: factEditCol.height * 1.25 + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(1,1,1,0.95) : Qt.rgba(0,0,0,0.75) + border.width: 1 + border.color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(0,0,0,0.35) : Qt.rgba(1,1,1,0.35) + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * 8 + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: factEditCol + spacing: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + QGCLabel { + text: factEdit.fact ? factEdit.fact.shortDescription : "" + anchors.horizontalCenter: parent.horizontalCenter + } + FactTextField { + id: factEditor + width: _editFieldWidth + fact: factEdit.fact + anchors.horizontalCenter: parent.horizontalCenter + } + QGCButton { + text: qsTr("Close") + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + factEditor.completeEditing() + hideEditFact() + } + } + } + } + } + } + } +} + diff --git a/custom-example/res/CustomFlyView.qml b/custom-example/res/CustomFlyView.qml new file mode 100644 index 000000000..80ec26b83 --- /dev/null +++ b/custom-example/res/CustomFlyView.qml @@ -0,0 +1,555 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtQuick.Dialogs 1.3 +import QtPositioning 5.2 + +import QGroundControl 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FlightMap 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.QGCPositionManager 1.0 +import QGroundControl.Airspace 1.0 + +import CustomQuickInterface 1.0 +import Custom.Widgets 1.0 + +Item { + anchors.fill: parent + visible: !QGroundControl.videoManager.fullScreen + + readonly property string scaleState: "topMode" + readonly property string noGPS: qsTr("NO GPS") + readonly property real indicatorValueWidth: ScreenTools.defaultFontPixelWidth * 7 + + property real _indicatorDiameter: ScreenTools.defaultFontPixelWidth * 18 + property real _indicatorsHeight: ScreenTools.defaultFontPixelHeight + property var _sepColor: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(0,0,0,0.5) : Qt.rgba(1,1,1,0.5) + property color _indicatorsColor: qgcPal.text + + property bool _communicationLost: activeVehicle ? activeVehicle.connectionLost : false + property bool _isVehicleGps: activeVehicle && activeVehicle.gps && activeVehicle.gps.count.rawValue > 1 && activeVehicle.gps.hdop.rawValue < 1.4 + property var _dynamicCameras: activeVehicle ? activeVehicle.dynamicCameras : null + property bool _isCamera: _dynamicCameras ? _dynamicCameras.cameras.count > 0 : false + property int _curCameraIndex: _dynamicCameras ? _dynamicCameras.currentCamera : 0 + property var _camera: _isCamera ? _dynamicCameras.cameras.get(_curCameraIndex) : null + property bool _cameraPresent: _camera && _camera.cameraMode !== QGCCameraControl.CAM_MODE_UNDEFINED + property var _flightPermit: QGroundControl.airmapSupported ? QGroundControl.airspaceManager.flightPlan.flightPermitStatus : null + + property bool _airspaceIndicatorVisible: QGroundControl.airmapSupported && mainIsMap && _flightPermit && _flightPermit !== AirspaceFlightPlanProvider.PermitNone + + property string _altitude: activeVehicle ? (isNaN(activeVehicle.altitudeRelative.value) ? "0.0" : activeVehicle.altitudeRelative.value.toFixed(1)) + ' ' + activeVehicle.altitudeRelative.units : "0.0" + property string _distanceStr: isNaN(_distance) ? "0" : _distance.toFixed(0) + ' ' + (activeVehicle ? activeVehicle.altitudeRelative.units : "") + property real _heading: activeVehicle ? activeVehicle.heading.rawValue : 0 + + property real _distance: 0.0 + property string _messageTitle: "" + property string _messageText: "" + property bool _showAttitude: false + + function secondsToHHMMSS(timeS) { + var sec_num = parseInt(timeS, 10); + var hours = Math.floor(sec_num / 3600); + var minutes = Math.floor((sec_num - (hours * 3600)) / 60); + var seconds = sec_num - (hours * 3600) - (minutes * 60); + if (hours < 10) {hours = "0"+hours;} + if (minutes < 10) {minutes = "0"+minutes;} + if (seconds < 10) {seconds = "0"+seconds;} + return hours+':'+minutes+':'+seconds; + } + + Timer { + id: connectionTimer + interval: 5000 + running: false; + repeat: false; + onTriggered: { + //-- Vehicle is gone + if(activeVehicle) { + //-- Let video stream close + QGroundControl.settingsManager.videoSettings.rtspTimeout.rawValue = 1 + if(!activeVehicle.armed) { + //-- If it wasn't already set to auto-disconnect + if(!activeVehicle.autoDisconnect) { + //-- Vehicle is not armed. Close connection and tell user. + activeVehicle.disconnectInactiveVehicle() + connectionLostDisarmedDialog.open() + } + } else { + //-- Vehicle is armed. Show doom dialog. + connectionLostArmed.open() + } + } + } + } + + Connections { + target: QGroundControl.qgcPositionManger + onGcsPositionChanged: { + if (activeVehicle && gcsPosition.latitude && Math.abs(gcsPosition.latitude) > 0.001 && gcsPosition.longitude && Math.abs(gcsPosition.longitude) > 0.001) { + var gcs = QtPositioning.coordinate(gcsPosition.latitude, gcsPosition.longitude) + var veh = activeVehicle.coordinate; + _distance = QGroundControl.metersToAppSettingsDistanceUnits(gcs.distanceTo(veh)); + //-- Ignore absurd values + if(_distance > 99999) + _distance = 0; + if(_distance < 0) + _distance = 0; + } else { + _distance = 0; + } + } + } + + Connections { + target: QGroundControl.multiVehicleManager.activeVehicle + onConnectionLostChanged: { + if(!_communicationLost) { + //-- Communication regained + connectionTimer.stop(); + if(connectionLostArmed.visible) { + connectionLostArmed.close() + } + //-- Reset stream timeout + QGroundControl.settingsManager.videoSettings.rtspTimeout.rawValue = 60 + } else { + if(activeVehicle && !activeVehicle.autoDisconnect) { + //-- Communication lost + connectionTimer.start(); + } + } + } + } + + Connections { + target: QGroundControl.multiVehicleManager + onVehicleAdded: { + //-- Dismiss comm lost dialog if open + connectionLostDisarmedDialog.close() + } + } + + MessageDialog { + id: connectionLostDisarmedDialog + title: qsTr("Communication Lost") + text: qsTr("Connection to vehicle has been lost and closed.") + standardButtons: StandardButton.Ok + onAccepted: { + connectionLostDisarmedDialog.close() + } + } + + //-- Heading Indicator + Rectangle { + id: compassBar + height: ScreenTools.defaultFontPixelHeight * 1.5 + width: ScreenTools.defaultFontPixelWidth * 50 + color: "#DEDEDE" + radius: 2 + clip: true + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * (_airspaceIndicatorVisible ? 3 : 2) + anchors.horizontalCenter: parent.horizontalCenter + visible: !mainIsMap + Repeater { + model: 720 + visible: !mainIsMap + QGCLabel { + function _normalize(degrees) { + var a = degrees % 360 + if (a < 0) a += 360 + return a + } + property int _startAngle: modelData + 180 + _heading + property int _angle: _normalize(_startAngle) + anchors.verticalCenter: parent.verticalCenter + x: visible ? ((modelData * (compassBar.width / 360)) - (width * 0.5)) : 0 + visible: _angle % 45 == 0 + color: "#75505565" + font.pointSize: ScreenTools.smallFontPointSize + text: { + switch(_angle) { + case 0: return "N" + case 45: return "NE" + case 90: return "E" + case 135: return "SE" + case 180: return "S" + case 225: return "SW" + case 270: return "W" + case 315: return "NW" + } + return "" + } + } + } + } + Rectangle { + id: headingIndicator + height: ScreenTools.defaultFontPixelHeight + width: ScreenTools.defaultFontPixelWidth * 4 + color: qgcPal.windowShadeDark + visible: !mainIsMap + anchors.bottom: compassBar.top + anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * -0.1 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: _heading + color: qgcPal.text + font.pointSize: ScreenTools.smallFontPointSize + anchors.centerIn: parent + } + } + Image { + height: _indicatorsHeight + width: height + source: "/custom/img/compass_pointer.svg" + visible: !mainIsMap + fillMode: Image.PreserveAspectFit + sourceSize.height: height + anchors.top: compassBar.bottom + anchors.topMargin: ScreenTools.defaultFontPixelHeight * -0.5 + anchors.horizontalCenter: parent.horizontalCenter + } + + //-- Camera Control + Loader { + id: camControlLoader + visible: !mainIsMap && _cameraPresent && _camera.paramComplete + source: visible ? "/custom/CustomCameraControl.qml" : "" + anchors.right: parent.right + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * 4 + } + + //-- Map Scale + MapScale { + id: mapScale + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 16 + mapControl: mainWindow.flightDisplayMap + visible: rootBackground.visible && mainIsMap + } + + //-- Vehicle Indicator + Rectangle { + id: vehicleIndicator + color: qgcPal.windowShade + width: vehicleStatusGrid.width + (ScreenTools.defaultFontPixelWidth * 3) + height: vehicleStatusGrid.height + (ScreenTools.defaultFontPixelHeight * 1.5) + radius: 2 + anchors.bottom: multiVehicleSelector.visible ? multiVehicleSelector.top : parent.bottom + anchors.bottomMargin: ScreenTools.defaultFontPixelWidth + anchors.right: attitudeIndicator.visible ? attitudeIndicator.left : parent.right + anchors.rightMargin: attitudeIndicator.visible ? -ScreenTools.defaultFontPixelWidth : ScreenTools.defaultFontPixelWidth + GridLayout { + id: vehicleStatusGrid + columnSpacing: ScreenTools.defaultFontPixelWidth * 1.5 + rowSpacing: ScreenTools.defaultFontPixelHeight * 0.5 + columns: 7 + anchors.centerIn: parent + //-- Chronometer + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/chronometer.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + } + QGCLabel { + text: { + if(activeVehicle) + return secondsToHHMMSS(activeVehicle.getFact("flightTime").value) + return "00:00:00" + } + color: _indicatorsColor + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + //-- Ground Speed + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/horizontal_speed.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + } + QGCLabel { + text: activeVehicle ? activeVehicle.groundSpeed.value.toFixed(1) + ' ' + activeVehicle.groundSpeed.units : "0.0" + color: _indicatorsColor + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + //-- Vertical Speed + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/vertical_speed.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + + } + QGCLabel { + text: activeVehicle ? activeVehicle.climbRate.value.toFixed(1) + ' ' + activeVehicle.climbRate.units : "0.0" + color: _indicatorsColor + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + //-- Compass + Item { + Layout.rowSpan: 2 + Layout.minimumWidth: mainIsMap ? parent.height * 1.25 : 0 + Layout.fillHeight: true + Layout.fillWidth: true + //-- Large circle + Rectangle { + height: mainIsMap ? parent.height : 0 + width: mainIsMap ? height : 0 + radius: height * 0.5 + border.color: qgcPal.text + border.width: 1 + color: Qt.rgba(0,0,0,0) + anchors.centerIn: parent + visible: mainIsMap + } + //-- North Label + Rectangle { + height: mainIsMap ? ScreenTools.defaultFontPixelHeight * 0.75 : 0 + width: mainIsMap ? ScreenTools.defaultFontPixelWidth * 2 : 0 + radius: ScreenTools.defaultFontPixelWidth * 0.25 + color: qgcPal.windowShade + visible: mainIsMap + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * -0.25 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: "N" + color: qgcPal.text + font.pointSize: ScreenTools.smallFontPointSize + anchors.centerIn: parent + } + } + //-- Needle + Image { + id: compassNeedle + anchors.centerIn: parent + height: mainIsMap ? parent.height * 0.75 : 0 + width: height + source: "/custom/img/compass_needle.svg" + fillMode: Image.PreserveAspectFit + visible: mainIsMap + sourceSize.height: height + transform: [ + Rotation { + origin.x: compassNeedle.width / 2 + origin.y: compassNeedle.height / 2 + angle: _heading + }] + } + //-- Heading + Rectangle { + height: mainIsMap ? ScreenTools.defaultFontPixelHeight * 0.75 : 0 + width: mainIsMap ? ScreenTools.defaultFontPixelWidth * 3.5 : 0 + radius: ScreenTools.defaultFontPixelWidth * 0.25 + color: qgcPal.windowShade + visible: mainIsMap + anchors.bottom: parent.bottom + anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * -0.25 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + text: _heading + color: qgcPal.text + font.pointSize: ScreenTools.smallFontPointSize + anchors.centerIn: parent + } + } + } + //-- Second Row + //-- Odometer + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/odometer.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + + } + QGCLabel { + text: activeVehicle ? ('00000' + activeVehicle.flightDistance.value.toFixed(0)).slice(-5) + ' ' + activeVehicle.flightDistance.units : "00000" + color: _indicatorsColor + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + //-- Altitude + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/altitude.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + + } + QGCLabel { + text: _altitude + color: _indicatorsColor + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + //-- Distance + QGCColoredImage { + height: _indicatorsHeight + width: height + source: "/custom/img/distance.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + color: qgcPal.text + + } + QGCLabel { + text: _distance ? _distanceStr : noGPS + color: _distance ? _indicatorsColor : qgcPal.colorOrange + font.pointSize: ScreenTools.smallFontPointSize + Layout.fillWidth: true + Layout.minimumWidth: indicatorValueWidth + horizontalAlignment: Text.AlignRight + } + } + MouseArea { + anchors.fill: parent + onDoubleClicked: _showAttitude = !_showAttitude + } + } + + //-- Attitude Indicator + Rectangle { + color: qgcPal.windowShade + width: attitudeIndicator.width * 0.5 + height: vehicleIndicator.height + visible: _showAttitude + anchors.top: vehicleIndicator.top + anchors.left: vehicleIndicator.right + } + Rectangle { + id: attitudeIndicator + anchors.bottom: vehicleIndicator.bottom + anchors.bottomMargin: ScreenTools.defaultFontPixelWidth * -0.5 + anchors.right: parent.right + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + height: ScreenTools.defaultFontPixelHeight * 6 + width: height + radius: height * 0.5 + color: qgcPal.windowShade + visible: _showAttitude + CustomAttitudeWidget { + size: parent.height * 0.95 + vehicle: activeVehicle + showHeading: false + anchors.centerIn: parent + } + } + + //-- Multi Vehicle Selector + Row { + id: multiVehicleSelector + spacing: ScreenTools.defaultFontPixelWidth + anchors.bottom: parent.bottom + anchors.bottomMargin: ScreenTools.defaultFontPixelWidth + anchors.right: parent.right + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.multiVehicleManager.vehicles.count > 1 + Repeater { + model: QGroundControl.multiVehicleManager.vehicles.count + CustomVehicleButton { + property var _vehicle: QGroundControl.multiVehicleManager.vehicles.get(modelData) + vehicle: _vehicle + checked: (_vehicle && activeVehicle) ? _vehicle.id === activeVehicle.id : false + } + } + } + + //-- Connection Lost While Armed + Popup { + id: connectionLostArmed + width: mainWindow.width * 0.666 + height: connectionLostArmedCol.height * 1.5 + modal: true + focus: true + parent: Overlay.overlay + x: Math.round((mainWindow.width - width) * 0.5) + y: Math.round((mainWindow.height - height) * 0.5) + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + background: Rectangle { + anchors.fill: parent + color: qgcPal.alertBackground + border.color: qgcPal.alertBorder + radius: ScreenTools.defaultFontPixelWidth + } + Column { + id: connectionLostArmedCol + spacing: ScreenTools.defaultFontPixelHeight * 3 + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + QGCLabel { + text: qsTr("Communication Lost") + font.family: ScreenTools.demiboldFontFamily + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.alertText + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: qsTr("Warning: Connection to vehicle lost.") + color: qgcPal.alertText + font.family: ScreenTools.demiboldFontFamily + font.pointSize: ScreenTools.mediumFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: qsTr("The vehicle will automatically cancel the flight and return to land. Ensure a clear line of sight between transmitter and vehicle. Ensure the takeoff location is clear.") + width: connectionLostArmed.width * 0.75 + wrapMode: Text.WordWrap + color: qgcPal.alertText + font.family: ScreenTools.demiboldFontFamily + font.pointSize: ScreenTools.mediumFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + } + } + } +} diff --git a/custom-example/res/Images/altitude.svg b/custom-example/res/Images/altitude.svg new file mode 100644 index 000000000..ff74d3d1d --- /dev/null +++ b/custom-example/res/Images/altitude.svg @@ -0,0 +1,3 @@ + + + diff --git a/custom-example/res/Images/attitude_crosshair.svg b/custom-example/res/Images/attitude_crosshair.svg new file mode 100644 index 000000000..b49cb4251 --- /dev/null +++ b/custom-example/res/Images/attitude_crosshair.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/custom-example/res/Images/attitude_dial.svg b/custom-example/res/Images/attitude_dial.svg new file mode 100644 index 000000000..281c73132 --- /dev/null +++ b/custom-example/res/Images/attitude_dial.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/custom-example/res/Images/attitude_pointer.svg b/custom-example/res/Images/attitude_pointer.svg new file mode 100644 index 000000000..bf8b6faa6 --- /dev/null +++ b/custom-example/res/Images/attitude_pointer.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/custom-example/res/Images/camera_photo.svg b/custom-example/res/Images/camera_photo.svg new file mode 100644 index 000000000..80e638941 --- /dev/null +++ b/custom-example/res/Images/camera_photo.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/custom-example/res/Images/camera_settings.svg b/custom-example/res/Images/camera_settings.svg new file mode 100644 index 000000000..97c97489b --- /dev/null +++ b/custom-example/res/Images/camera_settings.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/custom-example/res/Images/camera_video.svg b/custom-example/res/Images/camera_video.svg new file mode 100644 index 000000000..7889b35b1 --- /dev/null +++ b/custom-example/res/Images/camera_video.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/custom-example/res/Images/chronometer.svg b/custom-example/res/Images/chronometer.svg new file mode 100644 index 000000000..06817733f --- /dev/null +++ b/custom-example/res/Images/chronometer.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/custom-example/res/Images/compass_needle.svg b/custom-example/res/Images/compass_needle.svg new file mode 100644 index 000000000..8417f6cc6 --- /dev/null +++ b/custom-example/res/Images/compass_needle.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/custom-example/res/Images/compass_pointer.svg b/custom-example/res/Images/compass_pointer.svg new file mode 100644 index 000000000..bfb6bbc2b --- /dev/null +++ b/custom-example/res/Images/compass_pointer.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/custom-example/res/Images/distance.svg b/custom-example/res/Images/distance.svg new file mode 100644 index 000000000..677a40903 --- /dev/null +++ b/custom-example/res/Images/distance.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/custom-example/res/Images/horizontal_speed.svg b/custom-example/res/Images/horizontal_speed.svg new file mode 100644 index 000000000..bdbc4efb3 --- /dev/null +++ b/custom-example/res/Images/horizontal_speed.svg @@ -0,0 +1,12 @@ + + + + + + + diff --git a/custom-example/res/Images/microSD.svg b/custom-example/res/Images/microSD.svg new file mode 100644 index 000000000..389b8b0ce --- /dev/null +++ b/custom-example/res/Images/microSD.svg @@ -0,0 +1,3 @@ + + + diff --git a/custom-example/res/Images/odometer.svg b/custom-example/res/Images/odometer.svg new file mode 100644 index 000000000..3e03abb95 --- /dev/null +++ b/custom-example/res/Images/odometer.svg @@ -0,0 +1,23 @@ + + + + + + + + +1 + diff --git a/custom-example/res/Images/vertical_speed.svg b/custom-example/res/Images/vertical_speed.svg new file mode 100644 index 000000000..2ce667890 --- /dev/null +++ b/custom-example/res/Images/vertical_speed.svg @@ -0,0 +1,12 @@ + + + + + + + diff --git a/custom-example/res/Images/void.png b/custom-example/res/Images/void.png new file mode 100644 index 0000000000000000000000000000000000000000..1e9811e8ce6a35d24bf20aa5b37acdf24265b047 GIT binary patch literal 1639 zcmcIlO>f*p7w zufa;M_pQY3^rja0T(t74;o|sORNcYWSjLCF^;NT2dzx`97NP`L3&QANs-DYEwsZLo zKZfPOn>vge!8+MYrCe+ib+%3wi{SxMSimjG)clq)po4nVls|&5M>rc7N)tISt1|?^K)T1wx1a= zHA6_^`!YR;4NfMtejZPA#ZyZ$u2ob%8<%YLJxLcJAc1od!C}c$mwkWKb+c z$z}rA)l5hKs)^-7Hi^t2=JwtBNgSUwGA|ae(4IegiYCXgM+46@ER(9*2N~e*Nzp>0 zZ~D{pTzuD$76n?DXvnxa8IjIaNr+7Er;E3Qc{hhPw9B~w(tE0818Zg8Emq|0p@d7%>*=DtSgVOYE7H;;QwZBy>_RKZQ;-x)o^Ia@G-P? zQ2})WO*{DLE4h0&o^%l-`Dp3dhhQae*4)`zkG+qY-+uoOcdPo5 literal 0 HcmV?d00001 diff --git a/custom-example/res/MainToolbar/CustomArmedIndicator.qml b/custom-example/res/MainToolbar/CustomArmedIndicator.qml new file mode 100644 index 000000000..db1edb942 --- /dev/null +++ b/custom-example/res/MainToolbar/CustomArmedIndicator.qml @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.2 + +import QGroundControl 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +//------------------------------------------------------------------------- +//-- Armed Indicator +Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: labelRow.width + (ScreenTools.defaultFontPixelWidth * 6) + color: qgcPal.windowShade + + property bool _armed: activeVehicle ? activeVehicle.armed : false + + Row { + id: labelRow + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + QGCLabel { + id: labelText + text: _armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text + font.pointSize: ScreenTools.defaultFontPointSize + anchors.verticalCenter: parent.verticalCenter + } + Rectangle { + height: ScreenTools.defaultFontPixelHeight * 0.5 + width: height + radius: height * 0.5 + color: _armed ? qgcPal.colorGreen : qgcPal.colorRed + border.color: qgcPal.window + border.width: 1 + anchors.verticalCenter: parent.verticalCenter + } + } + QGCMouseArea { + fillItem: parent + onClicked: _armed ? mainWindow.disarmVehicle() : mainWindow.armVehicle() + } +} diff --git a/custom-example/res/MainToolbar/CustomBatteryIndicator.qml b/custom-example/res/MainToolbar/CustomBatteryIndicator.qml new file mode 100644 index 000000000..6e58fb95a --- /dev/null +++ b/custom-example/res/MainToolbar/CustomBatteryIndicator.qml @@ -0,0 +1,207 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.11 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Battery Indicator +Item { + id: _root + width: batteryIndicatorRow.width + anchors.top: parent.top + anchors.bottom: parent.bottom + + property var battery1: activeVehicle ? activeVehicle.battery : null + property var battery2: activeVehicle ? activeVehicle.battery2 : null + property bool hasSecondBattery: battery2 && battery2.voltage.value !== -1 + + function lowestBattery() { + if(activeVehicle) { + if(hasSecondBattery) { + if(activeVehicle.battery2.percentRemaining.value < activeVehicle.battery.percentRemaining.value) { + return activeVehicle.battery2 + } + } + return activeVehicle.battery + } + return null + } + + function getBatteryColor(battery) { + if(battery) { + if(battery.percentRemaining.value > 75) { + return qgcPal.text + } + if(battery.percentRemaining.value > 50) { + return qgcPal.colorOrange + } + if(battery.percentRemaining.value > 0.1) { + return qgcPal.colorRed + } + } + return qgcPal.colorGrey + } + + function getBatteryPercentageText(battery) { + if(battery) { + if(battery.percentRemaining.value > 98.9) { + return "100%" + } + if(battery.percentRemaining.value > 0.1) { + return battery.percentRemaining.valueString + battery.percentRemaining.units + } + if(battery.voltage.value >= 0) { + return battery.voltage.valueString + battery.voltage.units + } + } + return "N/A" + } + + Component { + id: batteryInfo + + Rectangle { + width: battCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: battCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + + Column { + id: battCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(battGrid.width, battLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: battLabel + text: qsTr("Battery Status") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: battGrid + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { + id: batteryLabel + text: qsTr("Battery 1") + Layout.alignment: Qt.AlignVCenter + } + QGCColoredImage { + height: batteryLabel.height + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + Rectangle { + color: getBatteryColor(activeVehicle ? activeVehicle.battery : null) + anchors.left: parent.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 0.125 + height: parent.height * 0.35 + width: activeVehicle ? (activeVehicle.battery.percentRemaining.value / 100) * parent.width * 0.875 : 0 + anchors.verticalCenter: parent.verticalCenter + } + } + + QGCLabel { text: qsTr("Voltage:") } + QGCLabel { text: (battery1 && battery1.voltage.value !== -1) ? (battery1.voltage.valueString + " " + battery1.voltage.units) : "N/A" } + QGCLabel { text: qsTr("Accumulated Consumption:") } + QGCLabel { text: (battery1 && battery1.mahConsumed.value !== -1) ? (battery1.mahConsumed.valueString + " " + battery1.mahConsumed.units) : "N/A" } + Item { + width: 1 + height: 1 + visible: hasSecondBattery; + Layout.columnSpan: 2 + } + + QGCLabel { + text: qsTr("Battery 2") + visible: hasSecondBattery + Layout.alignment: Qt.AlignVCenter + } + QGCColoredImage { + height: batteryLabel.height + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + color: qgcPal.text + visible: hasSecondBattery + fillMode: Image.PreserveAspectFit + Rectangle { + color: getBatteryColor(activeVehicle ? activeVehicle.battery2 : null) + anchors.left: parent.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 0.125 + height: parent.height * 0.35 + width: activeVehicle ? (activeVehicle.battery2.percentRemaining.value / 100) * parent.width * 0.875 : 0 + anchors.verticalCenter: parent.verticalCenter + } + } + + QGCLabel { text: qsTr("Voltage:"); visible: hasSecondBattery; } + QGCLabel { text: (battery2 && battery2.voltage.value !== -1) ? (battery2.voltage.valueString + " " + battery2.voltage.units) : "N/A"; visible: hasSecondBattery; } + QGCLabel { text: qsTr("Accumulated Consumption:"); visible: hasSecondBattery; } + QGCLabel { text: (battery2 && battery2.mahConsumed.value !== -1) ? (battery2.mahConsumed.valueString + " " + battery2.mahConsumed.units) : "N/A"; visible: hasSecondBattery; } + } + } + } + } + + Row { + id: batteryIndicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + opacity: (activeVehicle && activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 + spacing: ScreenTools.defaultFontPixelWidth + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + Rectangle { + color: getBatteryColor(lowestBattery()) + anchors.left: parent.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 0.25 + height: parent.height * 0.35 + width: activeVehicle ? (activeVehicle.battery.percentRemaining.value / 100) * parent.width * 0.75 : 0 + anchors.verticalCenter: parent.verticalCenter + } + } + QGCLabel { + text: getBatteryPercentageText(lowestBattery()) + font.pointSize: ScreenTools.smallFontPointSize + color: getBatteryColor(lowestBattery()) + anchors.verticalCenter: parent.verticalCenter + } + } + MouseArea { + anchors.fill: parent + onClicked: { + mainWindow.showPopUp(_root, batteryInfo) + } + } +} diff --git a/custom-example/res/MainToolbar/CustomGPSIndicator.qml b/custom-example/res/MainToolbar/CustomGPSIndicator.qml new file mode 100644 index 000000000..6dfc4f166 --- /dev/null +++ b/custom-example/res/MainToolbar/CustomGPSIndicator.qml @@ -0,0 +1,120 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.11 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +import Custom.Widgets 1.0 + +//------------------------------------------------------------------------- +//-- GPS Indicator +Item { + id: _root + width: gpsRow.width + anchors.top: parent.top + anchors.bottom: parent.bottom + + function getGPSSignal() { + if(!activeVehicle || activeVehicle.gps.count.rawValue < 1 || activeVehicle.gps.hdop.rawValue > 1.4) { + return 0; + } else if(activeVehicle.gps.hdop.rawValue < 1.0) { + return 100; + } else if(activeVehicle.gps.hdop.rawValue < 1.1) { + return 75; + } else if(activeVehicle.gps.hdop.rawValue < 1.2) { + return 50; + } else { + return 25; + } + } + + Component { + id: gpsInfo + + Rectangle { + width: gpsCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: gpsCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + + Column { + id: gpsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(gpsGrid.width, gpsLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: gpsLabel + text: (activeVehicle && activeVehicle.gps.count.value >= 0) ? qsTr("GPS Status") : qsTr("GPS Data Unavailable") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: gpsGrid + visible: (activeVehicle && activeVehicle.gps.count.value >= 0) + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + + QGCLabel { text: qsTr("GPS Count:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.count.valueString : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("GPS Lock:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.lock.enumStringValue : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("HDOP:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.hdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("VDOP:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.vdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("Course Over Ground:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.courseOverGround.valueString : qsTr("--.--", "No data to display") } + } + } + } + } + + Row { + id: gpsRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth * 0.25 + QGCColoredImage { + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/qmlimages/Gps.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + opacity: getGPSSignal() > 0 ? 1 : 0.5 + } + CustomSignalStrength { + anchors.verticalCenter: parent.verticalCenter + size: parent.height * 0.75 + percent: getGPSSignal() + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainWindow.showPopUp(_root, gpsInfo) + } + } +} diff --git a/custom-example/res/MainToolbar/CustomMainToolBar.qml b/custom-example/res/MainToolbar/CustomMainToolBar.qml new file mode 100644 index 000000000..8a3f635eb --- /dev/null +++ b/custom-example/res/MainToolbar/CustomMainToolBar.qml @@ -0,0 +1,285 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controllers 1.0 + +import Custom.Widgets 1.0 + +Item { + id: toolBar + anchors.fill: parent + property string sectionTitle: qsTr("Fly") + property bool inPlanView: planViewLoader.visible + property bool inFlyView: rootBackground.visible + //------------------------------------------------------------------------- + //-- Setup can be invoked from c++ side + Connections { + target: setupWindow + onVisibleChanged: { + if(setupWindow.visible) { + vehicleSetup.checked = true + sectionTitle = vehicleSetup.text + } + } + } + //------------------------------------------------------------------------- + //-- Initial State + Component.onCompleted: { + flyButton.checked = true + sectionTitle = flyButton.text + } + //------------------------------------------------------------------------- + //-- Fly/Plan state toggle + onInPlanViewChanged: { + if(inPlanView) { + planButton.checked = true + sectionTitle = planButton.text + } + } + onInFlyViewChanged: { + if(inFlyView) { + flyButton.checked = true + sectionTitle = flyButton.text + } + } + Row { + id: iconRow + height: parent.height + anchors.left: parent.left + spacing: ScreenTools.defaultFontPixelWidth * 2 + + CustomIconButton { + height: parent.height + onPressed: { + if(drawer.visible) { + drawer.close() + } else { + drawer.open() + } + // Easter egg mechanism + _pressCount++ + eggTimer.restart() + if (_pressCount == 5) { + QGroundControl.corePlugin.showAdvancedUI = !QGroundControl.corePlugin.showAdvancedUI + } + } + property int _pressCount: 0 + Timer { + id: eggTimer + interval: 1000 + onTriggered: parent._pressCount = 0 + } + } + Rectangle { + width: 1 + height: parent.height + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(0,0,0,0.15) : Qt.rgba(1,1,1,0.15) + } + //------------------------------------------------------------------------- + //-- Multi Vehicle Selector + Loader { + anchors.top: parent.top + anchors.bottom: parent.bottom + source: "/custom/CustomMultiVehicleSelector.qml" + visible: activeVehicle && !inPlanView + } + Rectangle { + width: 1 + height: parent.height + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(0,0,0,0.15) : Qt.rgba(1,1,1,0.15) + visible: activeVehicle && !inPlanView + } + //------------------------------------------------------------------------- + //-- Flight Mode + Loader { + anchors.top: parent.top + anchors.bottom: parent.bottom + source: "/custom/CustomModeIndicator.qml" + visible: activeVehicle && !inPlanView + } + } + //------------------------------------------------------------------------- + //-- Arm/Disarm + Loader { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + source: "/custom/CustomArmedIndicator.qml" + visible: activeVehicle && !inPlanView + } + //------------------------------------------------------------------------- + // Indicators + Loader { + source: inPlanView ? "/qml/PlanToolBarIndicators.qml" : "/custom/CustomMainToolBarIndicators.qml" + anchors.left: iconRow.right + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + //------------------------------------------------------------------------- + // Parameter download progress bar + Rectangle { + anchors.bottom: parent.bottom + height: ScreenTools.defaultFontPixelheight * 0.25 + width: activeVehicle ? activeVehicle.parameterManager.loadProgress * parent.width : 0 + color: qgcPal.colorGreen + } + //------------------------------------------------------------------------- + // Bottom single pixel divider + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(0,0,0,0.15) : Qt.rgba(1,1,1,0.15) + } + //------------------------------------------------------------------------- + //-- Navigation Drawer (Left to Right, on command or using touch gestures) + Drawer { + id: drawer + y: header.height + width: navButtonWidth + height: mainWindow.height - header.height + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + background: Rectangle { + color: qgcPal.window + } + ButtonGroup { + id: buttonGroup + buttons: buttons.children + } + ColumnLayout { + id: buttons + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight * 0.125 + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + CustomToolBarButton { + id: flyButton + text: qsTr("Fly") + icon.source: "/qmlimages/PaperPlane.svg" + Layout.fillWidth: true + onClicked: { + checked = true + drawer.close() + sectionTitle = text + mainWindow.showFlyView() + } + } + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + CustomToolBarButton { + id: planButton + text: qsTr("Plan") + icon.source: "/qmlimages/Plan.svg" + Layout.fillWidth: true + onClicked: { + checked = true + drawer.close() + sectionTitle = text + mainWindow.showPlanView() + } + } + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + CustomToolBarButton { + text: qsTr("Analyze") + icon.source: "/qmlimages/Analyze.svg" + Layout.fillWidth: true + onClicked: { + checked = true + drawer.close() + sectionTitle = text + mainWindow.showAnalyzeView() + } + } + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + CustomToolBarButton { + id: vehicleSetup + text: qsTr("Vehicle Setup") + icon.source: "/qmlimages/Gears.svg" + Layout.fillWidth: true + onClicked: { + checked = true + drawer.close() + sectionTitle = text + mainWindow.showSetupView() + } + } + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + } + ColumnLayout { + id: lowerButtons + anchors.bottom: parent.bottom + anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 0.125 + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight * 0.125 + Rectangle { + Layout.alignment: Qt.AlignVCenter + width: parent.width + height: 1 + color: Qt.rgba(1,1,1,0.15) + } + CustomToolBarButton { + id: settingsButton + text: qsTr("Settings") + icon.source: "/qmlimages/Gears.svg" + Layout.fillWidth: true + onClicked: { + checked = true + buttonGroup.checkState = Qt.Unchecked + drawer.close() + sectionTitle = text + mainWindow.showSettingsView() + } + } + Connections { + target: buttonGroup + onClicked: settingsButton.checked = false + } + } + } +} diff --git a/custom-example/res/MainToolbar/CustomMainToolBarIndicators.qml b/custom-example/res/MainToolbar/CustomMainToolBarIndicators.qml new file mode 100644 index 000000000..40ffc05d9 --- /dev/null +++ b/custom-example/res/MainToolbar/CustomMainToolBarIndicators.qml @@ -0,0 +1,82 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Dialogs 1.3 +import QtQuick.Layouts 1.11 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Item { + anchors.fill: parent + readonly property real _indicatorMargins: ScreenTools.defaultFontPixelHeight * 0.75 + //------------------------------------------------------------------------- + //-- Waiting for a vehicle + Row { + id: waitForVehicle + spacing: ScreenTools.defaultFontPixelWidth + visible: !activeVehicle + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + QGCColoredImage { + id: menuEdge + anchors.verticalCenter: parent.verticalCenter + height: ScreenTools.defaultFontPixelHeight + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + source: "/qmlimages/PaperPlane.svg" + color: qgcPal.buttonText + } + QGCLabel { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Waiting for a vehicle") + font.pointSize: ScreenTools.mediumFontPointSize + font.family: ScreenTools.demiboldFontFamily + } + } + //------------------------------------------------------------------------- + //-- Toolbar Indicators + Row { + id: indicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.rightMargin: ScreenTools.defaultFontPixelWidth * 2 + spacing: ScreenTools.defaultFontPixelWidth * 2 + visible: activeVehicle && !communicationLost + Repeater { + model: activeVehicle ? activeVehicle.toolBarIndicators : [] + Loader { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: _indicatorMargins + source: modelData; + } + } + Item { + width: 1 + height: 1 + } + Loader { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: _indicatorMargins + source: "/toolbar/MessageIndicator.qml" + } + } +} diff --git a/custom-example/res/MainToolbar/CustomModeIndicator.qml b/custom-example/res/MainToolbar/CustomModeIndicator.qml new file mode 100644 index 000000000..3cffb64b1 --- /dev/null +++ b/custom-example/res/MainToolbar/CustomModeIndicator.qml @@ -0,0 +1,85 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Mode Indicator +Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: selectorRow.width + + property var _flightModes: activeVehicle ? activeVehicle.flightModes : [ ] + + on_FlightModesChanged: flightModeSelector.updateFlightModesMenu() + + Row { + id: selectorRow + spacing: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: parent.verticalCenter + QGCLabel { + id: flightModeSelector + text: activeVehicle ? activeVehicle.flightMode : qsTr("N/A") + color: qgcPal.text + font.pointSize: ScreenTools.defaultFontPointSize + anchors.verticalCenter: parent.verticalCenter + Menu { + id: flightModesMenu + } + Component { + id: flightModeMenuItemComponent + MenuItem { + onTriggered: activeVehicle.flightMode = text + } + } + property var flightModesMenuItems: [] + function updateFlightModesMenu() { + if (activeVehicle && activeVehicle.flightModeSetAvailable) { + // Remove old menu items + var i = 0 + for (i = 0; i < flightModesMenuItems.length; i++) { + flightModesMenu.removeItem(flightModesMenuItems[i]) + } + flightModesMenuItems.length = 0 + // Add new items + for (i = 0; i < _flightModes.length; i++) { + var menuItem = flightModeMenuItemComponent.createObject(null, { "text": _flightModes[i] }) + flightModesMenuItems.push(menuItem) + flightModesMenu.insertItem(i, menuItem) + } + } + } + } + QGCColoredImage { + anchors.verticalCenter: parent.verticalCenter + height: ScreenTools.defaultFontPixelHeight * 0.5 + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + source: "/res/DropArrow.svg" + color: qgcPal.text + } + } + Component.onCompleted: flightModeSelector.updateFlightModesMenu() + MouseArea { + visible: activeVehicle && activeVehicle.flightModeSetAvailable + anchors.fill: parent + onClicked: flightModesMenu.popup() + } +} diff --git a/custom-example/res/MainToolbar/CustomMultiVehicleSelector.qml b/custom-example/res/MainToolbar/CustomMultiVehicleSelector.qml new file mode 100644 index 000000000..89f95a785 --- /dev/null +++ b/custom-example/res/MainToolbar/CustomMultiVehicleSelector.qml @@ -0,0 +1,104 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Multi Vehicle Selector +Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: selectorRow.width + property bool _multiVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1 + Component.onCompleted: { + updatemultiVehiclesMenu() + } + Connections { + target: QGroundControl.multiVehicleManager.vehicles + onCountChanged: updatemultiVehiclesMenu() + } + Row { + id: selectorRow + spacing: ScreenTools.defaultFontPixelWidth + anchors.verticalCenter: parent.verticalCenter + QGCColoredImage { + anchors.verticalCenter: parent.verticalCenter + height: ScreenTools.defaultFontPixelHeight + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + source: "/qmlimages/PaperPlane.svg" + color: qgcPal.text + } + QGCLabel { + id: multiVehicleSelector + text: "Vehicle " + (activeVehicle ? activeVehicle.id : "None") + color: qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + } + QGCColoredImage { + visible: _multiVehicles + anchors.verticalCenter: parent.verticalCenter + height: ScreenTools.defaultFontPixelHeight * 0.5 + width: height + sourceSize.height: parent.height + fillMode: Image.PreserveAspectFit + source: "/res/DropArrow.svg" + color: qgcPal.text + } + } + Menu { + id: multiVehiclesMenu + } + Component { + id: multiVehicleMenuItemComponent + MenuItem { + onTriggered: QGroundControl.multiVehicleManager.activeVehicle = vehicle + property int vehicleId: Number(text.split(" ")[1]) + property var vehicle: QGroundControl.multiVehicleManager.getVehicleById(vehicleId) + } + } + property var multiVehiclesMenuItems: [] + function updatemultiVehiclesMenu() { + if (_multiVehicles) { + // Remove old menu items + for (var i = 0; i < multiVehiclesMenuItems.length; i++) { + multiVehiclesMenu.removeItem(multiVehiclesMenuItems[i]) + } + multiVehiclesMenuItems.length = 0 + // Add new items + for (i = 0; i < QGroundControl.multiVehicleManager.vehicles.count; i++) { + var vehicle = QGroundControl.multiVehicleManager.vehicles.get(i) + var menuItem = multiVehicleMenuItemComponent.createObject(null, { "text": "Vehicle " + vehicle.id }) + multiVehiclesMenuItems.push(menuItem) + multiVehiclesMenu.insertItem(i, menuItem) + console.log("Vehicle " + vehicle.id) + } + } else { + console.log('No multiple vehicles: ' + QGroundControl.multiVehicleManager.vehicles.count) + } + } + MouseArea { + visible: _multiVehicles + anchors.fill: parent + onClicked: { + console.log('Clicked') + multiVehiclesMenu.popup() + } + } +} diff --git a/custom-example/res/MainToolbar/CustomRCRSSIIndicator.qml b/custom-example/res/MainToolbar/CustomRCRSSIIndicator.qml new file mode 100644 index 000000000..15a3eb80a --- /dev/null +++ b/custom-example/res/MainToolbar/CustomRCRSSIIndicator.qml @@ -0,0 +1,101 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @author Gus Grubba + */ + +import QtQuick 2.11 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.11 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +import Custom.Widgets 1.0 + +//------------------------------------------------------------------------- +//-- RC RSSI Indicator +Item { + id: _root + width: visible ? rssiRow.width : 0 + anchors.top: parent.top + anchors.bottom: parent.bottom + visible: activeVehicle ? activeVehicle.supportsRadio : true + + property bool _rcRSSIAvailable: activeVehicle ? activeVehicle.rcRSSI > 0 && activeVehicle.rcRSSI <= 100 : false + + Component { + id: rcRSSIInfo + + Rectangle { + width: rcrssiCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: rcrssiCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + + Column { + id: rcrssiCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(rcrssiGrid.width, rssiLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: rssiLabel + text: activeVehicle ? (activeVehicle.rcRSSI !== 255 ? qsTr("RC RSSI Status") : qsTr("RC RSSI Data Unavailable")) : qsTr("N/A", "No data available") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: rcrssiGrid + visible: _rcRSSIAvailable + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("RSSI:") } + QGCLabel { text: activeVehicle ? (activeVehicle.rcRSSI + "%") : 0 } + } + } + } + } + + Row { + id: rssiRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth * 0.25 + QGCColoredImage { + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/custom/img/menu_rc.svg" + color: qgcPal.text + fillMode: Image.PreserveAspectFit + opacity: _rcRSSIAvailable ? 1 : 0.5 + } + CustomSignalStrength { + anchors.verticalCenter: parent.verticalCenter + size: parent.height * 0.75 + percent: _rcRSSIAvailable ? activeVehicle.rcRSSI : 0 + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + mainWindow.showPopUp(_root, rcRSSIInfo) + } + } +} diff --git a/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.cc b/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.cc new file mode 100644 index 000000000..5942f3bf0 --- /dev/null +++ b/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.cc @@ -0,0 +1,107 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Autopilot Plugin + * @author Gus Grubba + */ + +#include "CustomAutoPilotPlugin.h" + +#include "QGCApplication.h" +#include "QGCCorePlugin.h" + +//----------------------------------------------------------------------------- +CustomAutoPilotPlugin::CustomAutoPilotPlugin(Vehicle* vehicle, QObject* parent) + : PX4AutoPilotPlugin(vehicle, parent) +{ + connect(qgcApp()->toolbox()->corePlugin(), &QGCCorePlugin::showAdvancedUIChanged, this, &CustomAutoPilotPlugin::_advancedChanged); +} + +//----------------------------------------------------------------------------- +void +CustomAutoPilotPlugin::_advancedChanged(bool) +{ + _components.clear(); + emit vehicleComponentsChanged(); +} + +//----------------------------------------------------------------------------- +const QVariantList& +CustomAutoPilotPlugin::vehicleComponents() +{ + if (_components.count() == 0 && !_incorrectParameterVersion) { + if (_vehicle) { + bool showAdvanced = qgcApp()->toolbox()->corePlugin()->showAdvancedUI(); + qDebug() << "Loading components:" << showAdvanced; + if (_vehicle->parameterManager()->parametersReady()) { + if(showAdvanced) { + _airframeComponent = new AirframeComponent(_vehicle, this); + _airframeComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_airframeComponent))); + } + if (!_vehicle->hilMode()) { + _sensorsComponent = new SensorsComponent(_vehicle, this); + _sensorsComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_sensorsComponent))); + } + if(showAdvanced) { + _radioComponent = new PX4RadioComponent(_vehicle, this); + _radioComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_radioComponent))); + + _flightModesComponent = new FlightModesComponent(_vehicle, this); + _flightModesComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_flightModesComponent))); + + _powerComponent = new PowerComponent(_vehicle, this); + _powerComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_powerComponent))); + + _motorComponent = new MotorComponent(_vehicle, this); + _motorComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_motorComponent))); + } + + _safetyComponent = new SafetyComponent(_vehicle, this); + _safetyComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_safetyComponent))); + + if(showAdvanced) { + _tuningComponent = new PX4TuningComponent(_vehicle, this); + _tuningComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_tuningComponent))); + + //-- Is there support for cameras? + if(_vehicle->parameterManager()->parameterExists(_vehicle->id(), "TRIG_MODE")) { + _cameraComponent = new CameraComponent(_vehicle, this); + _cameraComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_cameraComponent))); + } + } + + //-- Is there an ESP8266 Connected? + if(_vehicle->parameterManager()->parameterExists(MAV_COMP_ID_UDP_BRIDGE, "SW_VER")) { + _esp8266Component = new ESP8266Component(_vehicle, this); + _esp8266Component->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_esp8266Component))); + } + } else { + qWarning() << "Call to vehicleCompenents prior to parametersReady"; + } + + if(_vehicle->parameterManager()->parameterExists(_vehicle->id(), "SLNK_RADIO_CHAN")) { + _syslinkComponent = new SyslinkComponent(_vehicle, this); + _syslinkComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue(reinterpret_cast(_syslinkComponent))); + } + } else { + qWarning() << "Internal error"; + } + } + return _components; +} diff --git a/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.h b/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.h new file mode 100644 index 000000000..9b20b4d89 --- /dev/null +++ b/custom-example/src/AutoPilotPlugin/CustomAutoPilotPlugin.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Autopilot Plugin + * @author Gus Grubba + */ + +#pragma once + +#include "PX4AutoPilotPlugin.h" +#include "Vehicle.h" + +/// Custom overrides from standard PX4AutoPilotPlugin implementation +class CustomAutoPilotPlugin : public PX4AutoPilotPlugin +{ + Q_OBJECT +public: + CustomAutoPilotPlugin(Vehicle* vehicle, QObject* parent); + const QVariantList& vehicleComponents() override; +private slots: + void _advancedChanged (bool advanced); +private: + QVariantList _components; + +}; diff --git a/custom-example/src/CustomPlugin.cc b/custom-example/src/CustomPlugin.cc new file mode 100644 index 000000000..aa005b10b --- /dev/null +++ b/custom-example/src/CustomPlugin.cc @@ -0,0 +1,427 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @brief Custom QGCCorePlugin Implementation + * @author Gus Grubba + */ + +#include +#include +#include +#include "QGCSettings.h" +#include "MAVLinkLogManager.h" + +#include "CustomPlugin.h" +#include "CustomQuickInterface.h" + +#include "MultiVehicleManager.h" +#include "QGCApplication.h" +#include "SettingsManager.h" +#include "AppMessages.h" +#include "QmlComponentInfo.h" +#include "QGCPalette.h" + +QGC_LOGGING_CATEGORY(CustomLog, "CustomLog") + +CustomVideoReceiver::CustomVideoReceiver(QObject* parent) + : VideoReceiver(parent) +{ +#if defined(QGC_GST_STREAMING) + //-- Shorter RTSP test interval + _restart_time_ms = 1000; +#endif +} + +CustomVideoReceiver::~CustomVideoReceiver() +{ +} + +//----------------------------------------------------------------------------- +static QObject* +customQuickInterfaceSingletonFactory(QQmlEngine*, QJSEngine*) +{ + qCDebug(CustomLog) << "Creating CustomQuickInterface instance"; + CustomQuickInterface* pIFace = new CustomQuickInterface(); + CustomPlugin* pPlug = dynamic_cast(qgcApp()->toolbox()->corePlugin()); + if(pPlug) { + pIFace->init(); + } else { + qCritical() << "Error obtaining instance of CustomPlugin"; + } + return pIFace; +} + +//----------------------------------------------------------------------------- +CustomOptions::CustomOptions(CustomPlugin*, QObject* parent) + : QGCOptions(parent) +{ +} + +//----------------------------------------------------------------------------- +bool +CustomOptions::showFirmwareUpgrade() const +{ + return qgcApp()->toolbox()->corePlugin()->showAdvancedUI(); +} + +QColor +CustomOptions::toolbarBackgroundLight() const +{ + return CustomPlugin::_windowShadeEnabledLightColor; +} + +QColor +CustomOptions::toolbarBackgroundDark() const +{ + return CustomPlugin::_windowShadeEnabledDarkColor; +} + +//----------------------------------------------------------------------------- +CustomPlugin::CustomPlugin(QGCApplication *app, QGCToolbox* toolbox) + : QGCCorePlugin(app, toolbox) +{ + _pOptions = new CustomOptions(this, this); + _showAdvancedUI = false; +} + +//----------------------------------------------------------------------------- +CustomPlugin::~CustomPlugin() +{ +} + +//----------------------------------------------------------------------------- +void +CustomPlugin::setToolbox(QGCToolbox* toolbox) +{ + QGCCorePlugin::setToolbox(toolbox); + qmlRegisterSingletonType("CustomQuickInterface", 1, 0, "CustomQuickInterface", customQuickInterfaceSingletonFactory); + //-- Disable automatic logging + toolbox->mavlinkLogManager()->setEnableAutoStart(false); + toolbox->mavlinkLogManager()->setEnableAutoUpload(false); + connect(qgcApp()->toolbox()->corePlugin(), &QGCCorePlugin::showAdvancedUIChanged, this, &CustomPlugin::_advancedChanged); +} + +//----------------------------------------------------------------------------- +void +CustomPlugin::_advancedChanged(bool changed) +{ + //-- We are now in "Advanced Mode" (or not) + emit _pOptions->showFirmwareUpgradeChanged(changed); +} + +//----------------------------------------------------------------------------- +void +CustomPlugin::addSettingsEntry(const QString& title, + const char* qmlFile, + const char* iconFile/*= nullptr*/) +{ + Q_CHECK_PTR(qmlFile); + // 'this' instance will take ownership on the QmlComponentInfo instance + _customSettingsList.append(QVariant::fromValue( + new QmlComponentInfo(title, + QUrl::fromUserInput(qmlFile), + iconFile == nullptr ? QUrl() : QUrl::fromUserInput(iconFile), + this))); +} + +//----------------------------------------------------------------------------- +QVariantList& +CustomPlugin::settingsPages() +{ + if(_customSettingsList.isEmpty()) { + addSettingsEntry(tr("General"), "qrc:/qml/GeneralSettings.qml", "qrc:/res/gear-white.svg"); + addSettingsEntry(tr("Comm Links"), "qrc:/qml/LinkSettings.qml", "qrc:/res/waves.svg"); + addSettingsEntry(tr("Offline Maps"),"qrc:/qml/OfflineMap.qml", "qrc:/res/waves.svg"); +#if defined(QGC_GST_MICROHARD_ENABLED) + addSettingsEntry(tr("Microhard"), "qrc:/qml/MicrohardSettings.qml"); +#endif +#if defined(QGC_GST_TAISYNC_ENABLED) + addSettingsEntry(tr("Taisync"), "qrc:/qml/TaisyncSettings.qml"); +#endif +#if defined(QGC_AIRMAP_ENABLED) + addSettingsEntry(tr("AirMap"), "qrc:/qml/AirmapSettings.qml"); +#endif + addSettingsEntry(tr("MAVLink"), "qrc:/qml/MavlinkSettings.qml", " qrc:/res/waves.svg"); + addSettingsEntry(tr("Console"), "qrc:/qml/QGroundControl/Controls/AppMessages.qml"); +#if defined(QGC_ENABLE_QZXING) + addSettingsEntry(tr("Barcode Test"),"qrc:/custom/BarcodeReader.qml"); +#endif +#if defined(QT_DEBUG) + //-- These are always present on Debug builds + addSettingsEntry(tr("Mock Link"), "qrc:/qml/MockLink.qml"); + addSettingsEntry(tr("Debug"), "qrc:/qml/DebugWindow.qml"); + addSettingsEntry(tr("Palette Test"),"qrc:/qml/QmlTest.qml"); +#endif + } + return _customSettingsList; +} + +//----------------------------------------------------------------------------- +QGCOptions* +CustomPlugin::options() +{ + return _pOptions; +} + +//----------------------------------------------------------------------------- +QString +CustomPlugin::brandImageIndoor(void) const +{ + return QStringLiteral("/custom/img/void.png"); +} + +//----------------------------------------------------------------------------- +QString +CustomPlugin::brandImageOutdoor(void) const +{ + return QStringLiteral("/custom/img/void.png"); +} + +//----------------------------------------------------------------------------- +bool +CustomPlugin::overrideSettingsGroupVisibility(QString name) +{ + if (name == BrandImageSettings::name) { + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +VideoReceiver* +CustomPlugin::createVideoReceiver(QObject* parent) +{ + return new CustomVideoReceiver(parent); +} + +//----------------------------------------------------------------------------- +QQmlApplicationEngine* +CustomPlugin::createRootWindow(QObject *parent) +{ + QQmlApplicationEngine* pEngine = new QQmlApplicationEngine(parent); + pEngine->addImportPath("qrc:/qml"); + pEngine->addImportPath("qrc:/Custom/Widgets"); + pEngine->rootContext()->setContextProperty("joystickManager", qgcApp()->toolbox()->joystickManager()); + pEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel()); + pEngine->load(QUrl(QStringLiteral("qrc:/qml/MainRootWindow.qml"))); + return pEngine; +} + +//----------------------------------------------------------------------------- +bool +CustomPlugin::adjustSettingMetaData(const QString& settingsGroup, FactMetaData& metaData) +{ + if (settingsGroup == AppSettings::settingsGroup) { + if (metaData.name() == AppSettings::appFontPointSizeName) { + #if defined(WIN32) + int defaultFontPointSize = 11; + metaData.setRawDefaultValue(defaultFontPointSize); + #endif + } else if (metaData.name() == AppSettings::indoorPaletteName) { + QVariant indoorPalette = 1; + metaData.setRawDefaultValue(indoorPalette); + return true; + } + } + return true; +} + + +const QColor CustomPlugin::_windowShadeEnabledLightColor("#FFFFFF"); +const QColor CustomPlugin::_windowShadeEnabledDarkColor("#0B1420"); + +//----------------------------------------------------------------------------- +void +CustomPlugin::paletteOverride(QString colorName, QGCPalette::PaletteColorInfo_t& colorInfo) +{ + if (colorName == QStringLiteral("window")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#ffffff"); + } + else if (colorName == QStringLiteral("windowShade")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#342926"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#342926"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#d9d9d9"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#d9d9d9"); + } + else if (colorName == QStringLiteral("windowShadeDark")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#40332e"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#080f18"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#bdbdbd"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#bdbdbd"); + } + else if (colorName == QStringLiteral("text")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#777c89"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#9d9d9d"); + } + else if (colorName == QStringLiteral("warningText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#e03131"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#e03131"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#cc0808"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#cc0808"); + } + else if (colorName == QStringLiteral("button")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#594e4c"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#313d4d"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#ffffff"); + } + else if (colorName == QStringLiteral("buttonText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#777c89"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#9d9d9d"); + } + else if (colorName == QStringLiteral("buttonHighlight")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#6EF880"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#222a35"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#edcfb4"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#e4e4e4"); + } + else if (colorName == QStringLiteral("buttonHighlightText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#1d3c20"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#777c89"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#211b1b"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#2c2c2c"); + } + else if (colorName == QStringLiteral("primaryButton")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#24dc09"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#29313a"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#8e5e54"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("primaryButtonText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#777c89"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#cad0d0"); + } + else if (colorName == QStringLiteral("textField")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#0a111f"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#29313a"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#ffffff"); + } + else if (colorName == QStringLiteral("textFieldText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#777c89"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#808080"); + } + else if (colorName == QStringLiteral("mapButton")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#000000"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("mapButtonHighlight")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#84c448"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#be781c"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("mapIndicator")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#9dda4f"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#be781c"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("mapIndicatorChild")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#527942"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#766043"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("colorGreen")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#27bf89"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#0ca678"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#009431"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#009431"); + } + else if (colorName == QStringLiteral("colorOrange")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#f7b24a"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#f6921e"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#b95604"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#b95604"); + } + else if (colorName == QStringLiteral("colorRed")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#e1544c"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#e03131"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ed3939"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#ed3939"); + } + else if (colorName == QStringLiteral("colorGrey")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#8b90a0"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#8b90a0"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#808080"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#808080"); + } + else if (colorName == QStringLiteral("colorBlue")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#228be6"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#228be6"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#1a72ff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#1a72ff"); + } + else if (colorName == QStringLiteral("alertBackground")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#a04a18"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#a04a18"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#b45d48"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#b45d48"); + } + else if (colorName == QStringLiteral("alertBorder")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#c79218"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#c79218"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#808080"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#808080"); + } + else if (colorName == QStringLiteral("alertText")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#fff9ed"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#fff9ed"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#fff9ed"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#fff9ed"); + } + else if (colorName == QStringLiteral("missionItemEditor")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#585858"); + } + else if (colorName == QStringLiteral("hoverColor")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#E7D8AE"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#E7D8AE"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#464f5a"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#464f5a"); + } + else if (colorName == QStringLiteral("mapWidgetBorderLight")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#ffffff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#ffffff"); + } + else if (colorName == QStringLiteral("mapWidgetBorderDark")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#000000"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#000000"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#0b1420"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#000000"); + } + else if (colorName == QStringLiteral("brandingPurple")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#4a2c6d"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#4a2c6d"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#4a2c6d"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#4a2c6d"); + } + else if (colorName == QStringLiteral("brandingBlue")) { + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupEnabled] = QColor("#6045c5"); + colorInfo[QGCPalette::Dark][QGCPalette::ColorGroupDisabled] = QColor("#48d6ff"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupEnabled] = QColor("#6045c5"); + colorInfo[QGCPalette::Light][QGCPalette::ColorGroupDisabled] = QColor("#48d6ff"); + } +} diff --git a/custom-example/src/CustomPlugin.h b/custom-example/src/CustomPlugin.h new file mode 100644 index 000000000..be8af12ac --- /dev/null +++ b/custom-example/src/CustomPlugin.h @@ -0,0 +1,101 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @brief Custom QGCCorePlugin Declaration + * @author Gus Grubba + */ + +#pragma once + +#include "QGCCorePlugin.h" +#include "QGCOptions.h" +#include "QGCLoggingCategory.h" +#include "VideoReceiver.h" +#include "SettingsManager.h" + +#include + +class CustomPlugin; +class CustomSettings; + +Q_DECLARE_LOGGING_CATEGORY(CustomLog) + +//-- Our own, custom video receiver +class CustomVideoReceiver : public VideoReceiver +{ + Q_OBJECT +public: + + explicit CustomVideoReceiver(QObject* parent = nullptr); + ~CustomVideoReceiver(); + +}; + +//----------------------------------------------------------------------------- +//-- Our own, custom options +class CustomOptions : public QGCOptions +{ +public: + CustomOptions(CustomPlugin*, QObject* parent = nullptr); + bool wifiReliableForCalibration () const final { return true; } + QUrl flyViewOverlay () const final { return QUrl::fromUserInput("qrc:/custom/CustomFlyView.qml"); } + //-- We have our own toolbar + QUrl mainToolbarUrl () const final { return QUrl::fromUserInput("qrc:/custom/CustomMainToolBar.qml"); } + QUrl planToolbarUrl () const final { return QUrl::fromUserInput("qrc:/custom/CustomMainToolBar.qml"); } + //-- Don't show instrument widget + CustomInstrumentWidget* instrumentWidget () final { return nullptr; } + bool showMavlinkLogOptions () const final { return false; } + + bool showFirmwareUpgrade () const final; + //-- We handle multiple vehicles in a custom way + bool enableMultiVehicleList () const final { return false; } + //-- We handle our own map scale + bool enableMapScale () const final { return false; } + // TODO: Can't access QGCPalette without some workarounds, change this upstream + QColor toolbarBackgroundLight () const final; + QColor toolbarBackgroundDark () const final; +}; + + +//----------------------------------------------------------------------------- +class CustomPlugin : public QGCCorePlugin +{ + Q_OBJECT +public: + CustomPlugin(QGCApplication* app, QGCToolbox *toolbox); + ~CustomPlugin(); + + // Overrides from QGCCorePlugin + QVariantList& settingsPages () final; + QGCOptions* options () final; + QString brandImageIndoor () const final; + QString brandImageOutdoor () const final; + bool overrideSettingsGroupVisibility (QString name) final; + VideoReceiver* createVideoReceiver (QObject* parent) final; + QQmlApplicationEngine* createRootWindow (QObject* parent) final; + bool adjustSettingMetaData (const QString& settingsGroup, FactMetaData& metaData) final; + void paletteOverride (QString colorName, QGCPalette::PaletteColorInfo_t& colorInfo) final; + // Overrides from QGCTool + void setToolbox (QGCToolbox* toolbox); + + const static QColor _windowShadeEnabledLightColor; + const static QColor _windowShadeEnabledDarkColor; + +private slots: + void _advancedChanged (bool advanced); + +private: + void + addSettingsEntry( + const QString& title, + const char* qmlFile, + const char* iconFile = nullptr); + +private: + CustomOptions* _pOptions = nullptr; + QVariantList _customSettingsList; // Not to be mixed up with QGCCorePlugin implementation +}; diff --git a/custom-example/src/CustomQuickInterface.cc b/custom-example/src/CustomQuickInterface.cc new file mode 100644 index 000000000..1d1858310 --- /dev/null +++ b/custom-example/src/CustomQuickInterface.cc @@ -0,0 +1,44 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom QtQuick Interface + * @author Gus Grubba + */ + +#include "QGCApplication.h" +#include "AppSettings.h" +#include "SettingsManager.h" +#include "MAVLinkLogManager.h" +#include "QGCMapEngine.h" +#include "QGCApplication.h" +#include "PositionManager.h" + +#include "CustomPlugin.h" +#include "CustomQuickInterface.h" + +#include +#include + +//----------------------------------------------------------------------------- +CustomQuickInterface::CustomQuickInterface(QObject* parent) + : QObject(parent) +{ + qCDebug(CustomLog) << "CustomQuickInterface Created"; +} + +//----------------------------------------------------------------------------- +CustomQuickInterface::~CustomQuickInterface() +{ + qCDebug(CustomLog) << "CustomQuickInterface Destroyed"; +} + +//----------------------------------------------------------------------------- +void +CustomQuickInterface::init() +{ +} diff --git a/custom-example/src/CustomQuickInterface.h b/custom-example/src/CustomQuickInterface.h new file mode 100644 index 000000000..6c1d37cc5 --- /dev/null +++ b/custom-example/src/CustomQuickInterface.h @@ -0,0 +1,32 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom QtQuick Interface + * @author Gus Grubba + */ + +#pragma once + +#include "Vehicle.h" + +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// QtQuick Interface (UI) +class CustomQuickInterface : public QObject +{ + Q_OBJECT +public: + CustomQuickInterface(QObject* parent = nullptr); + ~CustomQuickInterface(); + void init (); +}; diff --git a/custom-example/src/FirmwarePlugin/CustomCameraControl.cc b/custom-example/src/FirmwarePlugin/CustomCameraControl.cc new file mode 100644 index 000000000..ff67b4d71 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomCameraControl.cc @@ -0,0 +1,135 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Camera Controller + * @author Gus Grubba + * + */ + +#include "CustomCameraControl.h" +#include "QGCCameraIO.h" + +QGC_LOGGING_CATEGORY(CustomCameraLog, "CustomCameraLog") +QGC_LOGGING_CATEGORY(CustomCameraVerboseLog, "CustomCameraVerboseLog") + +//----------------------------------------------------------------------------- +CustomCameraControl::CustomCameraControl(const mavlink_camera_information_t *info, Vehicle* vehicle, int compID, QObject* parent) + : QGCCameraControl(info, vehicle, compID, parent) +{ + connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &CustomCameraControl::_mavlinkMessageReceived); +} + +//----------------------------------------------------------------------------- +bool +CustomCameraControl::takePhoto() +{ + bool res = false; + res = QGCCameraControl::takePhoto(); + return res; +} + +//----------------------------------------------------------------------------- +bool +CustomCameraControl::stopTakePhoto() +{ + bool res = QGCCameraControl::stopTakePhoto(); + return res; +} + +//----------------------------------------------------------------------------- +bool +CustomCameraControl::startVideo() +{ + bool res = QGCCameraControl::startVideo(); + return res; +} + +//----------------------------------------------------------------------------- +bool +CustomCameraControl::stopVideo() +{ + bool res = QGCCameraControl::stopVideo(); + return res; +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::setVideoMode() +{ + if(cameraMode() != CAM_MODE_VIDEO) { + qCDebug(CustomCameraLog) << "setVideoMode()"; + Fact* pFact = getFact(kCAM_MODE); + if(pFact) { + pFact->setRawValue(CAM_MODE_VIDEO); + _setCameraMode(CAM_MODE_VIDEO); + } + } +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::setPhotoMode() +{ + if(cameraMode() != CAM_MODE_PHOTO) { + qCDebug(CustomCameraLog) << "setPhotoMode()"; + Fact* pFact = getFact(kCAM_MODE); + if(pFact) { + pFact->setRawValue(CAM_MODE_PHOTO); + _setCameraMode(CAM_MODE_PHOTO); + } + } +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::_setVideoStatus(VideoStatus status) +{ + QGCCameraControl::_setVideoStatus(status); +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::_mavlinkMessageReceived(const mavlink_message_t& message) +{ + switch (message.msgid) { + case MAVLINK_MSG_ID_MOUNT_ORIENTATION: + _handleGimbalOrientation(message); + break; + } +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::_handleGimbalOrientation(const mavlink_message_t& message) +{ + mavlink_mount_orientation_t o; + mavlink_msg_mount_orientation_decode(&message, &o); + if(fabsf(_gimbalRoll - o.roll) > 0.5f) { + _gimbalRoll = o.roll; + emit gimbalRollChanged(); + } + if(fabsf(_gimbalPitch - o.pitch) > 0.5f) { + _gimbalPitch = o.pitch; + emit gimbalPitchChanged(); + } + if(fabsf(_gimbalYaw - o.yaw) > 0.5f) { + _gimbalYaw = o.yaw; + emit gimbalYawChanged(); + } + if(!_gimbalData) { + _gimbalData = true; + emit gimbalDataChanged(); + } +} + +//----------------------------------------------------------------------------- +void +CustomCameraControl::handleCaptureStatus(const mavlink_camera_capture_status_t& cap) +{ + QGCCameraControl::handleCaptureStatus(cap); +} diff --git a/custom-example/src/FirmwarePlugin/CustomCameraControl.h b/custom-example/src/FirmwarePlugin/CustomCameraControl.h new file mode 100644 index 000000000..b159c66b6 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomCameraControl.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Camera Controller + * @author Gus Grubba + * + */ + +#pragma once + +#include "QGCCameraControl.h" + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(CustomCameraLog) +Q_DECLARE_LOGGING_CATEGORY(CustomCameraVerboseLog) + +//----------------------------------------------------------------------------- +class CustomCameraControl : public QGCCameraControl +{ + Q_OBJECT +public: + CustomCameraControl(const mavlink_camera_information_t* info, Vehicle* vehicle, int compID, QObject* parent = nullptr); + + Q_PROPERTY(qreal gimbalRoll READ gimbalRoll NOTIFY gimbalRollChanged) + Q_PROPERTY(qreal gimbalPitch READ gimbalPitch NOTIFY gimbalPitchChanged) + Q_PROPERTY(qreal gimbalYaw READ gimbalYaw NOTIFY gimbalYawChanged) + Q_PROPERTY(bool gimbalData READ gimbalData NOTIFY gimbalDataChanged) + + bool takePhoto () override; + bool stopTakePhoto () override; + bool startVideo () override; + bool stopVideo () override; + void setVideoMode () override; + void setPhotoMode () override; + void handleCaptureStatus (const mavlink_camera_capture_status_t& capStatus) override; + + qreal gimbalRoll () { return static_cast(_gimbalRoll);} + qreal gimbalPitch () { return static_cast(_gimbalPitch); } + qreal gimbalYaw () { return static_cast(_gimbalYaw); } + bool gimbalData () { return _gimbalData; } + +private slots: + void _mavlinkMessageReceived (const mavlink_message_t& message); + +signals: + void gimbalRollChanged (); + void gimbalPitchChanged (); + void gimbalYawChanged (); + void gimbalDataChanged (); + +protected: + void _setVideoStatus (VideoStatus status) override; + +private: + void _handleGimbalOrientation(const mavlink_message_t& message); + +private: + float _gimbalRoll = 0.0; + float _gimbalPitch = 0.0; + float _gimbalYaw = 0.0; + bool _gimbalData = false; +}; diff --git a/custom-example/src/FirmwarePlugin/CustomCameraManager.cc b/custom-example/src/FirmwarePlugin/CustomCameraManager.cc new file mode 100644 index 000000000..df96efcb9 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomCameraManager.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Camera Controller + * @author Gus Grubba + * + */ + +#include "QGCApplication.h" +#include "CustomCameraManager.h" + +//----------------------------------------------------------------------------- +CustomCameraManager::CustomCameraManager(Vehicle *vehicle) + : QGCCameraManager(vehicle) +{ +} diff --git a/custom-example/src/FirmwarePlugin/CustomCameraManager.h b/custom-example/src/FirmwarePlugin/CustomCameraManager.h new file mode 100644 index 000000000..72e37e2d5 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomCameraManager.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Camera Controller + * @author Gus Grubba + * + */ + +#pragma once + +#include "QGCCameraManager.h" + +//----------------------------------------------------------------------------- +class CustomCameraManager : public QGCCameraManager +{ + Q_OBJECT +public: + CustomCameraManager(Vehicle* vehicle); +}; diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc new file mode 100644 index 000000000..3f3a664c4 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.cc @@ -0,0 +1,57 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Firmware Plugin (PX4) + * @author Gus Grubba + * + */ + +#include "CustomFirmwarePlugin.h" +#include "CustomAutoPilotPlugin.h" +#include "CustomCameraManager.h" +#include "CustomCameraControl.h" + +//----------------------------------------------------------------------------- +CustomFirmwarePlugin::CustomFirmwarePlugin() +{ +} + +//----------------------------------------------------------------------------- +AutoPilotPlugin* CustomFirmwarePlugin::autopilotPlugin(Vehicle* vehicle) +{ + return new CustomAutoPilotPlugin(vehicle, vehicle); +} + +//----------------------------------------------------------------------------- +QGCCameraManager* +CustomFirmwarePlugin::createCameraManager(Vehicle *vehicle) +{ + return new CustomCameraManager(vehicle); +} + +//----------------------------------------------------------------------------- +QGCCameraControl* +CustomFirmwarePlugin::createCameraControl(const mavlink_camera_information_t* info, Vehicle *vehicle, int compID, QObject* parent) +{ + return new CustomCameraControl(info, vehicle, compID, parent); +} + +//----------------------------------------------------------------------------- +const QVariantList& +CustomFirmwarePlugin::toolBarIndicators(const Vehicle* vehicle) +{ + Q_UNUSED(vehicle); + if(_toolBarIndicatorList.size() == 0) { + _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/GPSIndicator.qml"))); + _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/TelemetryRSSIIndicator.qml"))); + _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/RCRSSIIndicator.qml"))); + _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/BatteryIndicator.qml"))); + } + return _toolBarIndicatorList; +} + diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h new file mode 100644 index 000000000..47aa0aff8 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePlugin.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Firmware Plugin (PX4) + * @author Gus Grubba + * + */ + +#pragma once + +#include "FirmwarePlugin.h" +#include "PX4FirmwarePlugin.h" + +class CustomCameraManager; + +class CustomFirmwarePlugin : public PX4FirmwarePlugin +{ + Q_OBJECT +public: + CustomFirmwarePlugin(); + // FirmwarePlugin overrides + AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) override; + QGCCameraManager* createCameraManager (Vehicle *vehicle) override; + QGCCameraControl* createCameraControl (const mavlink_camera_information_t* info, Vehicle* vehicle, int compID, QObject* parent = nullptr) override; + const QVariantList& toolBarIndicators (const Vehicle* vehicle) override; +private: + QVariantList _toolBarIndicatorList; +}; diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc b/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc new file mode 100644 index 000000000..6be526827 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.cc @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Firmware Plugin Factory (PX4) + * @author Gus Grubba + * + */ + +#include "CustomFirmwarePluginFactory.h" +#include "CustomFirmwarePlugin.h" + +CustomFirmwarePluginFactory CustomFirmwarePluginFactoryImp; + +CustomFirmwarePluginFactory::CustomFirmwarePluginFactory() + : _pluginInstance(nullptr) +{ +} + +QList CustomFirmwarePluginFactory::supportedFirmwareTypes() const +{ + QList list; + list.append(MAV_AUTOPILOT_PX4); + return list; +} + +FirmwarePlugin* CustomFirmwarePluginFactory::firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) +{ + Q_UNUSED(vehicleType); + if (autopilotType == MAV_AUTOPILOT_PX4) { + if (!_pluginInstance) { + _pluginInstance = new CustomFirmwarePlugin; + } + return _pluginInstance; + } + return nullptr; +} diff --git a/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.h b/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.h new file mode 100644 index 000000000..6e8a28f71 --- /dev/null +++ b/custom-example/src/FirmwarePlugin/CustomFirmwarePluginFactory.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * + * (c) 2009-2019 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + * @file + * @brief Custom Firmware Plugin Factory (PX4) + * @author Gus Grubba + * + */ + +#pragma once + +#include "FirmwarePlugin.h" + +class CustomFirmwarePlugin; + +class CustomFirmwarePluginFactory : public FirmwarePluginFactory +{ + Q_OBJECT +public: + CustomFirmwarePluginFactory(); + QList supportedFirmwareTypes () const override; + FirmwarePlugin* firmwarePluginForAutopilot (MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) override; +private: + CustomFirmwarePlugin* _pluginInstance; +}; + +extern CustomFirmwarePluginFactory CustomFirmwarePluginFactoryImp; -- 2.22.0