From fb26f834a21234551e9c129a7516ce3fcdd9aedc Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 29 Jun 2015 11:59:29 -0700 Subject: [PATCH] Parameter validation support --- qgroundcontrol.qrc | 1 + src/FactSystem/Fact.cc | 157 +++++++---- src/FactSystem/Fact.h | 8 + src/FactSystem/FactMetaData.cc | 61 ++++- src/FactSystem/FactMetaData.h | 15 ++ src/QGCApplication.cc | 5 + src/QmlControls/ParameterEditor.qml | 247 ++++++------------ src/QmlControls/ParameterEditorDialog.qml | 164 ++++++++++++ .../QGroundControl.Controls.qmldir | 4 +- 9 files changed, 439 insertions(+), 223 deletions(-) create mode 100644 src/QmlControls/ParameterEditorDialog.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 20fd3daf3..9a5f6dedd 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -106,6 +106,7 @@ src/QmlControls/QGCViewMessage.qml src/QmlControls/ParameterEditor.qml + src/QmlControls/ParameterEditorDialog.qml src/ViewWidgets/ParameterEditorWidget.qml src/ViewWidgets/CustomCommandWidget.qml diff --git a/src/FactSystem/Fact.cc b/src/FactSystem/Fact.cc index dc70c8313..54e33dc90 100644 --- a/src/FactSystem/Fact.cc +++ b/src/FactSystem/Fact.cc @@ -51,34 +51,19 @@ Fact::Fact(int componentId, QString name, FactMetaData::ValueType_t type, QObjec void Fact::setValue(const QVariant& value) { - QVariant newValue; - - switch (type()) { - case FactMetaData::valueTypeInt8: - case FactMetaData::valueTypeInt16: - case FactMetaData::valueTypeInt32: - newValue = QVariant(value.toInt()); - break; - - case FactMetaData::valueTypeUint8: - case FactMetaData::valueTypeUint16: - case FactMetaData::valueTypeUint32: - newValue = QVariant(value.toUInt()); - break; - - case FactMetaData::valueTypeFloat: - newValue = QVariant(value.toFloat()); - break; - - case FactMetaData::valueTypeDouble: - newValue = QVariant(value.toDouble()); - break; - } - - if (newValue != _value) { - _value.setValue(newValue); - emit valueChanged(_value); - emit _containerValueChanged(_value); + if (_metaData) { + QVariant typedValue; + QString errorString; + + if (_metaData->convertAndValidate(value, true /* convertOnly */, typedValue, errorString)) { + if (typedValue != _value) { + _value.setValue(typedValue); + emit valueChanged(_value); + emit _containerValueChanged(_value); + } + } + } else { + qWarning() << "Meta data pointer missing"; } } @@ -110,11 +95,15 @@ QString Fact::valueString(void) const QVariant Fact::defaultValue(void) { - Q_ASSERT(_metaData); - if (!_metaData->defaultValueAvailable()) { - qDebug() << "Access to unavailable default value"; + if (_metaData) { + if (!_metaData->defaultValueAvailable()) { + qDebug() << "Access to unavailable default value"; + } + return _metaData->defaultValue(); + } else { + qWarning() << "Meta data pointer missing"; + return QVariant(0); } - return _metaData->defaultValue(); } FactMetaData::ValueType_t Fact::type(void) @@ -124,38 +113,82 @@ FactMetaData::ValueType_t Fact::type(void) QString Fact::shortDescription(void) { - Q_ASSERT(_metaData); - return _metaData->shortDescription(); + if (_metaData) { + return _metaData->shortDescription(); + } else { + qWarning() << "Meta data pointer missing"; + return QString(); + } } QString Fact::longDescription(void) { - Q_ASSERT(_metaData); - return _metaData->longDescription(); + if (_metaData) { + return _metaData->longDescription(); + } else { + qWarning() << "Meta data pointer missing"; + return QString(); + } } QString Fact::units(void) { - Q_ASSERT(_metaData); - return _metaData->units(); + if (_metaData) { + return _metaData->units(); + } else { + qWarning() << "Meta data pointer missing"; + return QString(); + } } QVariant Fact::min(void) { - Q_ASSERT(_metaData); - return _metaData->min(); + if (_metaData) { + return _metaData->min(); + } else { + qWarning() << "Meta data pointer missing"; + return QVariant(0); + } } QVariant Fact::max(void) { - Q_ASSERT(_metaData); - return _metaData->max(); + if (_metaData) { + return _metaData->max(); + } else { + qWarning() << "Meta data pointer missing"; + return QVariant(0); + } +} + +bool Fact::minIsDefaultForType(void) +{ + if (_metaData) { + return _metaData->minIsDefaultForType(); + } else { + qWarning() << "Meta data pointer missing"; + return false; + } +} + +bool Fact::maxIsDefaultForType(void) +{ + if (_metaData) { + return _metaData->maxIsDefaultForType(); + } else { + qWarning() << "Meta data pointer missing"; + return false; + } } QString Fact::group(void) { - Q_ASSERT(_metaData); - return _metaData->group(); + if (_metaData) { + return _metaData->group(); + } else { + qWarning() << "Meta data pointer missing"; + return QString(); + } } void Fact::setMetaData(FactMetaData* metaData) @@ -165,16 +198,40 @@ void Fact::setMetaData(FactMetaData* metaData) bool Fact::valueEqualsDefault(void) { - Q_ASSERT(_metaData); - if (_metaData->defaultValueAvailable()) { - return _metaData->defaultValue() == value(); + if (_metaData) { + if (_metaData->defaultValueAvailable()) { + return _metaData->defaultValue() == value(); + } else { + return false; + } } else { + qWarning() << "Meta data pointer missing"; return false; } } bool Fact::defaultValueAvailable(void) { - Q_ASSERT(_metaData); - return _metaData->defaultValueAvailable(); -} \ No newline at end of file + if (_metaData) { + return _metaData->defaultValueAvailable(); + } else { + qWarning() << "Meta data pointer missing"; + return false; + } +} + +QString Fact::validate(const QString& value, bool convertOnly) +{ + if (_metaData) { + + QVariant typedValue; + QString errorString; + + _metaData->convertAndValidate(value, convertOnly, typedValue, errorString); + + return errorString; + } else { + qWarning() << "Meta data pointer missing"; + return QString("Internal error: Meta data pointer missing"); + } +} diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index a8b486f74..bcb1d2989 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -55,9 +55,15 @@ public: Q_PROPERTY(QString shortDescription READ shortDescription CONSTANT) Q_PROPERTY(QString longDescription READ longDescription CONSTANT) Q_PROPERTY(QVariant min READ min CONSTANT) + Q_PROPERTY(bool minIsDefaultForType READ minIsDefaultForType CONSTANT) Q_PROPERTY(QVariant max READ max CONSTANT) + Q_PROPERTY(bool maxIsDefaultForType READ maxIsDefaultForType CONSTANT) Q_PROPERTY(QString group READ group CONSTANT) + /// Convert and validate value + /// @param convertOnly true: validate type conversion only, false: validate against meta data as well + Q_INVOKABLE QString validate(const QString& value, bool convertOnly); + // Property system methods QString name(void) const; @@ -73,7 +79,9 @@ public: QString longDescription(void); QString units(void); QVariant min(void); + bool minIsDefaultForType(void); QVariant max(void); + bool maxIsDefaultForType(void); QString group(void); /// Sets the meta data associated with the Fact. diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index 308445a52..33377ac08 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -39,7 +39,9 @@ FactMetaData::FactMetaData(ValueType_t type, QObject* parent) : _defaultValue(0), _defaultValueAvailable(false), _min(_minForType()), - _max(_maxForType()) + _max(_maxForType()), + _minIsDefaultForType(true), + _maxIsDefaultForType(true) { } @@ -68,6 +70,7 @@ void FactMetaData::setMin(const QVariant& min) { if (min > _minForType()) { _min = min; + _minIsDefaultForType = false; } else { qWarning() << "Attempt to set min below allowable value"; _min = _minForType(); @@ -81,6 +84,7 @@ void FactMetaData::setMax(const QVariant& max) _max = _maxForType(); } else { _max = max; + _maxIsDefaultForType = false; } } @@ -133,3 +137,58 @@ QVariant FactMetaData::_maxForType(void) // Make windows compiler happy, even switch is full cased return QVariant(); } + +bool FactMetaData::convertAndValidate(const QVariant& value, bool convertOnly, QVariant& typedValue, QString& errorString) +{ + bool convertOk; + + errorString.clear(); + + switch (type()) { + case FactMetaData::valueTypeInt8: + case FactMetaData::valueTypeInt16: + case FactMetaData::valueTypeInt32: + typedValue = QVariant(value.toInt(&convertOk)); + if (!convertOnly && convertOk) { + if (min() > typedValue || typedValue > max()) { + errorString = QString("Value must be within %1 and %2").arg(min().toInt()).arg(max().toInt()); + } + } + break; + + case FactMetaData::valueTypeUint8: + case FactMetaData::valueTypeUint16: + case FactMetaData::valueTypeUint32: + typedValue = QVariant(value.toUInt(&convertOk)); + if (!convertOnly && convertOk) { + if (min() > typedValue || typedValue > max()) { + errorString = QString("Value must be within %1 and %2").arg(min().toUInt()).arg(max().toUInt()); + } + } + break; + + case FactMetaData::valueTypeFloat: + typedValue = QVariant(value.toFloat(&convertOk)); + if (!convertOnly && convertOk) { + if (min() > typedValue || typedValue > max()) { + errorString = QString("Value must be within %1 and %2").arg(min().toFloat()).arg(max().toFloat()); + } + } + break; + + case FactMetaData::valueTypeDouble: + typedValue = QVariant(value.toDouble(&convertOk)); + if (!convertOnly && convertOk) { + if (min() > typedValue || typedValue > max()) { + errorString = QString("Value must be within %1 and %2").arg(min().toDouble()).arg(max().toDouble()); + } + } + break; + } + + if (!convertOk) { + errorString = "Invalid number"; + } + + return convertOk && errorString.isEmpty(); +} diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index 2d218b06e..2afcdf198 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -55,6 +55,7 @@ public: FactMetaData(ValueType_t type, QObject* parent = NULL); // Property accessors + QString name(void) { return _name; } QString group(void) { return _group; } ValueType_t type(void) { return _type; } QVariant defaultValue(void); @@ -64,8 +65,11 @@ public: QString units(void) { return _units; } QVariant min(void) { return _min; } QVariant max(void) { return _max; } + bool minIsDefaultForType(void) { return _minIsDefaultForType; } + bool maxIsDefaultForType(void) { return _maxIsDefaultForType; } // Property setters + void setName(const QString& name) { _name = name; } void setGroup(const QString& group) { _group = group; } void setDefaultValue(const QVariant& defaultValue); void setShortDescription(const QString& shortDescription) { _shortDescription = shortDescription; } @@ -73,11 +77,20 @@ public: void setUnits(const QString& units) { _units = units; } void setMin(const QVariant& max); void setMax(const QVariant& max); + + /// Converts the specified value, validating against meta data + /// @param value Value to convert, can be string + /// @param convertOnly true: convert to correct type only, do not validate against meta data + /// @param typeValue Converted value, correctly typed + /// @param errorString Error string if convert fails + /// @returns false: Convert failed, errorString set + bool convertAndValidate(const QVariant& value, bool convertOnly, QVariant& typedValue, QString& errorString); private: QVariant _minForType(void); QVariant _maxForType(void); + QString _name; QString _group; ValueType_t _type; QVariant _defaultValue; @@ -87,6 +100,8 @@ private: QString _units; QVariant _min; QVariant _max; + bool _minIsDefaultForType; + bool _maxIsDefaultForType; }; #endif \ No newline at end of file diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index b31af8540..180cb971b 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -167,6 +167,11 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) if (fullLogging) { QLoggingCategory::setFilterRules(QStringLiteral("*Log=true")); } else { + if (_runningUnitTests) { + // We need to turn off these warnings until the firmware meta data is cleaned up + QLoggingCategory::setFilterRules(QStringLiteral("PX4ParameterLoaderLog.warning=false")); + } + // First thing we want to do is set up the qtlogging.ini file. If it doesn't already exist we copy // it to the correct location. This way default debug builds will have logging turned off. diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index 8d9db803d..6c9522f38 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -51,88 +51,94 @@ QGCView { ParameterEditorController { id: controller; factPanel: panel } - QGCViewPanel { - id: panel - anchors.fill: parent + Component { + id: editorDialogComponent - Component { - id: factRowsComponent + ParameterEditorDialog { fact: __editorDialogFact } + } // Component - Editor Dialog - Column { - id: factColumn - x: __leftMargin + Component { + id: factRowsComponent - QGCLabel { - height: defaultTextHeight + (ScreenTools.defaultFontPizelSize * 0.5) - text: group - verticalAlignment: Text.AlignVCenter - font.pixelSize: ScreenTools.mediumFontPixelSize - } + Column { + id: factColumn + x: __leftMargin - Rectangle { - width: parent.width - height: 1 - color: __qgcPal.text - } + QGCLabel { + text: group + verticalAlignment: Text.AlignVCenter + font.pixelSize: ScreenTools.mediumFontPixelSize + } - Repeater { - model: controller.getFactsForGroup(componentId, group) + Rectangle { + width: parent.width + height: 1 + color: __qgcPal.text + } - Column { - property Fact modelFact: controller.getParameterFact(componentId, modelData) - - Item { - x: __leftMargin - width: parent.width - height: defaultTextHeight + (ScreenTools.defaultFontPixelSize * 0.5) - - QGCLabel { - id: nameLabel - width: defaultTextWidth * (__maxParamChars + 1) - height: parent.height - verticalAlignment: Text.AlignVCenter - text: modelFact.name - } + Repeater { + model: controller.getFactsForGroup(componentId, group) - QGCLabel { - id: valueLabel - width: defaultTextWidth * 20 - height: parent.height - anchors.left: nameLabel.right - verticalAlignment: Text.AlignVCenter - color: modelFact.valueEqualsDefault ? __qgcPal.text : "orange" - text: modelFact.valueString + " " + modelFact.units - } + Column { + property Fact modelFact: controller.getParameterFact(componentId, modelData) - QGCLabel { - height: parent.height - anchors.left: valueLabel.right - verticalAlignment: Text.AlignVCenter - visible: fullMode - text: modelFact.shortDescription - } + Item { + x: __leftMargin + width: parent.width + height: ScreenTools.defaultFontPixelSize * 1.75 - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton + QGCLabel { + id: nameLabel + width: defaultTextWidth * (__maxParamChars + 1) + height: parent.height + verticalAlignment: Text.AlignVCenter + text: modelFact.name + } - onClicked: { - __editorDialogFact = modelFact - showDialog(editorDialogComponent, "Parameter Editor", fullMode ? 50 : -1, StandardButton.Cancel | StandardButton.Save) - } - } + QGCLabel { + id: valueLabel + width: defaultTextWidth * 20 + height: parent.height + anchors.left: nameLabel.right + verticalAlignment: Text.AlignVCenter + color: modelFact.valueEqualsDefault ? __qgcPal.text : "orange" + text: modelFact.valueString + " " + modelFact.units + } + + QGCLabel { + height: parent.height + anchors.left: valueLabel.right + verticalAlignment: Text.AlignVCenter + visible: fullMode + text: modelFact.shortDescription } - Rectangle { - x: __leftMargin - width: factColumn.width - __leftMargin - __rightMargin - height: 1 - color: __qgcPal.windowShade + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + onClicked: { + __editorDialogFact = modelFact + showDialog(editorDialogComponent, "Parameter Editor", fullMode ? 50 : -1, StandardButton.Cancel | StandardButton.Save) + } } - } // Column - Fact - } // Repeater - Facts - } // Column - Facts - } // Component - factRowsComponent + } + + Rectangle { + x: __leftMargin + width: factColumn.width - __leftMargin - __rightMargin + height: 1 + color: __qgcPal.windowShade + } + } // Column - Fact + } // Repeater - Facts + } // Column - Facts + } // Component - factRowsComponent + + QGCViewPanel { + id: panel + anchors.fill: parent + Column { anchors.fill: parent @@ -255,106 +261,5 @@ QGCView { } // ScrollView - Facts } // Item - Group ScrollView + Facts } // Column - Outer - } - - Component { - id: editorDialogComponent - - QGCViewDialog { - id: editorDialog - - ParameterEditorController { id: controller; factPanel: editorDialog } - - property bool fullMode: true - - function accept() { - __editorDialogFact.value = valueField.text - editorDialog.hideDialog() - } - - Column { - spacing: defaultTextHeight - anchors.left: parent.left - anchors.right: parent.right - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: __editorDialogFact.shortDescription ? __editorDialogFact.shortDescription : "Description missing" - } - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - visible: __editorDialogFact.longDescription - text: __editorDialogFact.longDescription - } - - QGCTextField { - id: valueField - text: __editorDialogFact.valueString - } - - QGCLabel { text: __editorDialogFact.name } - - Row { - spacing: defaultTextWidth - - QGCLabel { text: "Units:" } - QGCLabel { text: __editorDialogFact.units ? __editorDialogFact.units : "none" } - } - - Row { - spacing: defaultTextWidth - - QGCLabel { text: "Minimum value:" } - QGCLabel { text: __editorDialogFact.min } - } - - Row { - spacing: defaultTextWidth - - QGCLabel { text: "Maxmimum value:" } - QGCLabel { text: __editorDialogFact.max } - } - - Row { - spacing: defaultTextWidth - - QGCLabel { text: "Default value:" } - QGCLabel { text: __editorDialogFact.defaultValueAvailable ? __editorDialogFact.defaultValue : "none" } - } - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: "Warning: Modifying parameters while vehicle is in flight can lead to vehicle instability and possible vehicle loss. " + - "Make sure you know what you are doing and double-check your values before Save!" - } - } // Column - Fact information - - - QGCButton { - anchors.rightMargin: defaultTextWidth - anchors.right: rcButton.left - anchors.bottom: parent.bottom - visible: __editorDialogFact.defaultValueAvailable - text: "Reset to default" - - onClicked: { - __editorDialogFact.value = __editorDialogFact.defaultValue - editorDialog.hideDialog() - } - } - - QGCButton { - id: rcButton - anchors.right: parent.right - anchors.bottom: parent.bottom - visible: __editorDialogFact.defaultValueAvailable - text: "Set RC to Param..." - onClicked: controller.setRCToParam(__editorDialogFact.name) - } - } // Rectangle - editorDialog - } // Component - Editor Dialog + } // QGCViewPanel } // QGCView diff --git a/src/QmlControls/ParameterEditorDialog.qml b/src/QmlControls/ParameterEditorDialog.qml new file mode 100644 index 000000000..54b1ac87e --- /dev/null +++ b/src/QmlControls/ParameterEditorDialog.qml @@ -0,0 +1,164 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Don Gagne + +import QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Styles 1.2 +import QtQuick.Dialogs 1.2 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 + +QGCViewDialog { + property Fact fact + property bool validate: false + property string validateValue + + ParameterEditorController { id: controller; factPanel: parent } + + function accept() { + var errorString = fact.validate(valueField.text, forceSave.checked) + if (errorString == "") { + fact.value = valueField.text + fact.valueChanged(fact.value) + hideDialog() + } else { + validationError.text = errorString + forceSave.visible = true + } + } + + Component.onCompleted: { + if (validate) { + validationError.text = fact.validate(validateValue, false /* convertOnly */) + forceSave.visible = true + } + } + + Column { + spacing: defaultTextHeight + anchors.left: parent.left + anchors.right: parent.right + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: fact.shortDescription ? fact.shortDescription : "Description missing" + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + visible: fact.longDescription + text: fact.longDescription + } + + QGCTextField { + id: valueField + text: validate ? validateValue : fact.valueString + } + + QGCLabel { text: fact.name } + + Row { + spacing: defaultTextWidth + + QGCLabel { text: "Units:" } + QGCLabel { text: fact.units ? fact.units : "none" } + } + + Row { + spacing: defaultTextWidth + visible: !fact.minIsDefaultForType + + QGCLabel { text: "Minimum value:" } + QGCLabel { text: fact.min } + } + + Row { + spacing: defaultTextWidth + visible: !fact.maxIsDefaultForType + + QGCLabel { text: "Maximum value:" } + QGCLabel { text: fact.max } + } + + Row { + spacing: defaultTextWidth + + QGCLabel { text: "Default value:" } + QGCLabel { text: fact.defaultValueAvailable ? fact.defaultValue : "none" } + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: "Warning: Modifying parameters while vehicle is in flight can lead to vehicle instability and possible vehicle loss. " + + "Make sure you know what you are doing and double-check your values before Save!" + } + + QGCLabel { + id: validationError + width: parent.width + wrapMode: Text.WordWrap + color: "yellow" + } + + QGCCheckBox { + id: forceSave + visible: false + text: "Force save (dangerous!)" + } + } // Column - Fact information + + + QGCButton { + id: bottomButton + anchors.rightMargin: defaultTextWidth + anchors.right: rcButton.left + anchors.bottom: parent.bottom + visible: fact.defaultValueAvailable + text: "Reset to default" + + onClicked: { + fact.value = fact.defaultValue + fact.valueChanged(fact.value) + hideDialog() + } + } + + QGCButton { + id: rcButton + anchors.right: parent.right + anchors.bottom: parent.bottom + text: "Set RC to Param..." + visible: !validate + onClicked: controller.setRCToParam(fact.name) + } +} // QGCViewDialog diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index c158fd07b..6f58994b6 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -14,9 +14,11 @@ SubMenuButton 1.0 SubMenuButton.qml IndicatorButton 1.0 IndicatorButton.qml VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml -ParameterEditor 1.0 ParameterEditor.qml ViewWidget 1.0 ViewWidget.qml +ParameterEditor 1.0 ParameterEditor.qml +ParameterEditorDialog 1.0 ParameterEditorDialog.qml + QGCView 1.0 QGCView.qml QGCViewPanel 1.0 QGCViewPanel.qml QGCViewDialog 1.0 QGCViewDialog.qml -- 2.22.0