2020-03-28 23:28:39 +01:00
|
|
|
#include <QSet>
|
2016-10-29 18:01:25 +02:00
|
|
|
#include <QGraphicsScene>
|
2015-10-05 01:43:48 +02:00
|
|
|
#include <QEvent>
|
2016-10-29 18:01:25 +02:00
|
|
|
#include <QMouseEvent>
|
2019-08-26 21:03:40 +02:00
|
|
|
#include <QScrollBar>
|
2018-06-22 17:29:05 +02:00
|
|
|
#include <QGraphicsSimpleTextItem>
|
|
|
|
#include <QPalette>
|
2018-09-09 18:46:43 +02:00
|
|
|
#include <QLocale>
|
2020-12-22 22:09:09 +01:00
|
|
|
#include <QOpenGLWidget>
|
2017-11-26 18:54:03 +01:00
|
|
|
#include "data/graph.h"
|
2016-02-12 10:09:17 +01:00
|
|
|
#include "axisitem.h"
|
2020-11-22 14:38:52 +01:00
|
|
|
#include "axislabelitem.h"
|
2015-10-12 01:12:12 +02:00
|
|
|
#include "slideritem.h"
|
2015-10-17 01:33:02 +02:00
|
|
|
#include "sliderinfoitem.h"
|
2015-10-12 01:12:12 +02:00
|
|
|
#include "infoitem.h"
|
2016-10-17 23:14:07 +02:00
|
|
|
#include "griditem.h"
|
2016-08-16 00:27:54 +02:00
|
|
|
#include "graphitem.h"
|
2016-09-19 00:56:10 +02:00
|
|
|
#include "pathitem.h"
|
2017-09-29 11:43:09 +02:00
|
|
|
#include "format.h"
|
2019-10-15 23:59:15 +02:00
|
|
|
#include "graphicsscene.h"
|
2016-02-11 20:58:52 +01:00
|
|
|
#include "graphview.h"
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2015-10-17 01:33:02 +02:00
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
#define MARGIN 10.0
|
|
|
|
|
2020-11-22 14:38:52 +01:00
|
|
|
#define IW(item) ((item)->boundingRect().width())
|
|
|
|
#define IH(item) ((item)->boundingRect().height())
|
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
GraphView::GraphView(QWidget *parent)
|
2015-10-05 01:43:48 +02:00
|
|
|
: QGraphicsView(parent)
|
|
|
|
{
|
2019-10-15 23:59:15 +02:00
|
|
|
_scene = new GraphicsScene(this);
|
2015-10-05 01:43:48 +02:00
|
|
|
setScene(_scene);
|
|
|
|
|
2016-12-07 02:02:39 +01:00
|
|
|
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
|
|
|
setRenderHint(QPainter::Antialiasing, true);
|
2016-03-30 01:48:48 +02:00
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
2019-01-04 20:23:02 +01:00
|
|
|
setBackgroundBrush(QBrush(palette().brush(QPalette::Base)));
|
2016-03-30 01:48:48 +02:00
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
_xAxis = new AxisItem(AxisItem::X);
|
2019-02-11 23:28:08 +01:00
|
|
|
_xAxis->setZValue(1.0);
|
2015-10-05 01:43:48 +02:00
|
|
|
_yAxis = new AxisItem(AxisItem::Y);
|
2019-02-11 23:28:08 +01:00
|
|
|
_yAxis->setZValue(1.0);
|
2020-11-22 14:38:52 +01:00
|
|
|
_xAxisLabel = new AxisLabelItem(AxisLabelItem::X);
|
|
|
|
_xAxisLabel->setZValue(1.0);
|
|
|
|
_yAxisLabel = new AxisLabelItem(AxisLabelItem::Y);
|
|
|
|
_yAxisLabel->setZValue(1.0);
|
2015-10-05 01:43:48 +02:00
|
|
|
_slider = new SliderItem();
|
2019-10-15 23:59:15 +02:00
|
|
|
_slider->setZValue(4.0);
|
2016-08-16 00:27:54 +02:00
|
|
|
_sliderInfo = new SliderInfoItem(_slider);
|
2019-10-15 23:59:15 +02:00
|
|
|
_sliderInfo->setZValue(4.0);
|
2016-08-16 00:27:54 +02:00
|
|
|
_info = new InfoItem();
|
2016-10-17 23:14:07 +02:00
|
|
|
_grid = new GridItem();
|
2018-06-22 17:29:05 +02:00
|
|
|
_message = new QGraphicsSimpleTextItem(tr("Data not available"));
|
|
|
|
_message->setBrush(QPalette().brush(QPalette::Disabled,
|
|
|
|
QPalette::WindowText));
|
2015-10-13 23:49:15 +02:00
|
|
|
|
2021-04-28 00:01:07 +02:00
|
|
|
connect(_slider, &SliderItem::positionChanged, this,
|
|
|
|
&GraphView::emitSliderPositionChanged);
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2016-12-06 01:48:26 +01:00
|
|
|
_width = 1;
|
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
_xScale = 1;
|
|
|
|
_yScale = 1;
|
2016-06-16 20:47:32 +02:00
|
|
|
_yOffset = 0;
|
2015-10-17 01:33:02 +02:00
|
|
|
|
|
|
|
_precision = 0;
|
2016-03-27 13:23:00 +02:00
|
|
|
_minYRange = 0.01;
|
2016-03-30 20:50:51 +02:00
|
|
|
|
|
|
|
_sliderPos = 0;
|
2016-09-19 00:56:10 +02:00
|
|
|
|
|
|
|
_units = Metric;
|
2016-09-19 23:35:04 +02:00
|
|
|
_graphType = Distance;
|
2016-10-17 23:14:07 +02:00
|
|
|
_xLabel = tr("Distance");
|
2019-08-26 21:03:40 +02:00
|
|
|
|
|
|
|
_zoom = 1.0;
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2016-08-30 21:26:28 +02:00
|
|
|
GraphView::~GraphView()
|
|
|
|
{
|
2018-06-01 19:07:21 +02:00
|
|
|
delete _xAxis;
|
|
|
|
delete _yAxis;
|
2020-11-22 14:38:52 +01:00
|
|
|
delete _xAxisLabel;
|
|
|
|
delete _yAxisLabel;
|
2018-06-01 19:07:21 +02:00
|
|
|
delete _slider;
|
|
|
|
delete _info;
|
|
|
|
delete _grid;
|
2018-06-28 08:36:11 +02:00
|
|
|
delete _message;
|
2016-08-30 21:26:28 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
void GraphView::setYLabel(const QString &label)
|
2015-10-12 01:12:12 +02:00
|
|
|
{
|
2016-09-19 00:56:10 +02:00
|
|
|
_yLabel = label;
|
2020-11-22 14:51:57 +01:00
|
|
|
_yAxisLabel->setLabel(_yLabel, _yUnits);
|
2015-10-12 01:12:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
void GraphView::setYUnits(const QString &units)
|
2015-10-12 01:12:12 +02:00
|
|
|
{
|
2016-09-19 00:56:10 +02:00
|
|
|
_yUnits = units;
|
2020-11-22 14:51:57 +01:00
|
|
|
_yAxisLabel->setLabel(_yLabel, _yUnits);
|
2015-10-12 01:12:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
void GraphView::setXUnits()
|
2015-10-12 01:12:12 +02:00
|
|
|
{
|
2016-09-19 23:35:04 +02:00
|
|
|
if (_graphType == Distance) {
|
2018-02-11 23:51:57 +01:00
|
|
|
if (_units == Imperial) {
|
2016-09-19 00:56:10 +02:00
|
|
|
if (bounds().width() < MIINM) {
|
|
|
|
_xUnits = tr("ft");
|
|
|
|
_xScale = M2FT;
|
|
|
|
} else {
|
|
|
|
_xUnits = tr("mi");
|
|
|
|
_xScale = M2MI;
|
|
|
|
}
|
2018-02-11 23:51:57 +01:00
|
|
|
} else if (_units == Nautical) {
|
|
|
|
if (bounds().width() < NMIINM) {
|
|
|
|
_xUnits = tr("ft");
|
|
|
|
_xScale = M2FT;
|
|
|
|
} else {
|
|
|
|
_xUnits = tr("nmi");
|
|
|
|
_xScale = M2NMI;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bounds().width() < KMINM) {
|
|
|
|
_xUnits = tr("m");
|
|
|
|
_xScale = 1;
|
|
|
|
} else {
|
|
|
|
_xUnits = tr("km");
|
|
|
|
_xScale = M2KM;
|
|
|
|
}
|
2016-09-19 00:56:10 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bounds().width() < MININS) {
|
|
|
|
_xUnits = tr("s");
|
|
|
|
_xScale = 1;
|
|
|
|
} else if (bounds().width() < HINS) {
|
|
|
|
_xUnits = tr("min");
|
|
|
|
_xScale = MIN2S;
|
|
|
|
} else {
|
|
|
|
_xUnits = tr("h");
|
|
|
|
_xScale = H2S;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-22 14:51:57 +01:00
|
|
|
_xAxisLabel->setLabel(_xLabel, _xUnits);
|
2015-10-12 01:12:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
void GraphView::setUnits(Units units)
|
2015-10-12 01:12:12 +02:00
|
|
|
{
|
2016-09-19 00:56:10 +02:00
|
|
|
_units = units;
|
2017-09-24 19:54:13 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < _graphs.count(); i++)
|
|
|
|
_graphs.at(i)->setUnits(units);
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
setXUnits();
|
2017-10-04 23:15:39 +02:00
|
|
|
|
|
|
|
redraw();
|
2015-10-12 01:12:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 23:35:04 +02:00
|
|
|
void GraphView::setGraphType(GraphType type)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2016-09-19 00:56:10 +02:00
|
|
|
_graphType = type;
|
|
|
|
_bounds = QRectF();
|
2020-11-22 14:38:52 +01:00
|
|
|
_zoom = 1.0;
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
for (int i = 0; i < _graphs.count(); i++) {
|
2018-06-22 17:29:05 +02:00
|
|
|
GraphItem *gi = _graphs.at(i);
|
|
|
|
gi->setGraphType(type);
|
2019-10-27 19:53:32 +01:00
|
|
|
if (gi->bounds().isNull())
|
|
|
|
removeItem(gi);
|
|
|
|
else
|
|
|
|
addItem(gi);
|
2019-08-25 10:54:25 +02:00
|
|
|
_bounds |= gi->bounds();
|
2016-09-19 00:56:10 +02:00
|
|
|
}
|
|
|
|
|
2016-09-19 23:35:04 +02:00
|
|
|
if (type == Distance)
|
2016-09-19 00:56:10 +02:00
|
|
|
_xLabel = tr("Distance");
|
|
|
|
else
|
|
|
|
_xLabel = tr("Time");
|
|
|
|
setXUnits();
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2021-01-17 16:02:37 +01:00
|
|
|
if (singleGraph())
|
|
|
|
_sliderPos = (type == Distance)
|
|
|
|
? _graphs.first()->distanceAtTime(_sliderPos)
|
|
|
|
: _graphs.first()->timeAtDistance(_sliderPos);
|
|
|
|
else
|
|
|
|
_sliderPos = 0;
|
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
redraw();
|
|
|
|
}
|
|
|
|
|
2016-10-17 23:14:07 +02:00
|
|
|
void GraphView::showGrid(bool show)
|
|
|
|
{
|
|
|
|
_grid->setVisible(show);
|
|
|
|
}
|
|
|
|
|
2017-09-29 11:43:09 +02:00
|
|
|
void GraphView::showSliderInfo(bool show)
|
|
|
|
{
|
|
|
|
_sliderInfo->setVisible(show);
|
|
|
|
}
|
|
|
|
|
2019-08-25 10:54:25 +02:00
|
|
|
void GraphView::addGraph(GraphItem *graph)
|
2016-09-19 00:56:10 +02:00
|
|
|
{
|
2017-09-24 19:54:13 +02:00
|
|
|
_graphs.append(graph);
|
2019-10-27 19:53:32 +01:00
|
|
|
if (!graph->bounds().isNull())
|
|
|
|
_scene->addItem(graph);
|
2019-08-25 10:54:25 +02:00
|
|
|
_bounds |= graph->bounds();
|
2016-08-19 19:48:44 +02:00
|
|
|
|
2019-08-25 10:54:25 +02:00
|
|
|
setXUnits();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::removeGraph(GraphItem *graph)
|
|
|
|
{
|
|
|
|
_graphs.removeOne(graph);
|
|
|
|
_scene->removeItem(graph);
|
|
|
|
|
|
|
|
_bounds = QRectF();
|
|
|
|
for (int i = 0; i < _graphs.count(); i++)
|
|
|
|
_bounds |= _graphs.at(i)->bounds();
|
|
|
|
|
|
|
|
setXUnits();
|
2015-12-19 20:23:07 +01:00
|
|
|
}
|
|
|
|
|
2016-08-30 08:39:14 +02:00
|
|
|
void GraphView::removeItem(QGraphicsItem *item)
|
|
|
|
{
|
|
|
|
if (item->scene() == _scene)
|
|
|
|
_scene->removeItem(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::addItem(QGraphicsItem *item)
|
|
|
|
{
|
|
|
|
if (item->scene() != _scene)
|
|
|
|
_scene->addItem(item);
|
|
|
|
}
|
|
|
|
|
2016-09-26 10:09:56 +02:00
|
|
|
QRectF GraphView::bounds() const
|
2016-08-19 19:48:44 +02:00
|
|
|
{
|
2016-09-26 10:09:56 +02:00
|
|
|
QRectF br(_bounds);
|
2016-08-19 19:48:44 +02:00
|
|
|
br.moveTopLeft(QPointF(br.left(), -br.top() - br.height()));
|
2016-09-26 10:09:56 +02:00
|
|
|
return br;
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2017-10-04 23:15:39 +02:00
|
|
|
void GraphView::redraw()
|
|
|
|
{
|
2018-06-22 17:29:05 +02:00
|
|
|
if (!_graphs.isEmpty())
|
|
|
|
redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
|
2017-10-04 23:15:39 +02:00
|
|
|
}
|
|
|
|
|
2016-03-23 09:22:26 +01:00
|
|
|
void GraphView::redraw(const QSizeF &size)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
|
|
|
QRectF r;
|
|
|
|
QSizeF mx, my;
|
2016-03-27 13:23:00 +02:00
|
|
|
RangeF rx, ry;
|
2016-10-12 20:38:18 +02:00
|
|
|
qreal sx, sy;
|
2015-10-05 01:43:48 +02:00
|
|
|
|
|
|
|
|
2018-06-28 08:36:11 +02:00
|
|
|
if (_bounds.isNull()) {
|
2016-08-30 08:39:14 +02:00
|
|
|
removeItem(_xAxis);
|
|
|
|
removeItem(_yAxis);
|
2020-11-22 14:38:52 +01:00
|
|
|
removeItem(_xAxisLabel);
|
|
|
|
removeItem(_yAxisLabel);
|
2016-08-30 08:39:14 +02:00
|
|
|
removeItem(_slider);
|
|
|
|
removeItem(_info);
|
2016-10-17 23:14:07 +02:00
|
|
|
removeItem(_grid);
|
2018-06-22 17:29:05 +02:00
|
|
|
addItem(_message);
|
|
|
|
_scene->setSceneRect(_scene->itemsBoundingRect());
|
2016-08-30 08:39:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-22 17:29:05 +02:00
|
|
|
removeItem(_message);
|
2016-08-30 08:39:14 +02:00
|
|
|
addItem(_xAxis);
|
|
|
|
addItem(_yAxis);
|
2020-11-22 14:38:52 +01:00
|
|
|
addItem(_xAxisLabel);
|
|
|
|
addItem(_yAxisLabel);
|
2016-08-30 08:39:14 +02:00
|
|
|
addItem(_slider);
|
|
|
|
addItem(_info);
|
2016-10-17 23:14:07 +02:00
|
|
|
addItem(_grid);
|
2016-08-30 08:39:14 +02:00
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
|
|
|
|
ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
|
2016-06-16 20:47:32 +02:00
|
|
|
+ _yOffset);
|
2020-03-14 23:55:57 +01:00
|
|
|
if (ry.size() < _minYRange * _yScale)
|
|
|
|
ry.resize(_minYRange * _yScale);
|
2016-03-27 13:23:00 +02:00
|
|
|
|
2020-11-22 14:38:52 +01:00
|
|
|
_xAxis->setZoom(_zoom);
|
2016-03-17 00:50:20 +01:00
|
|
|
_xAxis->setRange(rx);
|
2020-11-21 20:37:22 +01:00
|
|
|
_xAxis->setZoom(_zoom);
|
2016-03-17 00:50:20 +01:00
|
|
|
_yAxis->setRange(ry);
|
2015-10-05 01:43:48 +02:00
|
|
|
mx = _xAxis->margin();
|
|
|
|
my = _yAxis->margin();
|
2016-03-17 00:50:20 +01:00
|
|
|
|
2016-09-26 10:09:56 +02:00
|
|
|
r = _bounds;
|
2016-03-27 13:23:00 +02:00
|
|
|
if (r.height() < _minYRange)
|
|
|
|
r.adjust(0, -(_minYRange/2 - r.height()/2), 0,
|
|
|
|
_minYRange/2 - r.height()/2);
|
2016-03-17 00:50:20 +01:00
|
|
|
|
2020-11-22 14:38:52 +01:00
|
|
|
sx = (size.width() - (my.width() + mx.width()) - IW(_yAxisLabel))
|
|
|
|
/ r.width();
|
2016-10-12 20:38:18 +02:00
|
|
|
sy = (size.height() - (mx.height() + my.height())
|
2020-11-22 14:38:52 +01:00
|
|
|
- IH(_info) - IH(_xAxisLabel)) / r.height();
|
2019-08-26 21:03:40 +02:00
|
|
|
sx *= _zoom;
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2019-08-25 10:54:25 +02:00
|
|
|
for (int i = 0; i < _graphs.size(); i++)
|
|
|
|
_graphs.at(i)->setScale(sx, sy);
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2016-10-12 20:38:18 +02:00
|
|
|
QPointF p(r.left() * sx, r.top() * sy);
|
|
|
|
QSizeF s(r.width() * sx, r.height() * sy);
|
2016-08-19 19:48:44 +02:00
|
|
|
r = QRectF(p, s);
|
2016-10-12 20:38:18 +02:00
|
|
|
if (r.height() < _minYRange * sy)
|
|
|
|
r.adjust(0, -(_minYRange/2 * sy - r.height()/2), 0,
|
|
|
|
(_minYRange/2) * sy - r.height()/2);
|
2018-05-20 19:38:01 +02:00
|
|
|
r = r.toRect();
|
2016-03-17 00:50:20 +01:00
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
_xAxis->setSize(r.width());
|
|
|
|
_yAxis->setSize(r.height());
|
|
|
|
_xAxis->setPos(r.bottomLeft());
|
|
|
|
_yAxis->setPos(r.bottomLeft());
|
|
|
|
|
2016-10-17 23:14:07 +02:00
|
|
|
_grid->setSize(r.size());
|
|
|
|
_grid->setTicks(_xAxis->ticks(), _yAxis->ticks());
|
|
|
|
_grid->setPos(r.bottomLeft());
|
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
_slider->setArea(r);
|
2016-08-16 00:27:54 +02:00
|
|
|
updateSliderPosition();
|
2015-12-19 20:49:22 +01:00
|
|
|
|
2020-11-22 14:38:52 +01:00
|
|
|
_info->setPos(QPointF(r.width()/2 - IW(_info)/2 - (IW(_yAxisLabel)
|
|
|
|
+ IW(_yAxis))/2, r.top() - IH(_info) - my.height()));
|
|
|
|
_xAxisLabel->setPos(QPointF(r.width()/2 - IW(_xAxisLabel)/2,
|
|
|
|
r.bottom() + mx.height()));
|
|
|
|
_yAxisLabel->setPos(QPointF(r.left() - my.width() - IW(_yAxisLabel),
|
|
|
|
r.bottom() - (r.height()/2 + IH(_yAxisLabel)/2)));
|
2015-10-12 01:12:12 +02:00
|
|
|
|
2015-10-05 01:43:48 +02:00
|
|
|
_scene->setSceneRect(_scene->itemsBoundingRect());
|
|
|
|
}
|
|
|
|
|
2018-06-22 17:29:05 +02:00
|
|
|
void GraphView::resizeEvent(QResizeEvent *e)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2018-06-22 17:29:05 +02:00
|
|
|
redraw(e->size() - QSizeF(MARGIN, MARGIN));
|
|
|
|
|
|
|
|
QGraphicsView::resizeEvent(e);
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2016-10-29 18:01:25 +02:00
|
|
|
void GraphView::mousePressEvent(QMouseEvent *e)
|
|
|
|
{
|
|
|
|
if (e->button() == Qt::LeftButton)
|
|
|
|
newSliderPosition(mapToScene(e->pos()));
|
|
|
|
|
|
|
|
QGraphicsView::mousePressEvent(e);
|
|
|
|
}
|
|
|
|
|
2019-08-26 21:03:40 +02:00
|
|
|
void GraphView::wheelEvent(QWheelEvent *e)
|
|
|
|
{
|
|
|
|
static int deg = 0;
|
|
|
|
|
2020-12-22 22:09:09 +01:00
|
|
|
deg += e->angleDelta().y() / 8;
|
2019-08-26 21:03:40 +02:00
|
|
|
if (qAbs(deg) < 15)
|
|
|
|
return;
|
|
|
|
deg = 0;
|
|
|
|
|
2020-12-22 22:09:09 +01:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
2019-08-26 21:03:40 +02:00
|
|
|
QPointF pos = mapToScene(e->pos());
|
2020-12-22 22:09:09 +01:00
|
|
|
#else // QT 5.15
|
|
|
|
QPointF pos = mapToScene(e->position().toPoint());
|
|
|
|
#endif // QT 5.15
|
2019-08-26 21:03:40 +02:00
|
|
|
QRectF gr(_grid->boundingRect());
|
|
|
|
QPointF r(pos.x() / gr.width(), pos.y() / gr.height());
|
|
|
|
|
2020-12-22 22:09:09 +01:00
|
|
|
_zoom = (e->angleDelta().y() > 0) ? _zoom * 1.25 : qMax(_zoom / 1.25, 1.0);
|
2019-08-26 21:03:40 +02:00
|
|
|
redraw();
|
|
|
|
|
2019-08-27 20:18:39 +02:00
|
|
|
QRectF ngr(_grid->boundingRect());
|
|
|
|
QPointF npos(mapFromScene(QPointF(r.x() * ngr.width(),
|
|
|
|
r.y() * ngr.height())));
|
2019-08-26 21:03:40 +02:00
|
|
|
QScrollBar *sb = horizontalScrollBar();
|
2020-12-22 22:09:09 +01:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
2019-08-26 21:03:40 +02:00
|
|
|
sb->setSliderPosition(sb->sliderPosition() + npos.x() - e->pos().x());
|
2020-12-22 22:09:09 +01:00
|
|
|
#else // QT 5.15
|
|
|
|
sb->setSliderPosition(sb->sliderPosition() + npos.x() - e->position().x());
|
|
|
|
#endif // QT 5.15
|
2019-08-26 21:03:40 +02:00
|
|
|
|
|
|
|
QGraphicsView::wheelEvent(e);
|
|
|
|
}
|
|
|
|
|
2019-10-13 20:20:32 +02:00
|
|
|
void GraphView::paintEvent(QPaintEvent *e)
|
2019-08-26 21:03:40 +02:00
|
|
|
{
|
|
|
|
QRectF viewRect(mapToScene(rect()).boundingRect());
|
2020-11-22 14:38:52 +01:00
|
|
|
_info->setPos(QPointF(viewRect.left() + (viewRect.width() - IW(_info))/2.0,
|
|
|
|
_info->pos().y()));
|
|
|
|
_xAxisLabel->setPos(QPointF(viewRect.left() + (viewRect.width()
|
|
|
|
- IW(_xAxisLabel))/2.0, _xAxisLabel->pos().y()));
|
2019-08-26 21:03:40 +02:00
|
|
|
|
2019-10-13 20:20:32 +02:00
|
|
|
QGraphicsView::paintEvent(e);
|
2019-08-26 21:03:40 +02:00
|
|
|
}
|
|
|
|
|
2017-08-31 16:28:37 +02:00
|
|
|
void GraphView::plot(QPainter *painter, const QRectF &target, qreal scale)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2017-08-31 16:28:37 +02:00
|
|
|
QSizeF canvas = QSizeF(target.width() / scale, target.height() / scale);
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2016-05-20 22:44:03 +02:00
|
|
|
setUpdatesEnabled(false);
|
2016-03-23 09:22:26 +01:00
|
|
|
redraw(canvas);
|
2016-05-21 16:10:24 +02:00
|
|
|
if (_slider->pos().x() == _slider->area().left())
|
|
|
|
_slider->hide();
|
2016-05-22 11:54:27 +02:00
|
|
|
_scene->render(painter, target);
|
2015-10-19 00:35:08 +02:00
|
|
|
_slider->show();
|
2016-05-20 23:47:37 +02:00
|
|
|
redraw();
|
2016-05-20 22:44:03 +02:00
|
|
|
setUpdatesEnabled(true);
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
void GraphView::clear()
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2019-08-25 10:54:25 +02:00
|
|
|
_graphs.clear();
|
|
|
|
|
2015-12-19 20:23:07 +01:00
|
|
|
_slider->clear();
|
2015-10-13 00:27:49 +02:00
|
|
|
_info->clear();
|
2016-08-19 19:48:44 +02:00
|
|
|
|
2016-03-02 09:34:39 +01:00
|
|
|
_palette.reset();
|
2015-10-05 01:43:48 +02:00
|
|
|
|
2016-03-27 13:23:00 +02:00
|
|
|
_bounds = QRectF();
|
2016-03-30 20:50:51 +02:00
|
|
|
_sliderPos = 0;
|
2019-08-26 21:03:40 +02:00
|
|
|
_zoom = 1.0;
|
2015-10-05 01:43:48 +02:00
|
|
|
|
|
|
|
_scene->setSceneRect(0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
2016-08-16 00:27:54 +02:00
|
|
|
void GraphView::updateSliderPosition()
|
|
|
|
{
|
2016-09-19 00:56:10 +02:00
|
|
|
if (_sliderPos <= bounds().right() && _sliderPos >= bounds().left()) {
|
|
|
|
_slider->setPos((_sliderPos / bounds().width())
|
|
|
|
* _slider->area().width(), _slider->area().bottom());
|
2019-08-25 10:54:25 +02:00
|
|
|
_slider->setVisible(true);
|
|
|
|
updateSliderInfo();
|
2016-08-19 19:48:44 +02:00
|
|
|
} else {
|
|
|
|
_slider->setPos(_slider->area().left(), _slider->area().bottom());
|
2016-08-16 10:25:08 +02:00
|
|
|
_slider->setVisible(false);
|
2016-08-19 19:48:44 +02:00
|
|
|
}
|
2016-08-16 00:27:54 +02:00
|
|
|
}
|
|
|
|
|
2021-01-17 16:02:37 +01:00
|
|
|
bool GraphView::singleGraph() const
|
|
|
|
{
|
|
|
|
return (_graphs.count() == 1
|
|
|
|
|| (_graphs.count() == 2 && _graphs.first()->secondaryGraph()));
|
|
|
|
}
|
|
|
|
|
2016-03-30 20:50:51 +02:00
|
|
|
void GraphView::updateSliderInfo()
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2018-09-09 18:46:43 +02:00
|
|
|
QLocale l(QLocale::system());
|
2019-08-25 10:54:25 +02:00
|
|
|
qreal r = 0, y = 0;
|
2021-01-17 16:02:37 +01:00
|
|
|
GraphItem *cardinal = singleGraph() ? _graphs.first() : 0;
|
2017-09-29 11:43:09 +02:00
|
|
|
|
2020-03-25 23:08:26 +01:00
|
|
|
if (cardinal) {
|
|
|
|
QRectF br(_bounds);
|
2017-09-29 11:43:09 +02:00
|
|
|
if (br.height() < _minYRange)
|
|
|
|
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
|
|
|
|
_minYRange/2 - br.height()/2);
|
|
|
|
|
2021-01-17 16:02:37 +01:00
|
|
|
y = -cardinal->yAtX(_sliderPos);
|
2017-09-29 11:43:09 +02:00
|
|
|
r = (y - br.bottom()) / br.height();
|
|
|
|
}
|
2016-03-30 01:48:48 +02:00
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
qreal pos = (_sliderPos / bounds().width()) * _slider->area().width();
|
2016-03-30 20:50:51 +02:00
|
|
|
SliderInfoItem::Side s = (pos + _sliderInfo->boundingRect().width()
|
2016-03-30 01:48:48 +02:00
|
|
|
> _slider->area().right()) ? SliderInfoItem::Left : SliderInfoItem::Right;
|
|
|
|
|
|
|
|
_sliderInfo->setSide(s);
|
2015-10-17 01:33:02 +02:00
|
|
|
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
|
2020-03-25 23:08:26 +01:00
|
|
|
QString xText(_graphType == Time ? Format::timeSpan(_sliderPos,
|
2018-09-09 18:46:43 +02:00
|
|
|
bounds().width() > 3600) : l.toString(_sliderPos * _xScale, 'f', 1)
|
2020-03-25 23:08:26 +01:00
|
|
|
+ UNIT_SPACE + _xUnits);
|
|
|
|
QString yText((!cardinal) ? QString() : l.toString(-y * _yScale + _yOffset,
|
|
|
|
'f', _precision) + UNIT_SPACE + _yUnits);
|
|
|
|
if (cardinal && cardinal->secondaryGraph()) {
|
2021-01-17 16:02:37 +01:00
|
|
|
qreal delta = y + cardinal->secondaryGraph()->yAtX(_sliderPos);
|
2020-12-22 22:09:09 +01:00
|
|
|
yText += QString(" ") + QChar(0x0394) + l.toString(-delta * _yScale
|
|
|
|
+ _yOffset, 'f', _precision) + UNIT_SPACE + _yUnits;
|
2020-03-25 23:08:26 +01:00
|
|
|
}
|
|
|
|
_sliderInfo->setText(xText, yText);
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2016-03-30 20:50:51 +02:00
|
|
|
void GraphView::emitSliderPositionChanged(const QPointF &pos)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2016-08-19 19:48:44 +02:00
|
|
|
if (_slider->area().width() <= 0)
|
|
|
|
return;
|
2016-03-30 20:50:51 +02:00
|
|
|
|
2016-09-19 00:56:10 +02:00
|
|
|
_sliderPos = (pos.x() / _slider->area().width()) * bounds().width();
|
2016-11-18 18:02:40 +01:00
|
|
|
_sliderPos = qMax(_sliderPos, bounds().left());
|
|
|
|
_sliderPos = qMin(_sliderPos, bounds().right());
|
2016-08-16 00:27:54 +02:00
|
|
|
updateSliderPosition();
|
2016-08-19 19:48:44 +02:00
|
|
|
|
2016-08-16 00:27:54 +02:00
|
|
|
emit sliderPositionChanged(_sliderPos);
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
void GraphView::setSliderPosition(qreal pos)
|
2015-10-05 01:43:48 +02:00
|
|
|
{
|
2019-08-25 10:54:25 +02:00
|
|
|
if (_graphs.isEmpty())
|
2016-03-23 09:22:26 +01:00
|
|
|
return;
|
|
|
|
|
2016-08-16 00:27:54 +02:00
|
|
|
_sliderPos = pos;
|
|
|
|
updateSliderPosition();
|
2015-10-05 01:43:48 +02:00
|
|
|
}
|
2015-10-12 01:12:12 +02:00
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
void GraphView::newSliderPosition(const QPointF &pos)
|
2015-10-13 23:49:15 +02:00
|
|
|
{
|
2016-08-16 00:27:54 +02:00
|
|
|
if (_slider->area().contains(pos))
|
2016-08-19 19:48:44 +02:00
|
|
|
_slider->setPos(pos);
|
2015-10-13 23:49:15 +02:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
void GraphView::addInfo(const QString &key, const QString &value)
|
2015-10-12 01:12:12 +02:00
|
|
|
{
|
|
|
|
_info->insert(key, value);
|
|
|
|
}
|
2015-12-19 20:23:07 +01:00
|
|
|
|
2016-02-11 20:58:52 +01:00
|
|
|
void GraphView::clearInfo()
|
2015-12-19 20:23:07 +01:00
|
|
|
{
|
|
|
|
_info->clear();
|
|
|
|
}
|
2016-12-06 01:48:26 +01:00
|
|
|
|
|
|
|
void GraphView::setPalette(const Palette &palette)
|
|
|
|
{
|
|
|
|
_palette = palette;
|
|
|
|
_palette.reset();
|
|
|
|
|
2020-03-28 23:28:39 +01:00
|
|
|
QSet<GraphItem*> secondary;
|
|
|
|
for (int i = 0; i < _graphs.count(); i++) {
|
|
|
|
GraphItem *g = _graphs[i];
|
|
|
|
if (g->secondaryGraph())
|
|
|
|
secondary.insert(g->secondaryGraph());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < _graphs.count(); i++) {
|
|
|
|
GraphItem *g = _graphs[i];
|
|
|
|
if (secondary.contains(g))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QColor color(_palette.nextColor());
|
|
|
|
g->setColor(color);
|
|
|
|
if (g->secondaryGraph())
|
|
|
|
g->secondaryGraph()->setColor(color);
|
|
|
|
}
|
2016-12-06 01:48:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GraphView::setGraphWidth(int width)
|
|
|
|
{
|
2016-12-07 00:03:36 +01:00
|
|
|
_width = width;
|
|
|
|
|
2016-12-06 01:48:26 +01:00
|
|
|
for (int i = 0; i < _graphs.count(); i++)
|
|
|
|
_graphs.at(i)->setWidth(width);
|
2017-10-04 23:15:39 +02:00
|
|
|
|
|
|
|
redraw();
|
2016-12-06 01:48:26 +01:00
|
|
|
}
|
2016-12-07 02:02:39 +01:00
|
|
|
|
|
|
|
void GraphView::useOpenGL(bool use)
|
|
|
|
{
|
2017-02-12 17:38:20 +01:00
|
|
|
if (use)
|
2020-12-22 22:09:09 +01:00
|
|
|
setViewport(new QOpenGLWidget);
|
2017-02-12 17:38:20 +01:00
|
|
|
else
|
2016-12-07 02:02:39 +01:00
|
|
|
setViewport(new QWidget);
|
|
|
|
}
|
2017-09-15 00:07:09 +02:00
|
|
|
|
|
|
|
void GraphView::useAntiAliasing(bool use)
|
|
|
|
{
|
|
|
|
setRenderHint(QPainter::Antialiasing, use);
|
|
|
|
}
|
2017-12-03 00:36:52 +01:00
|
|
|
|
|
|
|
void GraphView::setSliderColor(const QColor &color)
|
|
|
|
{
|
|
|
|
_slider->setColor(color);
|
|
|
|
_sliderInfo->setColor(color);
|
|
|
|
}
|
2019-01-24 00:45:22 +01:00
|
|
|
|
|
|
|
void GraphView::changeEvent(QEvent *e)
|
|
|
|
{
|
|
|
|
if (e->type() == QEvent::PaletteChange) {
|
|
|
|
_message->setBrush(QPalette().brush(QPalette::Disabled,
|
|
|
|
QPalette::WindowText));
|
|
|
|
setBackgroundBrush(QBrush(palette().brush(QPalette::Base)));
|
|
|
|
}
|
|
|
|
|
|
|
|
QGraphicsView::changeEvent(e);
|
|
|
|
}
|