diff --git a/custom-example/custom.pri b/custom-example/custom.pri
new file mode 100644
index 0000000000000000000000000000000000000000..c5695296443475a969aedf6a204c92cd4c348099
--- /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 0000000000000000000000000000000000000000..29e58bcc47bdbeb1cb82e7ae26bdf320c2f3a120
--- /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 0000000000000000000000000000000000000000..cb024582b7978d56913c328842a2e44c93d2a4d1
--- /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 0000000000000000000000000000000000000000..c8ab5ed31595cf13a5a62ed9d44f78d5d36c537c
--- /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 0000000000000000000000000000000000000000..8eeb161ec3297dd4d564921f47623febbc32f422
--- /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 0000000000000000000000000000000000000000..c6c906d60d26c5d942f0a6d4802bd8318d15f380
--- /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 0000000000000000000000000000000000000000..9c9a86693c6f7215c165442353aa247960e09fc5
--- /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 0000000000000000000000000000000000000000..e290f4a684e6c064ad34b1c4e62a284dc049c725
--- /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 0000000000000000000000000000000000000000..e46b4e2b98cfba8951674ee175584dd4d08829ad
--- /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 0000000000000000000000000000000000000000..bc1d9609e7aee8972f70b1e57459cea3b1093fe4
--- /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 0000000000000000000000000000000000000000..9c838de0ba14dcbd17637e5247ae9059497bf534
--- /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 0000000000000000000000000000000000000000..b4c6d03c11eaf300b861013d82b9132fca55abbb
--- /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 0000000000000000000000000000000000000000..80ec26b83ac9034eead0f0eb3ccbc9951cd98c13
--- /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 0000000000000000000000000000000000000000..ff74d3d1d08f239361b4df0eff5e1733de821ce8
--- /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 0000000000000000000000000000000000000000..b49cb4251db43ae9ed1b8c6cad085b43d572a5e6
--- /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 0000000000000000000000000000000000000000..281c731323ede2460701743845c2d11f3b76862d
--- /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 0000000000000000000000000000000000000000..bf8b6faa6c15ca76ea96d2113c56d2d481bb6114
--- /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 0000000000000000000000000000000000000000..80e638941ad33bf8ec664feb4fdd5b7cdbf5b7a6
--- /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 0000000000000000000000000000000000000000..97c97489b1809b00fb818ab1000d96f8a1b203ea
--- /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 0000000000000000000000000000000000000000..7889b35b1162b41224d03ca96bf69a044120f583
--- /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 0000000000000000000000000000000000000000..06817733f832f042402991076f4f38302f3c9d81
--- /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 0000000000000000000000000000000000000000..8417f6cc638f6ab5f0e13752e368e449581f39c6
--- /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 0000000000000000000000000000000000000000..bfb6bbc2bd5198f1ab04e88a2614f5ede02ed43c
--- /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 0000000000000000000000000000000000000000..677a40903ec8d381d77689d9cf6695e0adefe6c6
--- /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 0000000000000000000000000000000000000000..bdbc4efb355604faf54566d676ef0778d3dc1bcf
--- /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 0000000000000000000000000000000000000000..389b8b0cecfd281729a12b2a170a1ebc70ded62d
--- /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 0000000000000000000000000000000000000000..3e03abb955f92753ddbfc71e3d46b2ac8d519038
--- /dev/null
+++ b/custom-example/res/Images/odometer.svg
@@ -0,0 +1,23 @@
+
+
+
diff --git a/custom-example/res/Images/vertical_speed.svg b/custom-example/res/Images/vertical_speed.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2ce667890968eb7630bfb55cdf118828f157e8e6
--- /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
Binary files /dev/null and b/custom-example/res/Images/void.png differ
diff --git a/custom-example/res/MainToolbar/CustomArmedIndicator.qml b/custom-example/res/MainToolbar/CustomArmedIndicator.qml
new file mode 100644
index 0000000000000000000000000000000000000000..db1edb9427b9f21d5e50cc24122859b7e843ced5
--- /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 0000000000000000000000000000000000000000..6e58fb95a77210bf554dc7b41947a761ec59f5b2
--- /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 0000000000000000000000000000000000000000..6dfc4f166769b36ae20f4331fa02998b050de8bf
--- /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 0000000000000000000000000000000000000000..8a3f635eb1f0639dc01c7ccfbdcb13b535458327
--- /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 0000000000000000000000000000000000000000..40ffc05d998dea06309ccc237fbfb12c15cbecaa
--- /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 0000000000000000000000000000000000000000..3cffb64b123c4f23de2485d8984ac5006fee252f
--- /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 0000000000000000000000000000000000000000..89f95a785445604a4ff8bffa76f4a5e8e0c736e5
--- /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 0000000000000000000000000000000000000000..15a3eb80adb8cfa380958856683bd340ae103cf9
--- /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 0000000000000000000000000000000000000000..5942f3bf060f74e9ca8a8a441551f27be82018a1
--- /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 0000000000000000000000000000000000000000..9b20b4d895aea5dfb8c60e92201d217e12836804
--- /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 0000000000000000000000000000000000000000..aa005b10b40f318480ab8c0177ba81a7f8ad306d
--- /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 0000000000000000000000000000000000000000..be8af12ac836cae2cc7d723792a41bb4cf7fac56
--- /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 0000000000000000000000000000000000000000..1d1858310a9387e6520105d21869bc174846b006
--- /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 0000000000000000000000000000000000000000..6c1d37cc5fc57426eaf3c82c3f3ae5c4ded0b40c
--- /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 0000000000000000000000000000000000000000..ff67b4d71f94a9dd8d8a1d5259fe9848040821d5
--- /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 0000000000000000000000000000000000000000..b159c66b66b28e8fadb1addc552d926fdc3bb5ca
--- /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 0000000000000000000000000000000000000000..df96efcb9575c0306acad4cfbd97ee769131ea7f
--- /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 0000000000000000000000000000000000000000..72e37e2d558c71e9139bee82b4f68a076df3bc38
--- /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 0000000000000000000000000000000000000000..3f3a664c4531d63c119b76452dae9cd1b393f7ac
--- /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 0000000000000000000000000000000000000000..47aa0aff832d05c2b3bc5b01273238f6df70a76b
--- /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 0000000000000000000000000000000000000000..6be526827d2ade4c322955acae1d4ba6e9a00ed6
--- /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 0000000000000000000000000000000000000000..6e8a28f71003f7750a1209a512da00889ce3c740
--- /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;