1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-06 23:03:22 +02:00
GPXSee/src/GUI/graphview.cpp

503 lines
10 KiB
C++
Raw Normal View History

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>
#include <QPaintEngine>
#include <QPaintDevice>
2017-11-26 18:54:03 +01:00
#include "data/graph.h"
2016-12-20 00:11:30 +01:00
#include "opengl.h"
#include "config.h"
2016-02-12 10:09:17 +01:00
#include "axisitem.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"
#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"
#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
GraphView::GraphView(QWidget *parent)
2015-10-05 01:43:48 +02:00
: QGraphicsView(parent)
{
2017-01-02 09:42:34 +01:00
_scene = new QGraphicsScene(this);
2015-10-05 01:43:48 +02:00
setScene(_scene);
setBackgroundBrush(QBrush(Qt::white));
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setRenderHint(QPainter::Antialiasing, true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2015-10-05 01:43:48 +02:00
_xAxis = new AxisItem(AxisItem::X);
2016-12-06 01:48:26 +01:00
_xAxis->setZValue(2.0);
2015-10-05 01:43:48 +02:00
_yAxis = new AxisItem(AxisItem::Y);
2016-12-06 01:48:26 +01:00
_yAxis->setZValue(2.0);
2015-10-05 01:43:48 +02:00
_slider = new SliderItem();
2016-12-06 01:48:26 +01:00
_slider->setZValue(3.0);
_sliderInfo = new SliderInfoItem(_slider);
2016-12-06 01:48:26 +01:00
_sliderInfo->setZValue(3.0);
_info = new InfoItem();
2016-10-17 23:14:07 +02:00
_grid = new GridItem();
2015-10-13 23:49:15 +02:00
2015-10-05 01:43:48 +02:00
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
SLOT(emitSliderPositionChanged(const QPointF&)));
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");
2015-10-05 01:43:48 +02:00
}
GraphView::~GraphView()
{
if (_xAxis->scene() != _scene)
delete _xAxis;
if (_yAxis->scene() != _scene)
delete _yAxis;
if (_slider->scene() != _scene)
delete _slider;
if (_info->scene() != _scene)
delete _info;
2016-10-17 23:14:07 +02:00
if (_grid->scene() != _scene)
delete _grid;
for (int i = 0; i < _graphs.count(); i++)
if (_graphs.at(i)->scene() != _scene)
delete _graphs[i];
}
void GraphView::createXLabel()
2015-10-12 01:12:12 +02:00
{
2018-03-29 00:29:08 +02:00
_xAxis->setLabel(QString("%1 [%2]").arg(_xLabel, _xUnits));
2015-10-12 01:12:12 +02:00
}
void GraphView::createYLabel()
2015-10-12 01:12:12 +02:00
{
2018-03-29 00:29:08 +02:00
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel, _yUnits));
2015-10-12 01:12:12 +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;
createYLabel();
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;
2015-10-12 01:12:12 +02:00
createYLabel();
}
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;
}
}
2015-10-12 01:12:12 +02:00
createXLabel();
}
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;
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();
2015-10-05 01:43:48 +02:00
2016-09-19 00:56:10 +02:00
for (int i = 0; i < _graphs.count(); i++) {
_graphs.at(i)->setGraphType(type);
2016-09-26 10:09:56 +02:00
if (_graphs.at(i)->scene() == _scene)
_bounds |= _graphs.at(i)->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
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);
}
void GraphView::addGraph(GraphItem *graph, int id)
2016-09-19 00:56:10 +02:00
{
QColor color(_palette.nextColor());
color.setAlpha(255);
graph->setUnits(_units);
graph->setId(id);
graph->setColor(color);
graph->setWidth(_width);
2015-10-05 01:43:48 +02:00
connect(this, SIGNAL(sliderPositionChanged(qreal)), graph,
2016-09-19 00:56:10 +02:00
SLOT(emitSliderPositionChanged(qreal)));
2016-08-19 19:48:44 +02:00
_graphs.append(graph);
2016-08-19 19:48:44 +02:00
if (!_hide.contains(id)) {
_visible.append(graph);
_scene->addItem(graph);
_bounds |= graph->bounds();
2016-09-19 00:56:10 +02:00
setXUnits();
2016-08-19 19:48:44 +02:00
}
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);
}
void GraphView::showGraph(bool show, int id)
{
if (show)
_hide.remove(id);
else
_hide.insert(id);
2016-08-19 19:48:44 +02:00
_visible.clear();
_bounds = QRectF();
for (int i = 0; i < _graphs.count(); i++) {
GraphItem* gi = _graphs.at(i);
2016-08-30 08:39:14 +02:00
if (_hide.contains(gi->id()))
removeItem(gi);
else {
addItem(gi);
2016-08-19 19:48:44 +02:00
_visible.append(gi);
_bounds |= gi->bounds();
2016-08-19 19:48:44 +02:00
}
}
}
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()
{
redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
}
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;
qreal sx, sy;
2015-10-05 01:43:48 +02:00
if (_visible.isEmpty() || _bounds.isNull()) {
2016-08-30 08:39:14 +02:00
removeItem(_xAxis);
removeItem(_yAxis);
removeItem(_slider);
removeItem(_info);
2016-10-17 23:14:07 +02:00
removeItem(_grid);
2016-08-30 08:39:14 +02:00
_scene->setSceneRect(QRectF());
return;
}
addItem(_xAxis);
addItem(_yAxis);
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);
2016-03-27 13:23:00 +02:00
if (ry.size() < _minYRange)
ry.resize(_minYRange);
_xAxis->setRange(rx);
_yAxis->setRange(ry);
2015-10-05 01:43:48 +02:00
mx = _xAxis->margin();
my = _yAxis->margin();
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);
sx = (size.width() - (my.width() + mx.width())) / r.width();
sy = (size.height() - (mx.height() + my.height())
2015-10-12 01:12:12 +02:00
- _info->boundingRect().height()) / r.height();
2015-10-05 01:43:48 +02:00
2016-08-19 19:48:44 +02:00
for (int i = 0; i < _visible.size(); i++)
_visible.at(i)->setScale(sx, sy);
2015-10-05 01:43:48 +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);
if (r.height() < _minYRange * sy)
r.adjust(0, -(_minYRange/2 * sy - r.height()/2), 0,
(_minYRange/2) * sy - r.height()/2);
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);
updateSliderPosition();
2015-12-19 20:49:22 +01:00
2016-08-19 19:48:44 +02:00
r |= _xAxis->sceneBoundingRect();
r |= _yAxis->sceneBoundingRect();
2015-10-12 01:12:12 +02:00
_info->setPos(r.topLeft() + QPointF(r.width()/2
- _info->boundingRect().width()/2, -_info->boundingRect().height()));
2015-10-05 01:43:48 +02:00
_scene->setSceneRect(_scene->itemsBoundingRect());
}
void GraphView::resizeEvent(QResizeEvent *)
2015-10-05 01:43:48 +02:00
{
2016-08-19 19:48:44 +02:00
redraw();
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);
}
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);
redraw(canvas);
if (_slider->pos().x() == _slider->area().left())
_slider->hide();
_scene->render(painter, target);
_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
}
void GraphView::clear()
2015-10-05 01:43:48 +02:00
{
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
for (int i = 0; i < _graphs.count(); i++)
delete _graphs[i];
2015-10-05 01:43:48 +02:00
_graphs.clear();
2016-08-19 19:48:44 +02:00
_visible.clear();
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;
2015-10-05 01:43:48 +02:00
_scene->setSceneRect(0, 0, 0, 0);
}
void GraphView::updateSliderPosition()
{
2016-09-19 00:56:10 +02:00
if (bounds().width() <= 0)
2016-08-19 19:48:44 +02:00
return;
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());
2016-08-19 19:48:44 +02:00
_slider->setVisible(!_visible.isEmpty());
} 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
}
2017-09-29 11:43:09 +02:00
if (_slider->isVisible())
updateSliderInfo();
}
2016-03-30 20:50:51 +02:00
void GraphView::updateSliderInfo()
2015-10-05 01:43:48 +02:00
{
2017-09-29 11:43:09 +02:00
qreal r, y;
2017-09-30 09:52:36 +02:00
if (_visible.count() > 1) {
2017-09-29 11:43:09 +02:00
r = 0;
2017-09-30 09:52:36 +02:00
y = 0;
} else {
2017-09-29 11:43:09 +02:00
QRectF br(_visible.first()->bounds());
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);
y = _visible.first()->yAtX(_sliderPos);
r = (y - br.bottom()) / br.height();
}
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()
> _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));
2017-09-29 11:43:09 +02:00
_sliderInfo->setText(_graphType == Time ? Format::timeSpan(_sliderPos,
bounds().width() > 3600) : QString::number(_sliderPos * _xScale, 'f', 1)
+ UNIT_SPACE + _xUnits, (_visible.count() > 1) ? QString()
: QString::number(-y * _yScale + _yOffset, 'f', _precision) + UNIT_SPACE
+ _yUnits);
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());
updateSliderPosition();
2016-08-19 19:48:44 +02:00
emit sliderPositionChanged(_sliderPos);
2015-10-05 01:43:48 +02:00
}
void GraphView::setSliderPosition(qreal pos)
2015-10-05 01:43:48 +02:00
{
2016-08-19 19:48:44 +02:00
if (_visible.isEmpty())
return;
_sliderPos = pos;
updateSliderPosition();
2015-10-05 01:43:48 +02:00
}
2015-10-12 01:12:12 +02:00
void GraphView::newSliderPosition(const QPointF &pos)
2015-10-13 23:49:15 +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
}
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
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();
for (int i = 0; i < _graphs.count(); i++) {
QColor color(_palette.nextColor());
color.setAlpha(255);
_graphs.at(i)->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
}
void GraphView::useOpenGL(bool use)
{
2017-02-12 17:38:20 +01:00
if (use)
2017-01-02 23:01:50 +01:00
setViewport(new OPENGL_WIDGET);
2017-02-12 17:38:20 +01:00
else
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);
}