From 6c4f6de71fde7f91e8e508463816ea6a21b07d0d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 31 Dec 2019 13:04:28 -0500 Subject: [PATCH] Make the chart a control --- qgroundcontrol.qrc | 1 + src/AnalyzeView/MAVLinkInspectorController.cc | 69 +++++--- src/AnalyzeView/MAVLinkInspectorController.h | 10 +- src/AnalyzeView/MAVLinkInspectorPage.qml | 155 +++--------------- src/QmlControls/MAVLinkChart.qml | 124 ++++++++++++++ .../QGroundControl/Controls/qmldir | 1 + 6 files changed, 196 insertions(+), 164 deletions(-) create mode 100644 src/QmlControls/MAVLinkChart.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 542cba60e..11a295946 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -89,6 +89,7 @@ src/QmlControls/KMLOrSHPFileDialog.qml src/QmlControls/LogReplayStatusBar.qml src/QmlControls/MainWindowSavedState.qml + src/QmlControls/MAVLinkChart.qml src/QmlControls/MAVLinkMessageButton.qml src/QmlControls/MissionCommandDialog.qml src/PlanView/MissionItemEditor.qml diff --git a/src/AnalyzeView/MAVLinkInspectorController.cc b/src/AnalyzeView/MAVLinkInspectorController.cc index 3984ca703..6d03b0b8c 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.cc +++ b/src/AnalyzeView/MAVLinkInspectorController.cc @@ -18,6 +18,8 @@ QT_CHARTS_USE_NAMESPACE Q_DECLARE_METATYPE(QAbstractSeries*) +#define UPDATE_FREQUENCY (1000 / 15) // 15Hz + //----------------------------------------------------------------------------- QGCMAVLinkMessageField::QGCMAVLinkMessageField(QGCMAVLinkMessage *parent, QString name, QString type) : QObject(parent) @@ -118,19 +120,25 @@ QGCMAVLinkMessageField::updateValue(QString newValue, qreal v) _msg->msgCtl()->updateYRange(_left); } } - _msg->msgCtl()->updateXRange(); - _updateSeries(); } } //----------------------------------------------------------------------------- void -QGCMAVLinkMessageField::_updateSeries() +QGCMAVLinkMessageField::updateSeries() { int count = _values.count(); if (count > 1) { + QList s; + s.reserve(count); + int idx = _dataIndex; + for(int i = 0; i < count; i++, idx++) { + if(idx >= count) idx = 0; + QPointF p(_values[idx]); + s.append(p); + } QLineSeries* lineSeries = static_cast(_pSeries); - lineSeries->replace(_values); + lineSeries->replace(s); } } @@ -512,8 +520,10 @@ MAVLinkInspectorController::MAVLinkInspectorController() connect(multiVehicleManager, &MultiVehicleManager::vehicleRemoved, this, &MAVLinkInspectorController::_vehicleRemoved); MAVLinkProtocol* mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); connect(mavlinkProtocol, &MAVLinkProtocol::messageReceived, this, &MAVLinkInspectorController::_receiveMessage); - connect(&_updateTimer, &QTimer::timeout, this, &MAVLinkInspectorController::_refreshFrequency); - _updateTimer.start(1000); + connect(&_updateFrequencyTimer, &QTimer::timeout, this, &MAVLinkInspectorController::_refreshFrequency); + connect(&_updateSeriesTimer, &QTimer::timeout, this, &MAVLinkInspectorController::_refreshSeries); + _updateFrequencyTimer.start(1000); + _updateSeriesTimer.start(UPDATE_FREQUENCY); MultiVehicleManager *manager = qgcApp()->toolbox()->multiVehicleManager(); connect(manager, &MultiVehicleManager::activeVehicleChanged, this, &MAVLinkInspectorController::_setActiveVehicle); _timeScaleSt.append(new TimeScale_st(this, tr("5 Sec"), 5 * 1000)); @@ -570,13 +580,13 @@ MAVLinkInspectorController::setLeftRangeIdx(quint32 r) { if(r < static_cast(_rangeSt.count())) { _leftRangeIndex = r; - _timeRange = _rangeSt[static_cast(r)]->range; + qreal range = _rangeSt[static_cast(r)]->range; emit leftRangeChanged(); //-- If not Auto, use defined range if(_leftRangeIndex > 0) { - _leftRangeMin = -_timeRange; + _leftRangeMin = -range; emit leftRangeMinChanged(); - _leftRangeMax = _timeRange; + _leftRangeMax = range; emit leftRangeMaxChanged(); } } @@ -588,13 +598,13 @@ MAVLinkInspectorController::setRightRangeIdx(quint32 r) { if(r < static_cast(_rangeSt.count())) { _rightRangeIndex = r; - _timeRange = _rangeSt[static_cast(r)]->range; + qreal range = _rangeSt[static_cast(r)]->range; emit rightRangeChanged(); //-- If not Auto, use defined range if(_rightRangeIndex > 0) { - _rightRangeMin = -_timeRange; + _rightRangeMin = -range; emit rightRangeMinChanged(); - _rightRangeMax = _timeRange; + _rightRangeMax = range; emit rightRangeMaxChanged(); } } @@ -826,6 +836,7 @@ MAVLinkInspectorController::addSeries(QGCMAVLinkMessageField* field, QAbstractSe { if(field) { field->addSeries(series, left); + _updateSeriesTimer.start(UPDATE_FREQUENCY); } } @@ -836,19 +847,29 @@ MAVLinkInspectorController::delSeries(QGCMAVLinkMessageField* field) if(field) { field->delSeries(); } - if(_leftChartFields.count() == 0) { - _leftRangeMin = 0; - _leftRangeMax = 1; - emit leftRangeMinChanged(); - emit leftRangeMaxChanged(); - } - if(_rightChartFields.count() == 0) { - _rightRangeMin = 0; - _rightRangeMax = 1; - emit rightRangeMinChanged(); - emit rightRangeMaxChanged(); - } if(_leftChartFields.count() == 0 && _rightChartFields.count() == 0) { updateXRange(); + _updateSeriesTimer.stop(); + } +} + +//----------------------------------------------------------------------------- +void +MAVLinkInspectorController::_refreshSeries() +{ + updateXRange(); + for(int i = 0; i < _leftChartFields.count(); i++) { + QObject* object = qvariant_cast(_leftChartFields.at(i)); + QGCMAVLinkMessageField* pField = qobject_cast(object); + if(pField) { + pField->updateSeries(); + } + } + for(int i = 0; i < _rightChartFields.count(); i++) { + QObject* object = qvariant_cast(_rightChartFields.at(i)); + QGCMAVLinkMessageField* pField = qobject_cast(object); + if(pField) { + pField->updateSeries(); + } } } diff --git a/src/AnalyzeView/MAVLinkInspectorController.h b/src/AnalyzeView/MAVLinkInspectorController.h index 3052629db..53ba832cd 100644 --- a/src/AnalyzeView/MAVLinkInspectorController.h +++ b/src/AnalyzeView/MAVLinkInspectorController.h @@ -56,6 +56,7 @@ public: void addSeries (QAbstractSeries* series, bool left); void delSeries (); + void updateSeries (); signals: void seriesChanged (); @@ -63,10 +64,6 @@ signals: void valueChanged (); private: - void _updateSeries (); - -private: - QString _type; QString _name; QString _value; @@ -242,6 +239,7 @@ private slots: void _vehicleRemoved (Vehicle* vehicle); void _setActiveVehicle (Vehicle* vehicle); void _refreshFrequency (); + void _refreshSeries (); private: QGCMAVLinkVehicle* _findVehicle (uint8_t id); @@ -275,9 +273,9 @@ private: qreal _rightRangeMin = 0; qreal _rightRangeMax = 1; quint32 _rightRangeIndex = 0; ///> Auto Range - qreal _timeRange = 0; QGCMAVLinkVehicle* _activeVehicle = nullptr; - QTimer _updateTimer; + QTimer _updateFrequencyTimer; + QTimer _updateSeriesTimer; QStringList _vehicleNames; QmlObjectListModel _vehicles; ///< List of QGCMAVLinkVehicle QVariantList _rightChartFields; diff --git a/src/AnalyzeView/MAVLinkInspectorPage.qml b/src/AnalyzeView/MAVLinkInspectorPage.qml index 71bc9f3c0..444e19274 100644 --- a/src/AnalyzeView/MAVLinkInspectorPage.qml +++ b/src/AnalyzeView/MAVLinkInspectorPage.qml @@ -286,6 +286,7 @@ AnalyzePage { Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 30 Layout.maximumWidth: ScreenTools.defaultFontPixelWidth * 30 text: object.value + elide: Text.ElideRight } } Repeater { @@ -301,14 +302,14 @@ AnalyzePage { delegate: QGCCheckBox { Layout.row: index Layout.column: 3 - enabled: (object.series !== null && object.left) || (object.selectable && controller.seriesCount < chartView.maxSeriesCount) + enabled: (object.series !== null && object.left) || (object.selectable && controller.seriesCount < 12) checked: enabled ? (object.series !== null && object.left) : false onClicked: { if(enabled) { if(checked) { - chartView.addDimension(object, true) + chart1.addDimension(object, true) } else { - chartView.delDimension(object) + chart1.delDimension(object) } } } @@ -319,14 +320,14 @@ AnalyzePage { delegate: QGCCheckBox { Layout.row: index Layout.column: 4 - enabled: (object.series !== null && !object.left) || (object.selectable && controller.seriesCount < chartView.maxSeriesCount && (object.series === null && !object.left)) + enabled: (object.series !== null && !object.left) || (object.selectable && controller.seriesCount < 12 && (object.series === null && !object.left)) checked: enabled ? (object.series !== null && !object.left) : false onClicked: { if(enabled) { if(checked) { - chartView.addDimension(object, false) + chart2.addDimension(object, false) } else { - chartView.delDimension(object) + chart2.delDimension(object) } } } @@ -334,135 +335,21 @@ AnalyzePage { } } Item { height: ScreenTools.defaultFontPixelHeight * 0.25; width: 1 } - ChartView { - id: chartView + MAVLinkChart { + id: chart1 + height: ScreenTools.defaultFontPixelHeight * 20 + visible: controller.leftChartFields.length > 0 + min: controller.leftRangeMin + max: controller.leftRangeMax + Layout.fillWidth: true + } + MAVLinkChart { + id: chart2 + height: ScreenTools.defaultFontPixelHeight * 20 + visible: controller.rightChartFields.length > 0 + min: controller.rightRangeMin + max: controller.rightRangeMax Layout.fillWidth: true - height: ScreenTools.defaultFontPixelHeight * 20 - theme: ChartView.ChartThemeDark - antialiasing: true - visible: controller.leftChartFields.length > 0 || controller.rightChartFields.length > 0 - animationOptions: ChartView.NoAnimation - legend.visible: false - margins.bottom: ScreenTools.defaultFontPixelHeight * 1.5 - margins.top: chartHeader.height + (ScreenTools.defaultFontPixelHeight * 2) - - property int maxSeriesCount: seriesColors.length - property var seriesColors: ["antiquewhite", "aqua", "chartreuse", "chocolate", "crimson", "darkturquoise", "aquamarine", "azure", "coral", "cornflowerblue", "darkorange", "gold", "hotpink", "lavenderblush", "lightskyblue"] - - function addDimension(field, left) { - console.log(field.name + ' ' + field + ' AxisY1: ' + axisY1 + ' AxisY2: ' + axisY2) - console.log(controller.seriesCount + ' ' + chartView.seriesColors[controller.seriesCount]) - var serie = createSeries(ChartView.SeriesTypeLine, field.label) - serie.axisX = axisX - if(left) { - serie.axisY = axisY1 - } else { - serie.axisYRight = axisY2 - } - serie.useOpenGL = true - serie.color = chartView.seriesColors[controller.seriesCount] - controller.addSeries(field, serie, left) - } - - function delDimension(field) { - chartView.removeSeries(field.series) - controller.delSeries(field) - console.log('Remove: ' + controller.seriesCount + ' ' + field.name) - } - - DateTimeAxis { - id: axisX - min: visible ? controller.rangeXMin : new Date() - max: visible ? controller.rangeXMax : new Date() - format: "hh:mm:ss" - tickCount: 5 - gridVisible: true - labelsFont.family: "Fixed" - labelsFont.pixelSize: ScreenTools.smallFontPointSize - } - - ValueAxis { - id: axisY1 - min: visible ? controller.leftRangeMin : 0 - max: visible ? controller.leftRangeMax : 0 - visible: controller.leftChartFields.length > 0 - lineVisible: false - labelsFont.family: "Fixed" - labelsFont.pixelSize: ScreenTools.smallFontPointSize - //labelsColor: qgcPal.colorRed - } - - ValueAxis { - id: axisY2 - min: visible ? controller.rightRangeMin : 0 - max: visible ? controller.rightRangeMax : 0 - visible: controller.rightChartFields.length > 0 - lineVisible: false - labelsFont.family: "Fixed" - labelsFont.pixelSize: ScreenTools.smallFontPointSize - //labelsColor: qgcPal.colorGreen - } - - RowLayout { - id: chartHeader - anchors.left: parent.left - anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 4 - anchors.right: parent.right - anchors.rightMargin:ScreenTools.defaultFontPixelWidth * 4 - anchors.top: parent.top - anchors.topMargin: ScreenTools.defaultFontPixelHeight * 1.5 - spacing: 0 - QGCLabel { - text: qsTr("Scale:"); - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - QGCComboBox { - id: timeScaleSelector - width: ScreenTools.defaultFontPixelWidth * 10 - height: ScreenTools.defaultFontPixelHeight - model: controller.timeScales - currentIndex: controller.timeScale - onActivated: controller.timeScale = index - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - GridLayout { - columns: 2 - columnSpacing: ScreenTools.defaultFontPixelWidth - rowSpacing: ScreenTools.defaultFontPixelHeight * 0.25 - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.fillWidth: true - QGCLabel { - text: qsTr("Range Left:"); - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - QGCComboBox { - Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 8 - height: ScreenTools.defaultFontPixelHeight * 1.5 - model: controller.rangeList - currentIndex: controller.leftRangeIdx - onActivated: controller.leftRangeIdx = index - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - QGCLabel { - text: qsTr("Range Right:"); - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - QGCComboBox { - Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 8 - height: ScreenTools.defaultFontPixelHeight * 1.5 - model: controller.rangeList - currentIndex: controller.rightRangeIdx - onActivated: controller.rightRangeIdx = index - font.pixelSize: ScreenTools.smallFontPointSize - Layout.alignment: Qt.AlignVCenter - } - } - } } } } diff --git a/src/QmlControls/MAVLinkChart.qml b/src/QmlControls/MAVLinkChart.qml new file mode 100644 index 000000000..8868371ed --- /dev/null +++ b/src/QmlControls/MAVLinkChart.qml @@ -0,0 +1,124 @@ +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 +import QtCharts 2.3 + +import QGroundControl 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.ScreenTools 1.0 + +ChartView { + id: chartView + theme: ChartView.ChartThemeDark + antialiasing: true + animationOptions: ChartView.NoAnimation + legend.visible: false + backgroundColor: qgcPal.window + backgroundRoundness: 0 + margins.bottom: ScreenTools.defaultFontPixelHeight * 1.5 + margins.top: chartHeader.height + (ScreenTools.defaultFontPixelHeight * 2) + + property int maxSeriesCount: seriesColors.length + property var seriesColors: ["chartreuse", "chocolate", "yellowgreen", "thistle", "silver", "darkturquoise", "blue", "green"] + property alias max: axisY.max + property alias min: axisY.min + + function addDimension(field, left) { + console.log(field.name + ' AxisY: ' + axisY) + console.log(chartView.count + ' ' + chartView.seriesColors[chartView.count]) + var serie = createSeries(ChartView.SeriesTypeLine, field.label) + serie.axisX = axisX + serie.axisY = axisY + serie.useOpenGL = true + serie.color = chartView.seriesColors[chartView.count] + serie.width = 1 + controller.addSeries(field, serie, left) + } + + function delDimension(field) { + chartView.removeSeries(field.series) + controller.delSeries(field) + console.log('Remove: ' + chartView.count + ' ' + field.name) + } + + DateTimeAxis { + id: axisX + min: controller.rangeXMin + max: controller.rangeXMax + format: "mm:ss.zzz" + tickCount: 5 + gridVisible: true + labelsFont.family: "Fixed" + labelsFont.pixelSize: ScreenTools.smallFontPointSize + } + + ValueAxis { + id: axisY + lineVisible: false + labelsFont.family: "Fixed" + labelsFont.pixelSize: ScreenTools.smallFontPointSize + } + + RowLayout { + id: chartHeader + anchors.left: parent.left + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 4 + anchors.right: parent.right + anchors.rightMargin: ScreenTools.defaultFontPixelWidth * 4 + anchors.top: parent.top + anchors.topMargin: ScreenTools.defaultFontPixelHeight * 1.5 + spacing: 0 + QGCLabel { + text: qsTr("Scale:"); + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + QGCComboBox { + id: timeScaleSelector + width: ScreenTools.defaultFontPixelWidth * 10 + height: ScreenTools.defaultFontPixelHeight + model: controller.timeScales + currentIndex: controller.timeScale + onActivated: controller.timeScale = index + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + GridLayout { + columns: 2 + columnSpacing: ScreenTools.defaultFontPixelWidth + rowSpacing: ScreenTools.defaultFontPixelHeight * 0.25 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.fillWidth: true + QGCLabel { + text: qsTr("Range Left:"); + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + QGCComboBox { + Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 8 + height: ScreenTools.defaultFontPixelHeight * 1.5 + model: controller.rangeList + currentIndex: controller.leftRangeIdx + onActivated: controller.leftRangeIdx = index + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + QGCLabel { + text: qsTr("Range Right:"); + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + QGCComboBox { + Layout.minimumWidth: ScreenTools.defaultFontPixelWidth * 8 + height: ScreenTools.defaultFontPixelHeight * 1.5 + model: controller.rangeList + currentIndex: controller.rightRangeIdx + onActivated: controller.rightRangeIdx = index + font.pixelSize: ScreenTools.smallFontPointSize + Layout.alignment: Qt.AlignVCenter + } + } + } +} diff --git a/src/QmlControls/QGroundControl/Controls/qmldir b/src/QmlControls/QGroundControl/Controls/qmldir index fae59ca2d..33bd69248 100644 --- a/src/QmlControls/QGroundControl/Controls/qmldir +++ b/src/QmlControls/QGroundControl/Controls/qmldir @@ -93,3 +93,4 @@ VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml ViewWidget 1.0 ViewWidget.qml QGCHoverButton 1.0 QGCHoverButton.qml +MAVLinkChart 1.0 MAVLinkChart.qml -- 2.22.0