diff --git a/scalepicker.cpp b/scalepicker.cpp new file mode 100644 --- /dev/null +++ b/scalepicker.cpp @@ -0,0 +1,250 @@ +/* + Copyright © 2015 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot 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. + + serialplot 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 serialplot. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +#include "scalepicker.h" + +// minimum size for pick (in pixels) +#define MIN_PICK_SIZE (2) + +class PlotOverlay : public QwtWidgetOverlay +{ +public: + PlotOverlay(QWidget* widget, ScalePicker* picker); + +protected: + virtual void drawOverlay(QPainter*) const; + +private: + ScalePicker* _picker; +}; + +PlotOverlay::PlotOverlay(QWidget* widget, ScalePicker* picker) : + QwtWidgetOverlay(widget) +{ + _picker = picker; +} + +void PlotOverlay::drawOverlay(QPainter* painter) const +{ + _picker->drawPlotOverlay(painter); +} + +class ScaleOverlay : public QwtWidgetOverlay +{ +public: + ScaleOverlay(QWidget* widget, ScalePicker* picker); + +protected: + virtual void drawOverlay(QPainter*) const; + +private: + ScalePicker* _picker; +}; + +ScaleOverlay::ScaleOverlay(QWidget* widget, ScalePicker* picker) : + QwtWidgetOverlay(widget) +{ + _picker = picker; +} + +void ScaleOverlay::drawOverlay(QPainter* painter) const +{ + _picker->drawScaleOverlay(painter); +} + +ScalePicker::ScalePicker(QwtScaleWidget* scaleWidget, QWidget* canvas) : + QObject(scaleWidget) +{ + _scaleWidget = scaleWidget; + _canvas = canvas; + scaleWidget->installEventFilter(this); + scaleWidget->setMouseTracking(true); + pickerOverlay = new PlotOverlay(canvas, this); + scaleOverlay = new ScaleOverlay(scaleWidget, this); + started = false; + pressed = false; +} + +bool ScalePicker::eventFilter(QObject* object, QEvent* event) +{ + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease || + event->type() == QEvent::MouseMove) + { + QMouseEvent* mouseEvent = (QMouseEvent*) event; + double pos = this->position(mouseEvent); + double posPx = this->positionPx(mouseEvent); + currentPosPx = posPx; + + if (event->type() == QEvent::MouseButtonPress && + mouseEvent->button() == Qt::LeftButton) + { + pressed = true; // not yet started + firstPos = pos; + firstPosPx = posPx; + } + else if (event->type() == QEvent::MouseMove) + { + // make sure pick size is big enough, so that just + // clicking won't trigger pick + if (!started && pressed && (fabs(posPx-firstPosPx) > MIN_PICK_SIZE)) + { + started = true; + emit pickStarted(pos); + } + else if (started) + { + pickerOverlay->updateOverlay(); + emit picking(firstPos, pos); + } + scaleOverlay->updateOverlay(); + } + else // event->type() == QEvent::MouseButtonRelease + { + if (started) + { + // finalize + started = false; + pressed = false; + emit picked(firstPos, pos); + } + } + return true; + } + else if (event->type() == QEvent::Leave) + { + scaleOverlay->updateOverlay(); + return true; + } + else + { + return QObject::eventFilter(object, event); + } +} + +void ScalePicker::drawPlotOverlay(QPainter* painter) +{ + if (started) + { + painter->save(); + painter->setPen(_pen); + + QRect rect; + if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale || + _scaleWidget->alignment() == QwtScaleDraw::TopScale) + { + int height = painter->device()->height(); + rect = QRect(posCanvasPx(firstPosPx), 0, currentPosPx-firstPosPx, height); + } + else // vertical + { + int width = painter->device()->width(); + rect = QRect(0, posCanvasPx(firstPosPx), width, currentPosPx-firstPosPx); + } + painter->drawRect(rect); + painter->restore(); + } +} + +void ScalePicker::drawScaleOverlay(QPainter* painter) +{ + painter->save(); + painter->setPen(_pen); + if (1) + { + + if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale || + _scaleWidget->alignment() == QwtScaleDraw::TopScale) + { + int height = painter->device()->height(); + if (started) painter->drawLine(firstPosPx, 0, firstPosPx, height); + if (started || _scaleWidget->underMouse()) + { + painter->drawLine(currentPosPx, 0, currentPosPx, height); + } + } + else // vertical + { + int width = painter->device()->width(); + if (started) painter->drawLine(0, firstPosPx, width, firstPosPx); + if (started || _scaleWidget->underMouse()) + { + painter->drawLine(0, currentPosPx, width, currentPosPx); + } + } + } + painter->restore(); +} + +void ScalePicker::setPen(QPen pen) +{ + _pen = pen; +} + +double ScalePicker::position(QMouseEvent* mouseEvent) +{ + double pos; + pos = positionPx(mouseEvent); + // convert the position of the click to the plot coordinates + pos = _scaleWidget->scaleDraw()->scaleMap().invTransform(pos); + return pos; +} + +double ScalePicker::positionPx(QMouseEvent* mouseEvent) +{ + double pos; + if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale || + _scaleWidget->alignment() == QwtScaleDraw::TopScale) + { + pos = mouseEvent->pos().x(); + } + else // left or right scale + { + pos = mouseEvent->pos().y(); + } + return pos; +} + +/* + * Scale widget and canvas widget is not always aligned. Especially + * when zooming scaleWidget moves around. This causes irregularities + * when drawing the tracker lines. This function maps scale widgets + * pixel coordinate to canvas' coordinate. + */ +double ScalePicker::posCanvasPx(double pos) +{ + // assumption: scale.width < canvas.width && scale.x > canvas.x + if (_scaleWidget->alignment() == QwtScaleDraw::BottomScale || + _scaleWidget->alignment() == QwtScaleDraw::TopScale) + { + return pos + (_scaleWidget->x() - _canvas->x()); + } + else // left or right scale + { + return pos + (_scaleWidget->y() - _canvas->y()); + } + return pos; +}