Newer
Older
#include <QDockWidget>
#include "QGCXYPlot.h"
#include "ui_QGCXYPlot.h"
#include "MAVLinkProtocol.h"
#include "UASManager.h"
#include "IncrementalPlot.h"
#include <float.h>
#include <qwt_plot.h>
#include <qwt_plot_layout.h>
#include <qwt_scale_engine.h>
class XYPlotCurve : public QwtPlotItem
{
public:
XYPlotCurve() {
m_maxStorePoints = 10000;
m_maxShowPoints = 15;
setItemAttribute(QwtPlotItem::AutoScale);
minMaxSet = false;
m_color = Qt::white;
m_smoothPoints = 1;
void setMaxDataStorePoints(int max) { m_maxStorePoints = max; itemChanged(); }
void setMaxDataShowPoints(int max) { m_maxShowPoints = max; itemChanged(); }
void setSmoothPoints(int smoothPoints) { m_smoothPoints = smoothPoints; itemChanged(); }
int maxDataStorePoints() const { return m_maxStorePoints; }
int maxDataShowPoints() const { return m_maxShowPoints; }
int smoothPoints() const { return m_smoothPoints; }
void appendData(const QPointF &data) {
if(!minMaxSet) {
xmin = xmax = data.x();
ymin = ymax = data.y();
minMaxSet = true;
} else if(m_autoScale) {
xmin = qMin(xmin, data.x());
xmax = qMax(xmax, data.x());
ymin = qMin(ymin, data.y());
ymax = qMax(ymax, data.y());
}
m_data.append(data);
while(m_data.size() > m_maxStorePoints)
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
m_data.removeFirst();
itemChanged();
}
void clear() {
minMaxSet = false;
m_data.clear();
itemChanged();
}
void setColor(const QColor &color) {
m_color = color;
}
void unsetMinMax() {
if(m_autoScale)
return;
m_autoScale = true;
clear();
}
void setMinMax(double xmin, double xmax, double ymin, double ymax )
{
this->xmin = xmin;
this->xmax = xmax;
this->ymin = ymin;
this->ymax = ymax;
m_autoScale = false;
minMaxSet = true;
itemChanged();
}
double xMin() const { return xmin; }
double xMax() const { return xmax; }
double yMin() const { return ymin; }
double yMax() const { return ymax; }
virtual QwtDoubleRect boundingRect() const {
if(!minMaxSet)
return QwtDoubleRect(1,1,-2,-2);
return QwtDoubleRect(xmin,ymin,xmax-xmin,ymax-ymin);
}
protected:
/* From QwtPlotItem. Draw the complete series */
virtual void draw (QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRect &canvasRect) const
{
Q_UNUSED(canvasRect);
QPointF lastPoint;
if(m_data.isEmpty())
return;
QPointF smoothTotal(0,0);
int smoothCount = 0;
int start = qMax(0,m_data.size() - m_maxShowPoints);
int count = qMin(m_data.size()-start, m_maxShowPoints);
for(int i = qMax(0,start - m_smoothPoints); i < start; ++i) {
smoothTotal += m_data.at(i);
++smoothCount;
}
for(int i = 0; i < count; ++i) {
QPointF point = m_data.at(i+start);
if(m_smoothPoints > 1) {
smoothTotal += point;
if(smoothCount >= m_smoothPoints) {
Q_ASSERT(i + start - m_smoothPoints >= 0);
smoothTotal -= m_data.at(i + start - m_smoothPoints);
} else
++smoothCount;
point = smoothTotal/smoothCount;
}
QPointF paintCoord = QPointF(xMap.xTransform(point.x()), yMap.xTransform(point.y()));
m_color.setAlpha((m_maxShowPoints - count + i)*255/m_maxShowPoints);
if(i != 0)
if(i == count-1) {
//Draw marker for first point
const int marker_radius = 2;
QRectF marker = QRectF(paintCoord.x()-marker_radius, paintCoord.y()-marker_radius, marker_radius*2+1,marker_radius*2+1);
p->fillRect(marker,QBrush(m_color));
}
lastPoint = paintCoord;
}
}
private:
QList< QPointF > m_data;
int m_maxStorePoints;
int m_maxShowPoints;
int m_smoothPoints; /** Number of points to average across */
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
mutable QColor m_color;
double xmin;
double xmax;
double ymin;
double ymax;
bool minMaxSet;
bool m_autoScale;
};
QGCXYPlot::QGCXYPlot(QWidget *parent) :
QGCToolWidgetItem("XY Plot", parent),
ui(new Ui::QGCXYPlot),
plot(0),
xycurve(0),
x(0),
x_timestamp_us(0),
x_valid(false),
y(0),
y_timestamp_us(0),
y_valid(false),
max_timestamp_diff_us(10000) /* Default to 10ms tolerance between x and y values */
{
uas = 0;
ui->setupUi(this);
plot = new QwtPlot();
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
connect(ui->editFinishButton, SIGNAL(clicked()), this, SLOT(endEditMode()));
connect(MainWindow::instance(), SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
this, SLOT(appendData(int,QString,QString,QVariant,quint64)));
connect(ui->editXParam, SIGNAL(editTextChanged(QString)), this, SLOT(clearPlot()));
connect(ui->editYParam, SIGNAL(editTextChanged(QString)), this, SLOT(clearPlot()));
plot->plotLayout()->setAlignCanvasToScales(true);
QwtLinearScaleEngine* yScaleEngine = new QwtLinearScaleEngine();
plot->setAxisScaleEngine(QwtPlot::yLeft, yScaleEngine);
plot->setAxisAutoScale(QwtPlot::xBottom);
plot->setAxisAutoScale(QwtPlot::yLeft);
plot->setAutoReplot();
xycurve = new XYPlotCurve();
xycurve->attach(plot);
styleChanged(MainWindow::instance()->getStyle());
connect(MainWindow::instance(), SIGNAL(styleChanged(MainWindow::QGC_MAINWINDOW_STYLE)),
this, SLOT(styleChanged(MainWindow::QGC_MAINWINDOW_STYLE)));
connect(ui->minX, SIGNAL(valueChanged(double)),this, SLOT(updateMinMaxSettings()));
connect(ui->maxX, SIGNAL(valueChanged(double)),this, SLOT(updateMinMaxSettings()));
connect(ui->minY, SIGNAL(valueChanged(double)),this, SLOT(updateMinMaxSettings()));
connect(ui->maxY, SIGNAL(valueChanged(double)),this, SLOT(updateMinMaxSettings()));
connect(ui->automaticAxisRange, SIGNAL(toggled(bool)),this, SLOT(updateMinMaxSettings()));
connect(ui->maxDataShowSpinBox, SIGNAL(valueChanged(int)),this, SLOT(updateMinMaxSettings()));
connect(ui->maxDataStoreSpinBox, SIGNAL(valueChanged(int)),this, SLOT(updateMinMaxSettings()));
connect(ui->smoothSpinBox, SIGNAL(valueChanged(int)),this, SLOT(updateMinMaxSettings()));
setEditMode(false);
}
QGCXYPlot::~QGCXYPlot()
{
delete ui;
}
void QGCXYPlot::clearPlot()
{
xycurve->clear();
plot->clear();
}
void QGCXYPlot::setEditMode(bool editMode)
{
ui->lblXParam->setVisible(editMode);
ui->lblYParam->setVisible(editMode);
ui->editXParam->setVisible(editMode);
ui->editYParam->setVisible(editMode);
ui->editFinishButton->setVisible(editMode);
ui->editLine1->setVisible(editMode);
ui->editLine2->setVisible(editMode);
ui->lblMaxDataStore->setVisible(editMode);
ui->lblMaxDataShow->setVisible(editMode);
ui->lblMaxX->setVisible(editMode);
ui->lblMaxY->setVisible(editMode);
ui->lblMinX->setVisible(editMode);
ui->lblMinY->setVisible(editMode);
ui->maxX->setVisible(editMode);
ui->maxY->setVisible(editMode);
ui->minX->setVisible(editMode);
ui->minY->setVisible(editMode);
ui->maxDataShowSpinBox->setVisible(editMode);
ui->maxDataStoreSpinBox->setVisible(editMode);
ui->lblSmooth->setVisible(editMode);
ui->smoothSpinBox->setVisible(editMode);
if(!editMode) {
plot->setAxisTitle(QwtPlot::xBottom, ui->editXParam->currentText());
plot->setAxisTitle(QwtPlot::yLeft, ui->editYParam->currentText());
}
QGCToolWidgetItem::setEditMode(editMode);
updateMinMaxSettings(); //Do this after calling the parent
}
void QGCXYPlot::writeSettings(QSettings& settings)
{
settings.setValue("TYPE", "XYPLOT");
settings.setValue("QGC_XYPLOT_X", ui->editXParam->currentText());
settings.setValue("QGC_XYPLOT_Y", ui->editYParam->currentText());
settings.setValue("QGC_XYPLOT_MINX", ui->minX->value());
settings.setValue("QGC_XYPLOT_MAXX", ui->maxX->value());
settings.setValue("QGC_XYPLOT_MINY", ui->minY->value());
settings.setValue("QGC_XYPLOT_MAXY", ui->maxY->value());
settings.setValue("QGC_XYPLOT_MAXDATA_STORE", ui->maxDataStoreSpinBox->value());
settings.setValue("QGC_XYPLOT_MAXDATA_SHOW", ui->maxDataShowSpinBox->value());
settings.setValue("QGC_XYPLOT_AUTO", ui->automaticAxisRange->isChecked());
settings.setValue("QGC_XYPLOT_SMOOTH", ui->smoothSpinBox->value());
settings.sync();
}
void QGCXYPlot::readSettings(const QString& pre,const QVariantMap& settings)
{
ui->editXParam->setEditText(settings.value(pre + "QGC_XYPLOT_X", "").toString());
ui->editYParam->setEditText(settings.value(pre + "QGC_XYPLOT_Y", "").toString());
ui->automaticAxisRange->setChecked(settings.value(pre + "QGC_XYPLOT_AUTO", true).toBool());
ui->minX->setValue(settings.value(pre + "QGC_XYPLOT_MINX", 0).toDouble());
ui->maxX->setValue(settings.value(pre + "QGC_XYPLOT_MAXX", 0).toDouble());
ui->minY->setValue(settings.value(pre + "QGC_XYPLOT_MINY", 0).toDouble());
ui->maxY->setValue(settings.value(pre + "QGC_XYPLOT_MAXY", 0).toDouble());
ui->maxDataStoreSpinBox->setValue(settings.value(pre + "QGC_XYPLOT_MAXDATA_STORE", 10000).toInt());
ui->maxDataShowSpinBox->setValue(settings.value(pre + "QGC_XYPLOT_MAXDATA_SHOW", 15).toInt());
ui->smoothSpinBox->setValue(settings.value(pre + "QGC_XYPLOT_SMOOTH", 1).toInt());
plot->setAxisTitle(QwtPlot::xBottom, ui->editXParam->currentText());
plot->setAxisTitle(QwtPlot::yLeft, ui->editYParam->currentText());
updateMinMaxSettings();
}
void QGCXYPlot::readSettings(const QSettings& settings)
{
ui->editXParam->setEditText(settings.value("QGC_XYPLOT_X", "").toString());
ui->editYParam->setEditText(settings.value("QGC_XYPLOT_Y", "").toString());
ui->automaticAxisRange->setChecked(settings.value("QGC_XYPLOT_AUTO", true).toBool());
ui->minX->setValue(settings.value("QGC_XYPLOT_MINX", 0).toDouble());
ui->maxX->setValue(settings.value("QGC_XYPLOT_MAXX", 0).toDouble());
ui->minY->setValue(settings.value("QGC_XYPLOT_MINY", 0).toDouble());
ui->maxY->setValue(settings.value("QGC_XYPLOT_MAXY", 0).toDouble());
ui->maxDataStoreSpinBox->setValue(settings.value("QGC_XYPLOT_MAXDATA_STORE", 10000).toInt());
ui->maxDataShowSpinBox->setValue(settings.value("QGC_XYPLOT_MAXDATA_SHOW", 15).toInt());
ui->smoothSpinBox->setValue(settings.value("QGC_XYPLOT_SMOOTH", 1).toInt());
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
plot->setAxisTitle(QwtPlot::xBottom, ui->editXParam->currentText());
plot->setAxisTitle(QwtPlot::yLeft, ui->editYParam->currentText());
updateMinMaxSettings();
}
void QGCXYPlot::appendData(int uasId, const QString& curve, const QString& unit, const QVariant& variant, quint64 usec)
{
Q_UNUSED(uasId);
Q_UNUSED(unit);
if(isEditMode()) {
//When in edit mode, add all the items to the combo box
if(ui->editXParam->findText(curve) == -1) {
QString oldX = ui->editXParam->currentText();
QString oldY = ui->editYParam->currentText();
ui->editXParam->addItem(curve); //Annoyingly this can wipe out the current text
ui->editYParam->addItem(curve);
ui->editXParam->setEditText(oldX);
ui->editYParam->setEditText(oldY);
}
}
bool ok;
if(curve == ui->editXParam->currentText()) {
x = variant.toDouble(&ok);
if(!ok)
return;
x_timestamp_us = usec;
x_valid = true;
} else if(curve == ui->editYParam->currentText()) {
y = variant.toDouble(&ok);
if(!ok)
return;
y_timestamp_us = usec;
y_valid = true;
} else
return;
if(x_valid && y_valid && (int)qAbs(y_timestamp_us - x_timestamp_us) <= max_timestamp_diff_us) {
xycurve->appendData( QPointF(x,y) );
plot->update();
x_valid = false;
y_valid = false;
}
}
void QGCXYPlot::styleChanged(MainWindow::QGC_MAINWINDOW_STYLE style)
{
if (style == MainWindow::QGC_MAINWINDOW_STYLE_LIGHT)
xycurve->setColor(Qt::black);
else
xycurve->setColor(Qt::white);
}
void QGCXYPlot::updateMinMaxSettings()
{
bool automatic = ui->automaticAxisRange->isChecked();
ui->minX->setEnabled(!automatic);
ui->maxX->setEnabled(!automatic);
ui->minY->setEnabled(!automatic);
ui->maxY->setEnabled(!automatic);
if(automatic) {
xycurve->unsetMinMax();
} else {
xycurve->setMinMax(ui->minX->value(), ui->maxX->value(), ui->minY->value(), ui->maxY->value());
}
xycurve->setMaxDataStorePoints(ui->maxDataStoreSpinBox->value());
xycurve->setMaxDataShowPoints(ui->maxDataShowSpinBox->value());
xycurve->setSmoothPoints(ui->smoothSpinBox->value());
}
void QGCXYPlot::on_maxDataShowSpinBox_valueChanged(int value)
{
ui->maxDataStoreSpinBox->setMinimum(value);
if(ui->maxDataStoreSpinBox->value() < value)
ui->maxDataStoreSpinBox->setValue(value);