Multi Vehicle Panel Overhaul

This commit is contained in:
Ahmet Kaan Demirci
2025-01-17 20:54:06 +03:00
committed by Don Gagne
parent 43504cb7ca
commit d32d5f4cf1
20 changed files with 660 additions and 199 deletions
+4
View File
@@ -127,6 +127,7 @@
<file alias="QGroundControl/Controls/MissionItemIndexLabel.qml">src/QmlControls/MissionItemIndexLabel.qml</file>
<file alias="QGroundControl/Controls/MissionItemMapVisual.qml">src/PlanView/MissionItemMapVisual.qml</file>
<file alias="QGroundControl/Controls/MissionItemStatus.qml">src/PlanView/MissionItemStatus.qml</file>
<file alias="QGroundControl/Controls/MvPanelPage.qml">src/QmlControls/MvPanelPage.qml</file>
<file alias="QGroundControl/Controls/OfflineMapButton.qml">src/QmlControls/OfflineMapButton.qml</file>
<file alias="QGroundControl/Controls/OfflineMapEditor.qml">src/QtLocationPlugin/QMLControl/OfflineMapEditor.qml</file>
<file alias="QGroundControl/Controls/OfflineMapInfo.qml">src/UI/preferences/OfflineMapInfo.qml</file>
@@ -161,12 +162,14 @@
<file alias="QGroundControl/Controls/QGCMenuSeparator.qml">src/QmlControls/QGCMenuSeparator.qml</file>
<file alias="QGroundControl/Controls/QGCMouseArea.qml">src/QmlControls/QGCMouseArea.qml</file>
<file alias="QGroundControl/Controls/QGCMovableItem.qml">src/QmlControls/QGCMovableItem.qml</file>
<file alias="QGroundControl/Controls/QGCPageIndicator.qml">src/QmlControls/QGCPageIndicator.qml</file>
<file alias="QGroundControl/Controls/QGCPopupDialog.qml">src/QmlControls/QGCPopupDialog.qml</file>
<file alias="QGroundControl/Controls/PipView.qml">src/QmlControls/PipView.qml</file>
<file alias="QGroundControl/Controls/PipState.qml">src/QmlControls/PipState.qml</file>
<file alias="QGroundControl/Controls/QGCRadioButton.qml">src/QmlControls/QGCRadioButton.qml</file>
<file alias="QGroundControl/Controls/QGCSimpleMessageDialog.qml">src/QmlControls/QGCSimpleMessageDialog.qml</file>
<file alias="QGroundControl/Controls/QGCSlider.qml">src/QmlControls/QGCSlider.qml</file>
<file alias="QGroundControl/Controls/QGCSwipeView.qml">src/QmlControls/QGCSwipeView.qml</file>
<file alias="QGroundControl/Controls/QGCSwitch.qml">src/QmlControls/QGCSwitch.qml</file>
<file alias="QGroundControl/Controls/QGCTabBar.qml">src/QmlControls/QGCTabBar.qml</file>
<file alias="QGroundControl/Controls/QGCTabButton.qml">src/QmlControls/QGCTabButton.qml</file>
@@ -342,6 +345,7 @@
<file alias="Viewer3D/Models3D/Line3D.qml">src/Viewer3D/Viewer3DQml/Models3D/Line3D.qml</file>
<file alias="Viewer3D/Models3D/Viewer3DVehicleItems.qml">src/Viewer3D/Viewer3DQml/Models3D/Viewer3DVehicleItems.qml</file>
<file alias="Viewer3D/Viewer3DProgressBar.qml">src/Viewer3D/Viewer3DQml/Viewer3DProgressBar.qml</file>
<file alias="QGroundControl/FlightDisplay/FlyViewTopRightPanel.qml">src/FlightDisplay/FlyViewTopRightPanel.qml</file>
</qresource>
<qresource prefix="/FirstRunPromptDialogs">
<file alias="UnitsFirstRunPrompt.qml">src/FirstRunPromptDialogs/UnitsFirstRunPrompt.qml</file>
@@ -20,29 +20,6 @@ import QGroundControl.ScreenTools
ColumnLayout {
width: _rightPanelWidth
RowLayout {
id: multiVehiclePanelSelector
Layout.alignment: Qt.AlignTop
spacing: ScreenTools.defaultFontPixelWidth
visible: QGroundControl.multiVehicleManager.vehicles.count > 1 && QGroundControl.corePlugin.options.flyView.showMultiVehicleList
QGCMapPalette { id: mapPal; lightColors: true }
QGCRadioButton {
id: singleVehicleRadio
text: qsTr("Single")
checked: _showSingleVehicleUI
onClicked: _showSingleVehicleUI = true
textColor: mapPal.text
}
QGCRadioButton {
text: qsTr("Multi-Vehicle")
textColor: mapPal.text
onClicked: _showSingleVehicleUI = false
}
}
TerrainProgress {
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: _rightPanelWidth
@@ -54,7 +31,7 @@ ColumnLayout {
Loader {
id: photoVideoControlLoader
Layout.alignment: Qt.AlignTop | Qt.AlignRight
sourceComponent: globals.activeVehicle && _showSingleVehicleUI ? photoVideoControlComponent : undefined
sourceComponent: globals.activeVehicle ? photoVideoControlComponent : undefined
property real rightEdgeCenterInset: visible ? parent.width - x : 0
@@ -65,10 +42,4 @@ ColumnLayout {
}
}
}
MultiVehicleList {
Layout.preferredWidth: _rightPanelWidth
Layout.fillHeight: true
visible: !_showSingleVehicleUI
}
}
+252
View File
@@ -0,0 +1,252 @@
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick
import QtQuick.Layouts
import QGroundControl
import QGroundControl.FactSystem
import QGroundControl.FactControls
import QGroundControl.Controls
import QGroundControl.FlightDisplay
import QGroundControl.FlightMap
import QGroundControl.Palette
import QGroundControl.ScreenTools
Rectangle {
id: topRightPanel
width: contentWidth
height: Math.max(contentHeight, minimumHeight)
color: qgcPal.toolbarBackground
radius: ScreenTools.defaultFontPixelHeight / 2
visible: !QGroundControl.videoManager.fullScreen && _multipleVehicles && _settingEnableMVPanel
clip: true
property bool _settingEnableMVPanel: QGroundControl.settingsManager.appSettings.enableMultiVehiclePanel.value
property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1
property var vehicles: QGroundControl.multiVehicleManager.vehicles
property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles
property real contentWidth: Math.max(
multiVehicleList.implicitWidth,
swipeViewContainer.implicitWidth
) + ScreenTools.defaultFontPixelHeight
property real contentHeight: Math.min(
maximumHeight,
topRightPanelColumnLayout.implicitHeight + topRightPanelColumnLayout.spacing * ( topRightPanelColumnLayout.children.length - 1)
)
property real minimumHeight: selectedVehiclesLabel.height + swipeViewContainer.height
property real maximumHeight
QGCPalette { id: qgcPal }
ColumnLayout {
id: topRightPanelColumnLayout
anchors.fill: parent
anchors.margins: ScreenTools.defaultFontPixelHeight / 2
spacing: ScreenTools.defaultFontPixelHeight / 2
QGCLabel {
id: selectedVehiclesLabel
text: {
let ids = Array.from({length: selectedVehicles.count}, (_, i) =>
selectedVehicles.get(i).id
).sort((a, b) => a - b)
.join(", ")
return qsTr("Selected: ") + ids
}
}
MultiVehicleList {
id: multiVehicleList
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: multiVehicleList.innerColumnHeight * vehicles.count - _margins * 3
Rectangle {
anchors.fill: parent
visible: topRightPanel.height === maximumHeight
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 0
height: 1
color: QGroundControl.globalPalette.groupBorder
}
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop { position: 0.00; color: topRightPanel.color }
GradientStop { position: 0.05; color: "transparent" }
GradientStop { position: 0.95; color: "transparent" }
GradientStop { position: 1.00; color: topRightPanel.color }
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 0
height: 1
color: QGroundControl.globalPalette.groupBorder
}
}
}
Rectangle {
id: swipeViewContainer
Layout.fillWidth: true
implicitHeight: swipePages.implicitHeight
implicitWidth: swipePages.implicitWidth
color: "transparent"
QGCSwipeView {
id: swipePages
anchors.fill: parent
spacing: ScreenTools.defaultFontPixelHeight
implicitHeight: Math.max(buttonsPage.implicitHeight, photoVideoPage.implicitHeight)
implicitWidth: Math.max(buttonsPage.implicitWidth, photoVideoPage.implicitWidth)
MvPanelPage {
id: buttonsPage
implicitHeight: buttonsColumnLayout.implicitHeight + ScreenTools.defaultFontPixelHeight * 2
implicitWidth: buttonsColumnLayout.implicitWidth + ScreenTools.defaultFontPixelHeight * 2
ColumnLayout {
id: buttonsColumnLayout
anchors.right: parent.right
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: ScreenTools.defaultFontPixelHeight / 2
implicitHeight: Math.max(selectionRowLayout.height, actionRowLayout.height) + ScreenTools.defaultFontPixelHeight * 2
implicitWidth: Math.max(selectionRowLayout.width, actionRowLayout.width) + ScreenTools.defaultFontPixelHeight * 4
QGCLabel {
text: qsTr("Multi Vehicle Selection")
Layout.alignment: Qt.AlignHCenter
}
RowLayout {
id: selectionRowLayout
Layout.alignment: Qt.AlignHCenter
QGCButton {
text: qsTr("Select All")
enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count !== QGroundControl.multiVehicleManager.vehicles.count
onClicked: multiVehicleList.selectAll()
}
QGCButton {
text: qsTr("Deselect All")
enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count > 0
onClicked: multiVehicleList.deselectAll()
}
}
QGCLabel {
text: qsTr("Multi Vehicle Actions")
Layout.alignment: Qt.AlignHCenter
}
RowLayout {
id: actionRowLayout
Layout.alignment: Qt.AlignHCenter
QGCButton {
text: qsTr("Arm")
enabled: multiVehicleList.armAvailable()
onClicked: _guidedController.confirmAction(_guidedController.actionMVArm)
Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75
leftPadding: 0
rightPadding: 0
}
QGCButton {
text: qsTr("Disarm")
enabled: multiVehicleList.disarmAvailable()
onClicked: _guidedController.confirmAction(_guidedController.actionMVDisarm)
Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75
leftPadding: 0
rightPadding: 0
}
QGCButton {
text: qsTr("Start")
enabled: multiVehicleList.startAvailable()
onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission)
Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75
leftPadding: 0
rightPadding: 0
}
QGCButton {
text: qsTr("Pause")
enabled: multiVehicleList.pauseAvailable()
onClicked: _guidedController.confirmAction(_guidedController.actionMVPause)
Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75
leftPadding: 0
rightPadding: 0
}
}
}
} // Page 1
MvPanelPage {
id: photoVideoPage
implicitHeight: photoVideoControlLoader.implicitHeight + ScreenTools.defaultFontPixelHeight * 2
implicitWidth: photoVideoControlLoader.implicitWidth + ScreenTools.defaultFontPixelHeight * 2
// We use a Loader to load the photoVideoControlComponent only when the active vehicle is not null
// This make it easier to implement PhotoVideoControl without having to check for the mavlink camera
// to be null all over the place
Loader {
id: photoVideoControlLoader
anchors.horizontalCenter: parent.horizontalCenter
sourceComponent: globals.activeVehicle ? photoVideoControlComponent : undefined
property real rightEdgeCenterInset: visible ? parent.width - x : 0
Component {
id: photoVideoControlComponent
PhotoVideoControl {
}
}
}
} // Page 2
} // QGCSwipeView
QGCPageIndicator {
id: pageIndicator
count: swipePages.count
currentIndex: swipePages.currentIndex
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: ScreenTools.defaultFontPixelHeight / 4
delegate: Rectangle {
height: ScreenTools.defaultFontPixelHeight / 2
width: height
radius: width / 2
color: model.index === pageIndicator.currentIndex ? qgcPal.text : qgcPal.button
opacity: model.index === pageIndicator.currentIndex ? 0.9 : 0.3
}
}
}
}
}
+17 -3
View File
@@ -59,17 +59,30 @@ Item {
leftEdgeTopInset: toolStrip.leftEdgeTopInset
leftEdgeCenterInset: toolStrip.leftEdgeCenterInset
leftEdgeBottomInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.leftEdgeBottomInset : parentToolInsets.leftEdgeBottomInset
rightEdgeTopInset: topRightColumnLayout.rightEdgeTopInset
rightEdgeCenterInset: topRightColumnLayout.rightEdgeCenterInset
rightEdgeTopInset: topRightPanel.rightEdgeTopInset
rightEdgeCenterInset: topRightPanel.rightEdgeCenterInset
rightEdgeBottomInset: bottomRightRowLayout.rightEdgeBottomInset
topEdgeLeftInset: toolStrip.topEdgeLeftInset
topEdgeCenterInset: mapScale.topEdgeCenterInset
topEdgeRightInset: topRightColumnLayout.topEdgeRightInset
topEdgeRightInset: topRightPanel.topEdgeRightInset
bottomEdgeLeftInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.bottomEdgeLeftInset : parentToolInsets.bottomEdgeLeftInset
bottomEdgeCenterInset: bottomRightRowLayout.bottomEdgeCenterInset
bottomEdgeRightInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.bottomEdgeRightInset : bottomRightRowLayout.bottomEdgeRightInset
}
FlyViewTopRightPanel {
id: topRightPanel
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: _layoutMargin
anchors.rightMargin: _layoutMargin
maximumHeight: parent.height - (bottomRightRowLayout.height + _margins * 5)
property real topEdgeRightInset: height + _layoutMargin
property real rightEdgeTopInset: width + _layoutMargin
property real rightEdgeCenterInset: rightEdgeTopInset
}
FlyViewTopRightColumnLayout {
id: topRightColumnLayout
anchors.margins: _layoutMargin
@@ -77,6 +90,7 @@ Item {
anchors.bottom: bottomRightRowLayout.top
anchors.right: parent.right
spacing: _layoutSpacing
visible: !topRightPanel.visible
property real topEdgeRightInset: childrenRect.height + _layoutMargin
property real rightEdgeTopInset: width + _layoutMargin
+44 -10
View File
@@ -34,8 +34,10 @@ Item {
readonly property string emergencyStopTitle: qsTr("EMERGENCY STOP")
readonly property string armTitle: qsTr("Arm")
readonly property string mvArmTitle: qsTr("Arm (MV)")
readonly property string forceArmTitle: qsTr("Force Arm")
readonly property string disarmTitle: qsTr("Disarm")
readonly property string mvDisarmTitle: qsTr("Disarm (MV)")
readonly property string rtlTitle: qsTr("Return")
readonly property string takeoffTitle: qsTr("Takeoff")
readonly property string gripperTitle: qsTr("Gripper Function")
@@ -62,12 +64,15 @@ Item {
readonly property string changeHeadingTitle: qsTr("Change Heading")
readonly property string armMessage: qsTr("Arm the vehicle.")
readonly property string mvArmMessage: qsTr("Arm selected vehicles.")
readonly property string forceArmMessage: qsTr("WARNING: This will force arming of the vehicle bypassing any safety checks.")
readonly property string disarmMessage: qsTr("Disarm the vehicle")
readonly property string mvDisarmMessage: qsTr("Disarm selected vehicles.")
readonly property string emergencyStopMessage: qsTr("WARNING: THIS WILL STOP ALL MOTORS. IF VEHICLE IS CURRENTLY IN THE AIR IT WILL CRASH.")
readonly property string takeoffMessage: qsTr("Takeoff from ground and hold position.")
readonly property string gripperMessage: qsTr("Grab or Release the cargo")
readonly property string gripperMessage: qsTr("Grab or Release the cargo")
readonly property string startMissionMessage: qsTr("Takeoff from ground and start the current mission.")
readonly property string mvStartMissionMessage: qsTr("Takeoff from ground and start the current mission for selected vehicles.")
readonly property string continueMissionMessage: qsTr("Continue the mission from the current waypoint.")
readonly property string resumeMissionUploadFailMessage: qsTr("Upload of resume mission failed. Confirm to retry upload")
readonly property string landMessage: qsTr("Land the vehicle at the current position.")
@@ -80,7 +85,7 @@ Item {
readonly property string orbitMessage: qsTr("Orbit the vehicle around the specified location.")
readonly property string landAbortMessage: qsTr("Abort the landing sequence.")
readonly property string pauseMessage: qsTr("Pause the vehicle at it's current position, adjusting altitude up or down as needed.")
readonly property string mvPauseMessage: qsTr("Pause all vehicles at their current position.")
readonly property string mvPauseMessage: qsTr("Pause selected vehicles at their current position.")
readonly property string vtolTransitionFwdMessage: qsTr("Transition VTOL to fixed wing flight.")
readonly property string vtolTransitionMRMessage: qsTr("Transition VTOL to multi-rotor flight.")
readonly property string roiMessage: qsTr("Make the specified location a Region Of Interest.")
@@ -119,6 +124,10 @@ Item {
readonly property int actionSetEstimatorOrigin: 28
readonly property int actionSetFlightMode: 29
readonly property int actionChangeHeading: 30
readonly property int actionMVArm: 31
readonly property int actionMVDisarm: 32
readonly property int customActionStart: 10000 // Custom actions ids should start here so that they don't collide with the built in actions
@@ -417,6 +426,11 @@ Item {
confirmDialog.message = armMessage
confirmDialog.hideTrigger = Qt.binding(function() { return !showArm })
break;
case actionMVArm:
confirmDialog.title = mvArmTitle
confirmDialog.message = mvArmMessage
confirmDialog.hideTrigger = true
break;
case actionForceArm:
confirmDialog.title = forceArmTitle
confirmDialog.message = forceArmMessage
@@ -430,6 +444,11 @@ Item {
confirmDialog.message = disarmMessage
confirmDialog.hideTrigger = Qt.binding(function() { return !showDisarm })
break;
case actionMVDisarm:
confirmDialog.title = mvDisarmTitle
confirmDialog.message = mvDisarmMessage
confirmDialog.hideTrigger = true
break;
case actionEmergencyStop:
confirmDialog.title = emergencyStopTitle
confirmDialog.message = emergencyStopMessage
@@ -449,7 +468,7 @@ Item {
break;
case actionMVStartMission:
confirmDialog.title = mvStartMissionTitle
confirmDialog.message = startMissionMessage
confirmDialog.message = mvStartMissionMessage
confirmDialog.hideTrigger = true
break;
case actionContinueMission:
@@ -576,7 +595,7 @@ Item {
// Executes the specified action
function executeAction(actionCode, actionData, sliderOutputValue, optionChecked) {
var i;
var rgVehicle;
var selectedVehicles;
switch (actionCode) {
case actionRTL:
_activeVehicle.guidedModeRTL(optionChecked)
@@ -597,20 +616,35 @@ Item {
_activeVehicle.startMission()
break
case actionMVStartMission:
rgVehicle = QGroundControl.multiVehicleManager.vehicles
for (i = 0; i < rgVehicle.count; i++) {
rgVehicle.get(i).startMission()
selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles
for (i = 0; i < selectedVehicles.count; i++) {
var vehicle = selectedVehicles.get(i)
if (vehicle.armed === true){
vehicle.startMission()
}
}
break
case actionArm:
_activeVehicle.armed = true
break
case actionMVArm:
selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles
for (i = 0; i < selectedVehicles.count; i++) {
selectedVehicles.get(i).armed = true
}
break
case actionForceArm:
_activeVehicle.forceArm()
break
case actionDisarm:
_activeVehicle.armed = false
break
case actionMVDisarm:
selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles
for (i = 0; i < selectedVehicles.count; i++) {
selectedVehicles.get(i).armed = false
}
break
case actionEmergencyStop:
_activeVehicle.emergencyStop()
break
@@ -638,9 +672,9 @@ Item {
_activeVehicle.guidedModeChangeAltitude(altitudeChangeInMeters, true /* pauseVehicle */)
break
case actionMVPause:
rgVehicle = QGroundControl.multiVehicleManager.vehicles
for (i = 0; i < rgVehicle.count; i++) {
rgVehicle.get(i).pauseVehicle()
selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles
for (i = 0; i < selectedVehicles.count; i++) {
selectedVehicles.get(i).pauseVehicle()
}
break
case actionVtolTransitionToFwdFlight:
+142 -121
View File
@@ -19,66 +19,102 @@ import QGroundControl.Vehicle
import QGroundControl.FlightMap
Item {
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _widgetHeight: ScreenTools.defaultFontPixelHeight * 3
property color _textColor: "black"
property real _rectOpacity: 0.8
property var _guidedController: globals.guidedControllerFlyView
property real _margin: ScreenTools.defaultFontPixelWidth / 2
property real _widgetHeight: ScreenTools.defaultFontPixelHeight * 2
property var _guidedController: globals.guidedControllerFlyView
property var _activeVehicleColor: "green"
property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles
QGCPalette { id: qgcPal }
property real innerColumnHeight
Rectangle {
id: mvCommands
anchors.left: parent.left
anchors.right: parent.right
height: mvCommandsColumn.height + (_margin *2)
color: qgcPal.missionItemEditor
opacity: _rectOpacity
radius: _margin
DeadMouseArea {
anchors.fill: parent
}
Column {
id: mvCommandsColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: _margin
QGCLabel {
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("The following commands will be applied to all vehicles")
color: _textColor
wrapMode: Text.WordWrap
font.pointSize: ScreenTools.smallFontPointSize
function armAvailable() {
for (var i = 0; i < selectedVehicles.count; i++) {
var vehicle = selectedVehicles.get(i)
if (vehicle.armed === false) {
return true
}
}
return false
}
Row {
spacing: _margin
QGCButton {
text: qsTr("Pause")
onClicked: _guidedController.confirmAction(_guidedController.actionMVPause)
}
function disarmAvailable() {
for (var i = 0; i < selectedVehicles.count; i++) {
var vehicle = selectedVehicles.get(i)
if (vehicle.armed === true) {
return true
}
}
return false
}
QGCButton {
text: qsTr("Start Mission")
onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission)
}
function startAvailable() {
for (var i = 0; i < selectedVehicles.count; i++) {
var vehicle = selectedVehicles.get(i)
if (vehicle.armed === true && vehicle.flightMode !== vehicle.missionFlightMode){
return true
}
}
return false
}
function pauseAvailable() {
for (var i = 0; i < selectedVehicles.count; i++) {
var vehicle = selectedVehicles.get(i)
if (vehicle.armed === true && vehicle.pauseVehicleSupported) {
return true
}
}
return false
}
function selectVehicle(vehicleId) {
QGroundControl.multiVehicleManager.selectVehicle(vehicleId)
}
function deselectVehicle(vehicleId) {
QGroundControl.multiVehicleManager.deselectVehicle(vehicleId)
}
function toggleSelect(vehicleId) {
if (!vehicleSelected(vehicleId)) {
selectVehicle(vehicleId)
} else {
deselectVehicle(vehicleId)
}
}
function selectAll() {
var vehicles = QGroundControl.multiVehicleManager.vehicles
for (var i = 0; i < vehicles.count; i++) {
var vehicle = vehicles.get(i)
var vehicleId = vehicle.id
if (!vehicleSelected(vehicleId)) {
selectVehicle(vehicleId)
}
}
}
function deselectAll() {
QGroundControl.multiVehicleManager.deselectAllVehicles()
}
function vehicleSelected(vehicleId) {
for (var i = 0; i < selectedVehicles.count; i++ ) {
var currentId = selectedVehicles.get(i).id
if (vehicleId === currentId) {
return true
}
}
return false
}
QGCListView {
id: missionItemEditorListView
id: vehicleList
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: _margin
anchors.top: mvCommands.bottom
anchors.top: parent.top
anchors.bottom: parent.bottom
spacing: ScreenTools.defaultFontPixelHeight / 2
orientation: ListView.Vertical
@@ -89,95 +125,80 @@ Item {
property real _cacheBuffer: height * 2
delegate: Rectangle {
width: missionItemEditorListView.width
height: innerColumn.y + innerColumn.height + _margin
color: qgcPal.missionItemEditor
opacity: _rectOpacity
radius: _margin
width: vehicleList.width
height: innerColumn.y + innerColumn.height + _margin
color: QGroundControl.multiVehicleManager.activeVehicle == _vehicle ? _activeVehicleColor : qgcPal.button
radius: _margin
border.width: _vehicle && vehicleSelected(_vehicle.id) ? 2 : 0
border.color: qgcPal.text
property var _vehicle: object
ColumnLayout {
id: innerColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.left
spacing: _margin
Rectangle {
height: parent.height
width: innerColumn.width
anchors.horizontalCenter: parent.horizontalCenter
color: "transparent"
RowLayout {
Layout.fillWidth: true
id: innerColumn
anchors.margins: _margin
spacing: _margin
onHeightChanged: { innerColumnHeight = height + _margin * 2 + spacing * 2 }
QGCCompassWidget {
id: compassWidget
size: _widgetHeight
usedByMultipleVehicleList: true
vehicle: _vehicle
}
QGCLabel {
Layout.alignment: Qt.AlignTop
text: _vehicle ? _vehicle.id : ""
color: _textColor
text: " | "
font.pointSize: ScreenTools.largeFontPointSize
color: qgcPal.text
Layout.alignment: Qt.AlignHCenter
}
QGCLabel {
text: _vehicle ? _vehicle.id : ""
font.pointSize: ScreenTools.largeFontPointSize
color: qgcPal.text
Layout.alignment: Qt.AlignHCenter
}
QGCLabel {
text: " | "
font.pointSize: ScreenTools.largeFontPointSize
color: qgcPal.text
Layout.alignment: Qt.AlignHCenter
}
ColumnLayout {
Layout.alignment: Qt.AlignCenter
spacing: _margin
spacing: _margin
Layout.rightMargin: compassWidget.width / 4
Layout.alignment: Qt.AlignCenter
FlightModeMenu {
Layout.alignment: Qt.AlignHCenter
font.pointSize: ScreenTools.largeFontPointSize
color: _textColor
currentVehicle: _vehicle
Layout.alignment: Qt.AlignHCenter
font.pointSize: ScreenTools.largeFontPointSize
color: qgcPal.text
currentVehicle: _vehicle
}
QGCLabel {
Layout.alignment: Qt.AlignHCenter
text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed")
color: _textColor
Layout.alignment: Qt.AlignHCenter
text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed")
color: qgcPal.text
}
}
}
}
QGCCompassWidget {
size: _widgetHeight
usedByMultipleVehicleList: true
vehicle: _vehicle
}
QGCAttitudeWidget {
size: _widgetHeight
vehicle: _vehicle
}
} // RowLayout
Row {
spacing: ScreenTools.defaultFontPixelWidth
QGCButton {
text: qsTr("Arm")
visible: _vehicle && !_vehicle.armed
onClicked: _vehicle.armed = true
}
QGCButton {
text: qsTr("Start Mission")
visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.missionFlightMode
onClicked: _vehicle.startMission()
}
QGCButton {
text: qsTr("Pause")
visible: _vehicle && _vehicle.armed && _vehicle.pauseVehicleSupported
onClicked: _vehicle.pauseVehicle()
}
QGCButton {
text: qsTr("RTL")
visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.rtlFlightMode
onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode
}
QGCButton {
text: qsTr("Take control")
visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.takeControlFlightMode
onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode
}
} // Row
} // ColumnLayout
} // delegate - Rectangle
} // QGCListView
} // Item
QGCMouseArea {
anchors.fill: parent
onClicked: toggleSelect(_vehicle.id)
}
}
}
}
@@ -23,6 +23,7 @@ Canvas {
property real compassSize
property real heading
property bool simplified: false
property var _qgcPal: QGroundControl.globalPalette
@@ -33,7 +34,7 @@ Canvas {
onPaint: {
var ctx = getContext("2d")
ctx.strokeStyle = _qgcPal.text
ctx.strokeStyle = simplified ? "#EE3424" : _qgcPal.text
ctx.fillStyle = "#EE3424"
ctx.lineWidth = 1
ctx.beginPath()
+10 -5
View File
@@ -21,13 +21,16 @@ Rectangle {
height: size
radius: width / 2
color: qgcPal.window
border.color: qgcPal.text
border.width: usedByMultipleVehicleList ? 1 : 0
opacity: vehicle && usedByMultipleVehicleList && !vehicle.armed ? 0.5 : 1
property real size: _defaultSize
property var vehicle: null
property bool usedByMultipleVehicleList: false
property real _defaultSize: ScreenTools.defaultFontPixelHeight * (10)
property real _sizeRatio: ScreenTools.isTinyScreen ? (size / _defaultSize) * 0.5 : size / _defaultSize
property real _defaultSize: usedByMultipleVehicleList ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight * 10
property real _sizeRatio: (usedByMultipleVehicleList || ScreenTools.isTinyScreen) ? (size / _defaultSize) * 0.5 : size / _defaultSize
property int _fontSize: ScreenTools.defaultFontPointSize * _sizeRatio < 8 ? 8 : ScreenTools.defaultFontPointSize * _sizeRatio
property real _heading: vehicle ? vehicle.heading.rawValue : 0
property real _headingToHome: vehicle ? vehicle.headingToHome.rawValue : 0
@@ -36,7 +39,7 @@ Rectangle {
property real _courseOverGround: vehicle ? vehicle.gps.courseOverGround.rawValue : 0
property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings
property bool _showAdditionalIndicators: _flyViewSettings.showAdditionalIndicatorsCompass.value && !usedByMultipleVehicleList
property bool _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass.value
property bool _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass.value && !usedByMultipleVehicleList
function showCOG(){
if (_groundSpeed < 0.5) {
@@ -75,12 +78,14 @@ Rectangle {
}
CompassDial {
anchors.fill: parent
anchors.fill: parent
visible: !usedByMultipleVehicleList
}
CompassHeadingIndicator {
compassSize: size
heading: _heading
simplified: usedByMultipleVehicleList
}
Image {
@@ -144,7 +149,7 @@ Rectangle {
QGCLabel {
anchors.horizontalCenter: parent.horizontalCenter
y: size * 0.74
text: vehicle ? _heading.toFixed(0) + "°" : ""
text: vehicle && !usedByMultipleVehicleList ? _heading.toFixed(0) + "°" : ""
horizontalAlignment: Text.AlignHCenter
}
}
+28
View File
@@ -0,0 +1,28 @@
import QtQuick
import QtQuick.Controls
import QGroundControl
import QGroundControl.ScreenTools
import QGroundControl.Palette
Item {
property bool showBorder: true
property real contentMargin: ScreenTools.defaultFontPixelHeight
default property alias contentChildren: contentContainer.data
Rectangle {
color: "transparent"
anchors.fill: parent
border.color: QGroundControl.globalPalette.groupBorder
border.width: showBorder ? 1 : 0
radius: ScreenTools.defaultFontPixelHeight / 3
Item {
id: contentContainer
anchors.fill: parent
anchors.margins: contentMargin / 2
anchors.bottomMargin: contentMargin * 2
}
}
}
+6
View File
@@ -0,0 +1,6 @@
import QtQuick
import QtQuick.Controls
PageIndicator {
}
+7
View File
@@ -0,0 +1,7 @@
import QtQuick
import QtQuick.Controls
SwipeView {
}
@@ -49,6 +49,7 @@ MissionItemEditor 1.0 MissionItemEditor.qml
MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml
MissionItemMapVisual 1.0 MissionItemMapVisual.qml
MissionItemStatus 1.0 MissionItemStatus.qml
MvPanelPage 1.0 MvPanelPage.qml
OfflineMapButton 1.0 OfflineMapButton.qml
OfflineMapEditor 1.0 OfflineMapEditor.qml
OfflineMapInfo 1.0 OfflineMapInfo.qml
@@ -84,11 +85,13 @@ QGCMenuSeparator 1.0 QGCMenuSeparator.qml
QGCMouseArea 1.0 QGCMouseArea.qml
QGCMovableItem 1.0 QGCMovableItem.qml
QGCOptionsComboBox 1.0 QGCOptionsComboBox.qml
QGCPageIndicator 1.0 QGCPageIndicator.qml
QGCPopupDialog 1.0 QGCPopupDialog.qml
QGCRadioButton 1.0 QGCRadioButton.qml
QGCRoundButton 1.0 QGCRoundButton.qml
QGCSimpleMessageDialog 1.0 QGCSimpleMessageDialog.qml
QGCSlider 1.0 QGCSlider.qml
QGCSwipeView 1.0 QGCSwipeView.qml
QGCSwitch 1.0 QGCSwitch.qml
QGCTabBar 1.0 QGCTabBar.qml
QGCTabButton 1.0 QGCTabButton.qml
@@ -16,6 +16,7 @@ FlyViewToolBarIndicators 1.0 FlyViewToolBarIndicators.qml
FlyViewToolStrip 1.0 FlyViewToolStrip.qml
FlyViewToolStripActionList 1.0 FlyViewToolStripActionList.qml
FlyViewTopRightColumnLayout 1.0 FlyViewTopRightColumnLayout.qml
FlyViewTopRightPanel 1.0 FlyViewTopRightPanel.qml
FlyViewVideo 1.0 FlyViewVideo.qml
FlyViewWidgetLayer 1.0 FlyViewWidgetLayer.qml
GripperMenu 1.0 GripperMenu.qml
+7
View File
@@ -138,6 +138,13 @@
"type": "bool",
"default": false
},
{
"name": "enableMultiVehiclePanel",
"shortDesc": "Enable Multi-Vehicle Panel",
"longDesc": "Enable Multi-Vehicle Panel when multiple vehicles are connected.",
"type": "bool",
"default": true
},
{
"name": "appFontPointSize",
"shortDesc": "Application font size",
+1
View File
@@ -157,6 +157,7 @@ DECLARE_SETTINGSFACT(AppSettings, savePath)
DECLARE_SETTINGSFACT(AppSettings, androidSaveToSDCard)
DECLARE_SETTINGSFACT(AppSettings, useChecklist)
DECLARE_SETTINGSFACT(AppSettings, enforceChecklist)
DECLARE_SETTINGSFACT(AppSettings, enableMultiVehiclePanel)
DECLARE_SETTINGSFACT(AppSettings, mapboxToken)
DECLARE_SETTINGSFACT(AppSettings, mapboxAccount)
DECLARE_SETTINGSFACT(AppSettings, mapboxStyle)
+1
View File
@@ -43,6 +43,7 @@ public:
DEFINE_SETTINGFACT(androidSaveToSDCard)
DEFINE_SETTINGFACT(useChecklist)
DEFINE_SETTINGFACT(enforceChecklist)
DEFINE_SETTINGFACT(enableMultiVehiclePanel)
DEFINE_SETTINGFACT(mapboxToken)
DEFINE_SETTINGFACT(mapboxAccount)
DEFINE_SETTINGFACT(mapboxStyle)
+8
View File
@@ -28,6 +28,7 @@ SettingsPage {
property var _customMavlinkActionsSettings: _settingsManager.customMavlinkActionsSettings
property Fact _virtualJoystick: _settingsManager.appSettings.virtualJoystick
property Fact _virtualJoystickAutoCenterThrottle: _settingsManager.appSettings.virtualJoystickAutoCenterThrottle
property Fact _enableMultiVehiclePanel: _settingsManager.appSettings.enableMultiVehiclePanel
property Fact _showAdditionalIndicatorsCompass: _flyViewSettings.showAdditionalIndicatorsCompass
property Fact _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass
property Fact _guidedMinimumAltitude: _flyViewSettings.guidedMinimumAltitude
@@ -69,6 +70,13 @@ SettingsPage {
property Fact _enforceChecklist: _settingsManager.appSettings.enforceChecklist
}
FactCheckBoxSlider {
Layout.fillWidth: true
text: qsTr("Enable Multi-Vehicle Panel")
fact: _enableMultiVehiclePanel
visible: _enableMultiVehiclePanel.visible
}
FactCheckBoxSlider {
Layout.fillWidth: true
text: qsTr("Keep Map Centered On Vehicle")
+80 -29
View File
@@ -7,61 +7,112 @@
*
****************************************************************************/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QGroundControl
import QGroundControl.FactSystem
import QGroundControl.FactControls
import QGroundControl.Controls
import QGroundControl.MultiVehicleManager
import QGroundControl.ScreenTools
import QGroundControl.Palette
//-------------------------------------------------------------------------
//-- Multiple Vehicle Selector
QGCComboBox {
anchors.verticalCenter: parent.verticalCenter
font.pointSize: ScreenTools.mediumFontPointSize
currentIndex: -1
sizeToContents: true
model: _vehicleModel
RowLayout {
id: control
spacing: 0
property bool showIndicator: _multipleVehicles
property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1
property var _vehicleModel: [ ]
property bool showIndicator: _multipleVehicles
property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1
property var _vehicleModel: [ ]
Connections {
target: QGroundControl.multiVehicleManager.vehicles
onCountChanged: _updateVehicleModel()
onCountChanged: _updateVehicleModel()
}
Component.onCompleted: _updateVehicleModel()
on_ActiveVehicleChanged: _updateVehicleModel()
RowLayout {
Layout.fillWidth: true
QGCColoredImage {
width: ScreenTools.defaultFontPixelWidth * 4
height: ScreenTools.defaultFontPixelHeight * 1.33
fillMode: Image.PreserveAspectFit
mipmap: true
color: qgcPal.text
source: "/InstrumentValueIcons/airplane.svg"
}
QGCLabel {
text: _activeVehicle ? qsTr("Vehicle") + " " + _activeVehicle.id : qsTr("N/A")
font.pointSize: ScreenTools.mediumFontPointSize
Layout.alignment: Qt.AlignCenter
MouseArea {
anchors.fill: parent
onClicked: mainWindow.showIndicatorDrawer(vehicleSelectorDrawer, control)
}
}
}
Component {
id: vehicleSelectorDrawer
ToolIndicatorPage {
showExpand: true
contentComponent: Component {
ColumnLayout {
spacing: ScreenTools.defaultFontPixelWidth / 2
Repeater {
model: _vehicleModel
QGCButton {
text: modelData
Layout.fillWidth: true
onClicked: {
var vehicleId = modelData.split(" ")[1]
var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId)
QGroundControl.multiVehicleManager.activeVehicle = vehicle
mainWindow.closeIndicatorDrawer()
}
}
}
}
}
expandedComponent: Component {
SettingsGroupLayout {
Layout.fillWidth: true
FactCheckBoxSlider {
Layout.fillWidth: true
text: qsTr("Enable Multi-Vehicle Panel")
fact: _enableMultiVehiclePanel
visible: _enableMultiVehiclePanel.visible
property Fact _enableMultiVehiclePanel: QGroundControl.settingsManager.appSettings.enableMultiVehiclePanel
}
}
}
}
}
function _updateVehicleModel() {
var newCurrentIndex = -1
var newModel = [ ]
if (_multipleVehicles) {
for (var i = 0; i < QGroundControl.multiVehicleManager.vehicles.count; i++) {
var vehicle = QGroundControl.multiVehicleManager.vehicles.get(i)
newModel.push(qsTr("Vehicle") + " " + vehicle.id)
if (vehicle.id === _activeVehicle.id) {
newCurrentIndex = i
}
}
}
currentIndex = -1
_vehicleModel = newModel
currentIndex = newCurrentIndex
}
onActivated: (index) => {
var vehicleId = textAt(index).split(" ")[1]
var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId)
QGroundControl.multiVehicleManager.activeVehicle = vehicle
}
}
+39
View File
@@ -42,6 +42,7 @@ MultiVehicleManager::MultiVehicleManager(QObject *parent)
, _gcsHeartbeatTimer(new QTimer(this))
, _offlineEditingVehicle(new Vehicle(Vehicle::MAV_AUTOPILOT_TRACK, Vehicle::MAV_TYPE_TRACK, this))
, _vehicles(new QmlObjectListModel(this))
, _selectedVehicles(new QmlObjectListModel(this))
{
// qCDebug(MultiVehicleManagerLog) << Q_FUNC_INFO << this;
}
@@ -206,6 +207,8 @@ void MultiVehicleManager::_deleteVehiclePhase1(Vehicle *vehicle)
qCWarning(MultiVehicleManagerLog) << "Vehicle not found in map!";
}
deselectVehicle(vehicle->id());
_setActiveVehicleAvailable(false);
_setParameterReadyVehicleAvailable(false);
emit vehicleRemoved(vehicle);
@@ -333,6 +336,42 @@ void MultiVehicleManager::_sendGCSHeartbeat()
}
}
void MultiVehicleManager::selectVehicle(int vehicleId)
{
if(!_vehicleSelected(vehicleId)) {
Vehicle *const vehicle = getVehicleById(vehicleId);
_selectedVehicles->append(vehicle);
return;
}
}
void MultiVehicleManager::deselectVehicle(int vehicleId)
{
for (int i = 0; i < _selectedVehicles->count(); i++) {
Vehicle *const vehicle = qobject_cast<Vehicle*>(_selectedVehicles->get(i));
if (vehicle->id() == vehicleId) {
_selectedVehicles->removeAt(i);
return;
}
}
}
void MultiVehicleManager::deselectAllVehicles()
{
_selectedVehicles->clear();
}
bool MultiVehicleManager::_vehicleSelected(int vehicleId)
{
for (int i = 0; i < _selectedVehicles->count(); i++) {
Vehicle *const vehicle = qobject_cast<Vehicle*>(_selectedVehicles->get(i));
if (vehicle->id() == vehicleId) {
return true;
}
}
return false;
}
Vehicle *MultiVehicleManager::getVehicleById(int vehicleId) const
{
for (int i = 0; i < _vehicles->count(); i++) {
+7
View File
@@ -33,6 +33,7 @@ class MultiVehicleManager : public QObject
Q_PROPERTY(bool parameterReadyVehicleAvailable READ _getParameterReadyVehicleAvailable NOTIFY parameterReadyVehicleAvailableChanged)
Q_PROPERTY(Vehicle *activeVehicle READ activeVehicle WRITE setActiveVehicle NOTIFY activeVehicleChanged)
Q_PROPERTY(QmlObjectListModel *vehicles READ vehicles CONSTANT)
Q_PROPERTY(QmlObjectListModel *selectedVehicles READ selectedVehicles CONSTANT)
Q_PROPERTY(bool gcsHeartBeatEnabled READ _getGcsHeartbeatEnabled WRITE _setGcsHeartbeatEnabled NOTIFY gcsHeartBeatEnabledChanged)
Q_PROPERTY(Vehicle *offlineEditingVehicle READ offlineEditingVehicle CONSTANT)
@@ -45,7 +46,11 @@ public:
void init();
Q_INVOKABLE Vehicle *getVehicleById(int vehicleId) const;
Q_INVOKABLE void selectVehicle(int vehicleId);
Q_INVOKABLE void deselectVehicle(int vehicleId);
Q_INVOKABLE void deselectAllVehicles();
QmlObjectListModel *vehicles() const { return _vehicles; }
QmlObjectListModel *selectedVehicles() const { return _selectedVehicles; }
Vehicle *offlineEditingVehicle() const { return _offlineEditingVehicle; }
Vehicle *activeVehicle() const { return _activeVehicle; }
void setActiveVehicle(Vehicle *vehicle);
@@ -69,6 +74,7 @@ private slots:
private:
bool _vehicleExists(int vehicleId);
bool _vehicleSelected(int vehicleId);
void _setActiveVehicle(Vehicle *vehicle);
bool _getGcsHeartbeatEnabled() const { return _gcsHeartbeatEnabled; }
void _setGcsHeartbeatEnabled(bool gcsHeartBeatEnabled);
@@ -80,6 +86,7 @@ private:
QTimer *_gcsHeartbeatTimer = nullptr; ///< Timer to emit heartbeats
Vehicle *_offlineEditingVehicle = nullptr; ///< Disconnected vechicle used for offline editing
QmlObjectListModel *_vehicles = nullptr;
QmlObjectListModel *_selectedVehicles = nullptr;
bool _activeVehicleAvailable = false; ///< true: An active vehicle is available
bool _gcsHeartbeatEnabled = false; ///< Enabled/disable heartbeat emission
bool _parameterReadyVehicleAvailable = false; ///< true: An active vehicle with ready parameters is available