diff --git a/src/core.cpp b/src/core.cpp index 1cbd57dde3512b997f75c36f8cf4fce690f6b180..8842e6b9dff89d7aff6aed88d927328156b5f6aa 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -36,6 +36,11 @@ #include "plottable.h" #include "plottables/plottable-graph.h" #include "item.h" +#include "interaction.h" +#include "interactions/interaction-zoomrange.h" +#include "interactions/interaction-zoomrect.h" +#include "interactions/interaction-dragrange.h" +#include "interactions/interaction-select.h" //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////// QCustomPlot @@ -353,15 +358,13 @@ QCustomPlot::QCustomPlot(QWidget *parent) : mAutoAddPlottableToLegend(true), mAntialiasedElements(QCP::aeNone), mNotAntialiasedElements(QCP::aeNone), - mInteractions(0), + mInteractions(), mSelectionTolerance(8), - mNoAntialiasingOnDrag(false), mBackgroundBrush(Qt::white, Qt::SolidPattern), mBackgroundScaled(true), mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), mCurrentLayer(0), mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint), - mMultiSelectModifier(Qt::ControlModifier), mPaintBuffer(size()), mMouseEventElement(0), mReplotting(false) @@ -532,6 +535,64 @@ void QCustomPlot::setAutoAddPlottableToLegend(bool on) mAutoAddPlottableToLegend = on; } +QCP::Interactions QCustomPlot::interactions() const +{ + QCP::Interactions result = 0; + + if(interaction(QCP::iRangeDrag)) + result |= QCP::iRangeDrag; + if(interaction(QCP::iRangeZoom)) + result |= QCP::iRangeZoom; + if(interaction(QCP::iRectZoom)) + result |= QCP::iRectZoom; + if(QCPInteractionSelect *select = qobject_cast(interaction(QCP::iMultiSelect))) + result |= select->interactions(); + + return result; +} + +QCPAbstractInteraction *QCustomPlot::interaction(QCP::Interaction interaction) const +{ + switch(interaction) + { + case QCP::iRangeDrag: + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if(qobject_cast(interaction)) + return interaction; + } + break; + case QCP::iRangeZoom: + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if(qobject_cast(interaction)) + return interaction; + } + break; + case QCP::iRectZoom: + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if(qobject_cast(interaction)) + return interaction; + } + break; + case QCP::iMultiSelect: + case QCP::iSelectPlottables: + case QCP::iSelectAxes: + case QCP::iSelectLegend: + case QCP::iSelectItems: + case QCP::iSelectOther: + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if(qobject_cast(interaction)) + return interaction; + } + break; + } + + return 0; +} + /*! Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction enums. There are the following types of interactions: @@ -588,7 +649,64 @@ void QCustomPlot::setAutoAddPlottableToLegend(bool on) */ void QCustomPlot::setInteractions(const QCP::Interactions &interactions) { - mInteractions = interactions; + QCPAbstractInteraction *interactionZoomRange = interaction(QCP::iRangeZoom); + if(interactions.testFlag(QCP::iRangeZoom) && !interactionZoomRange) + { + QCPInteractionZoomRange *interaction = new QCPInteractionZoomRange(this); + interaction->addAxis(xAxis); + interaction->addAxis(yAxis); + mInteractions << interaction; + } + else if(!interactions.testFlag(QCP::iRangeZoom) && interactionZoomRange) + { + delete interactionZoomRange; + mInteractions.removeAll(interactionZoomRange); + } + + QCPAbstractInteraction *interactionRectZoom = interaction(QCP::iRectZoom); + if(interactions.testFlag(QCP::iRectZoom) && !interactionRectZoom) + { + QCPInteractionZoomRect *interaction = new QCPInteractionZoomRect(this); + interaction->addAxis(xAxis); + interaction->addAxis(yAxis); + mInteractions << interaction; + } + else if(!interactions.testFlag(QCP::iRectZoom) && interactionRectZoom) + { + delete interactionRectZoom; + mInteractions.removeAll(interactionRectZoom); + } + + QCPAbstractInteraction *interactionRangeDrag = interaction(QCP::iRangeDrag); + if(interactions.testFlag(QCP::iRangeDrag) && !interactionRangeDrag) + { + QCPInteractionDragRange *interaction = new QCPInteractionDragRange(this); + interaction->addAxis(xAxis); + interaction->addAxis(yAxis); + mInteractions << interaction; + } + else if(!interactions.testFlag(QCP::iRangeDrag) && interactionRangeDrag) + { + delete interactionRangeDrag; + mInteractions.removeAll(interactionRangeDrag); + } + + QCPInteractionSelect *interactionSelect = qobject_cast(interaction(QCP::iMultiSelect)); + QCP::Interactions interactionsSelectFlag = interactions & (QCP::iMultiSelect | QCP::iSelectPlottables | QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectItems | QCP::iSelectOther); + if(interactionsSelectFlag) + { + if(!interactionSelect) + { + interactionSelect = new QCPInteractionSelect(this); + mInteractions << interactionSelect; + } + interactionSelect->setInteractions(interactionsSelectFlag); + } + else if(interactionSelect) + { + delete interactionSelect; + mInteractions.removeAll(interactionSelect); + } } /*! @@ -600,10 +718,18 @@ void QCustomPlot::setInteractions(const QCP::Interactions &interactions) */ void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled) { - if (!enabled && mInteractions.testFlag(interaction)) - mInteractions &= ~interaction; - else if (enabled && !mInteractions.testFlag(interaction)) - mInteractions |= interaction; + QCP::Interactions currentInteractions = interactions(); + if(enabled) + currentInteractions |= interaction; + else + currentInteractions &= ~interaction; + + setInteractions(currentInteractions); +} + +void QCustomPlot::addInteraction(QCPAbstractInteraction *interaction) +{ + mInteractions << interaction; } /*! @@ -624,20 +750,6 @@ void QCustomPlot::setSelectionTolerance(int pixels) mSelectionTolerance = pixels; } -/*! - Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes - ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves - performance during dragging. Thus it creates a more responsive user experience. As soon as the - user stops dragging, the last replot is done with normal antialiasing, to restore high image - quality. - - \see setAntialiasedElements, setNotAntialiasedElements -*/ -void QCustomPlot::setNoAntialiasingOnDrag(bool enabled) -{ - mNoAntialiasingOnDrag = enabled; -} - /*! Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint. @@ -665,21 +777,6 @@ void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled) setPlottingHints(newHints); } -/*! - Sets the keyboard modifier that will be recognized as multi-select-modifier. - - If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects - by clicking on them one after the other while holding down \a modifier. - - By default the multi-select-modifier is set to Qt::ControlModifier. - - \see setInteractions -*/ -void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier) -{ - mMultiSelectModifier = modifier; -} - /*! Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect. @@ -2011,7 +2108,7 @@ void QCustomPlot::resizeEvent(QResizeEvent *event) void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) { emit mouseDoubleClick(event); - + QVariant details; QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); @@ -2039,6 +2136,12 @@ void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) mMouseEventElement->mouseReleaseEvent(event); mMouseEventElement = 0; } + + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if (interaction->active()) + interaction->mouseDoubleClickEvent(event); + } //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want. } @@ -2053,12 +2156,19 @@ void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event) void QCustomPlot::mousePressEvent(QMouseEvent *event) { emit mousePress(event); + mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release) // call event of affected layout element: mMouseEventElement = layoutElementAt(event->pos()); if (mMouseEventElement) mMouseEventElement->mousePressEvent(event); + + foreach (QCPAbstractInteraction *interaction, mInteractions) + { + if(interaction->active()) + interaction->mousePressEvent(event); + } QWidget::mousePressEvent(event); } @@ -2079,6 +2189,12 @@ void QCustomPlot::mouseMoveEvent(QMouseEvent *event) // call event of affected layout element: if (mMouseEventElement) mMouseEventElement->mouseMoveEvent(event); + + foreach (QCPAbstractInteraction *interaction, mInteractions) + { + if (interaction->active()) + interaction->mouseMoveEvent(event); + } QWidget::mouseMoveEvent(event); } @@ -2100,62 +2216,27 @@ void QCustomPlot::mouseMoveEvent(QMouseEvent *event) void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) { emit mouseRelease(event); - bool doReplot = false; if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation { if (event->button() == Qt::LeftButton) { - // handle selection mechanism: + // emit specialized object click signals: QVariant details; - QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details); - bool selectionStateChanged = false; - bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier); - // deselect all other layerables if not additive selection: - if (!additive) - { - foreach (QCPLayer *layer, mLayers) - { - foreach (QCPLayerable *layerable, layer->children()) - { - if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory())) - { - bool selChanged = false; - layerable->deselectEvent(&selChanged); - selectionStateChanged |= selChanged; - } - } - } - } - if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory())) - { - // a layerable was actually clicked, call its selectEvent: - bool selChanged = false; - clickedLayerable->selectEvent(event, additive, details, &selChanged); - selectionStateChanged |= selChanged; - } - if (selectionStateChanged) - { - doReplot = true; - emit selectionChangedByUser(); - } + QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false + if (QCPAbstractPlottable *ap = qobject_cast(clickedLayerable)) + emit plottableClick(ap, event); + else if (QCPAxis *ax = qobject_cast(clickedLayerable)) + emit axisClick(ax, details.value(), event); + else if (QCPAbstractItem *ai = qobject_cast(clickedLayerable)) + emit itemClick(ai, event); + else if (QCPLegend *lg = qobject_cast(clickedLayerable)) + emit legendClick(lg, 0, event); + else if (QCPAbstractLegendItem *li = qobject_cast(clickedLayerable)) + emit legendClick(li->parentLegend(), li, event); + else if (QCPPlotTitle *pt = qobject_cast(clickedLayerable)) + emit titleClick(event, pt); } - - // emit specialized object click signals: - QVariant details; - QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false - if (QCPAbstractPlottable *ap = qobject_cast(clickedLayerable)) - emit plottableClick(ap, event); - else if (QCPAxis *ax = qobject_cast(clickedLayerable)) - emit axisClick(ax, details.value(), event); - else if (QCPAbstractItem *ai = qobject_cast(clickedLayerable)) - emit itemClick(ai, event); - else if (QCPLegend *lg = qobject_cast(clickedLayerable)) - emit legendClick(lg, 0, event); - else if (QCPAbstractLegendItem *li = qobject_cast(clickedLayerable)) - emit legendClick(li->parentLegend(), li, event); - else if (QCPPlotTitle *pt = qobject_cast(clickedLayerable)) - emit titleClick(event, pt); } // call event of affected layout element: @@ -2165,8 +2246,11 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) mMouseEventElement = 0; } - if (doReplot || noAntialiasingOnDrag()) - replot(); + foreach (QCPAbstractInteraction *interaction, mInteractions) + { + if (interaction->active()) + interaction->mouseReleaseEvent(event); + } QWidget::mouseReleaseEvent(event); } @@ -2180,10 +2264,16 @@ void QCustomPlot::mouseReleaseEvent(QMouseEvent *event) void QCustomPlot::wheelEvent(QWheelEvent *event) { emit mouseWheel(event); - + // call event of affected layout element: if (QCPLayoutElement *el = layoutElementAt(event->pos())) el->wheelEvent(event); + + foreach(QCPAbstractInteraction *interaction, mInteractions) + { + if (interaction->active()) + interaction->mouseWheelEvent(event); + } QWidget::wheelEvent(event); } diff --git a/src/core.h b/src/core.h index 7b580fbb01dd62d2a10e2ef765ac65fbc8fe2882..f5420c3ad9f7bfe430b90d99d46866b4b3f44691 100644 --- a/src/core.h +++ b/src/core.h @@ -38,6 +38,7 @@ class QCPGraph; class QCPPlotTitle; class QCPLegend; class QCPAbstractLegendItem; +class QCPAbstractInteraction; class QCP_LIB_DECL QCustomPlot : public QWidget { @@ -50,8 +51,6 @@ class QCP_LIB_DECL QCustomPlot : public QWidget Q_PROPERTY(QCPLayoutGrid* plotLayout READ plotLayout) Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend) Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance) - Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag) - Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier) /// \endcond public: /*! @@ -86,11 +85,10 @@ public: QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; } QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; } bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; } - const QCP::Interactions interactions() const { return mInteractions; } + QCP::Interactions interactions() const; + QCPAbstractInteraction *interaction(QCP::Interaction) const; int selectionTolerance() const { return mSelectionTolerance; } - bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; } QCP::PlottingHints plottingHints() const { return mPlottingHints; } - Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; } // setters: void setViewport(const QRect &rect); @@ -106,11 +104,10 @@ public: void setAutoAddPlottableToLegend(bool on); void setInteractions(const QCP::Interactions &interactions); void setInteraction(const QCP::Interaction &interaction, bool enabled=true); + void addInteraction(QCPAbstractInteraction *interaction); void setSelectionTolerance(int pixels); - void setNoAntialiasingOnDrag(bool enabled); void setPlottingHints(const QCP::PlottingHints &hints); void setPlottingHint(QCP::PlottingHint hint, bool enabled=true); - void setMultiSelectModifier(Qt::KeyboardModifier modifier); // non-property methods: // plottable interface: @@ -163,6 +160,7 @@ public: QCPAxisRect* axisRect(int index=0) const; QList axisRects() const; QCPLayoutElement* layoutElementAt(const QPointF &pos) const; + QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const; Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false); QList selectedAxes() const; @@ -213,9 +211,8 @@ protected: QList mItems; QList mLayers; QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements; - QCP::Interactions mInteractions; + QList mInteractions; int mSelectionTolerance; - bool mNoAntialiasingOnDrag; QBrush mBackgroundBrush; QPixmap mBackgroundPixmap; QPixmap mScaledBackgroundPixmap; @@ -223,7 +220,6 @@ protected: Qt::AspectRatioMode mBackgroundScaledMode; QCPLayer *mCurrentLayer; QCP::PlottingHints mPlottingHints; - Qt::KeyboardModifier mMultiSelectModifier; // non-property members: QPixmap mPaintBuffer; @@ -249,7 +245,6 @@ protected: // non-virtual methods: void updateLayerIndices() const; - QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const; void drawBackground(QCPPainter *painter); friend class QCPLegend; diff --git a/src/global.h b/src/global.h index a34c4097030cdb57e16333bb2f86bb90449950a2..e2a1edb2a792f8397d1779902c361a33b3368344 100644 --- a/src/global.h +++ b/src/global.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) @@ -138,6 +139,7 @@ enum Interaction { iRangeDrag = 0x001 ///< 0x001 Axis ranges ar ,iSelectLegend = 0x020 ///< 0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ,iSelectItems = 0x040 ///< 0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem) ,iSelectOther = 0x080 ///< 0x080 All other objects are selectable (e.g. your own derived layerables, the plot title,...) + ,iRectZoom = 0x100 ///< 0x100 Axis ranges are zoomable by drawing a rectangle on a zone (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes) }; Q_DECLARE_FLAGS(Interactions, Interaction) diff --git a/src/interaction.cpp b/src/interaction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..482eedaf805f016ca3bb12c7798c31611ab48729 --- /dev/null +++ b/src/interaction.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 10.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction.h" + +#include "core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractInteraction +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractInteraction + \brief Base class for user interactions + + An interaction is basically the user pressing the mouse over the plot, moving it around, + and releasing the button. According to the implementation, the interaction may select a curve, + zoom in/out, translate the axis, ... + + Standard interactions can be activated/deactivated using \ref QCustomPlot::setInteractions. They + will use the most common buttons, and no keyboard modifiers, to suit usual needs. If you want to + customize an interaction, retrieve it using \ref QCustomPlot::interaction and call its specific + methods. + + Each interaction can be individually activated or deactived by calling \ref setActive. When an + interaction is inactive, it is ignored by the parent plot which does not call the event methods. + This is useful to activate/deactive interactions without destroying/recreating them. + + This class is an abstract placeholder for all interactions. To add you own custom interactions, + override it and call \ref QCustomPlot::addInteraction to make it fully active. + + When using many interactions at the same time, you should take care that they don't activate + together. For example, if you set a drag range and a zoom rect interaction both on the left mouse + button, this is going to do weird things, because each will catch the mouse events and to their + job. However, you can use a select and a zoom rect interaction on the left button, because + selection works on a single point click and zoom rect uses a mouse mouse : the select will + activate only if the cursor didn't move between click and release, and the zoom will activate only + if the cursor relevantly moved between click and release. To avoid bad cases, you can also use + keyboard modifiers. +*/ + +/*! + Creates a new QCPAbstractInteraction. This constructor is to be used by child classes only. +*/ +QCPAbstractInteraction::QCPAbstractInteraction(QCustomPlot *parentPlot) : + QObject(parentPlot), + mParentPlot(parentPlot), + mActive(true) +{ +} + +QCPAbstractInteraction::~QCPAbstractInteraction() +{ +} + +/*! + Converts the Qt::MouseButton retrieved on the QMouseEvent to a QCP MouseButton enumeration. + This is required to ease the configuration of interactions by including the wheel into the + enumeration, as if it were just two standard buttons. +*/ +QCPAbstractInteraction::MouseButton QCPAbstractInteraction::toMouseButton(Qt::MouseButton button) +{ + switch (button) + { + case Qt::LeftButton: + return mbLeft; + case Qt::RightButton: + return mbRight; + case Qt::MiddleButton: + return mbMiddle; + case Qt::XButton1: + return mbXButton1; + case Qt::XButton2: + return mbXButton2; + default: + return mbNone; + } +} diff --git a/src/interaction.h b/src/interaction.h new file mode 100644 index 0000000000000000000000000000000000000000..5c4e5cc7c45a55f24116be67bb7b91c3f4ef0757 --- /dev/null +++ b/src/interaction.h @@ -0,0 +1,84 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 07.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_INTERACTION_H +#define QCP_INTERACTION_H + +#include "global.h" +#include "layer.h" +#include "axis.h" + +class QCustomPlot; + +class QCP_LIB_DECL QCPAbstractInteraction : public QObject +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool active READ active WRITE setActive) + /// \endcond +public: + /*! + Defines the mouse buttons which may be pressed/released/triggered + */ + enum MouseButton { mbNone ///< No button + ,mbLeft ///< The mouse standard left button + ,mbRight ///< The mouse standard right button + ,mbMiddle ///< The mouse middle button, usually triggered by pressing the wheel + ,mbWheelUp ///< Pseudo-button, triggered when the wheel has been moved up + ,mbWheelDown ///< Pseudo-button, triggered when the wheel has been moved down + ,mbXButton1 ///< First optional mouse button, usually located on the side + ,mbXButton2 ///< Seconcd optional mouse button, usually located on the side + }; + + QCPAbstractInteraction(QCustomPlot *parentPlot); + virtual ~QCPAbstractInteraction(); + + // getters: + QCustomPlot *parentPlot() const { return mParentPlot; } + bool active() const { return mActive; } + + // setters: + void setActive(bool active) { mActive = active; } + + // introduced virtual method: + virtual void mouseDoubleClickEvent(QMouseEvent *event) { Q_UNUSED(event); } + virtual void mousePressEvent(QMouseEvent *event) { Q_UNUSED(event); } + virtual void mouseMoveEvent(QMouseEvent *event) { Q_UNUSED(event); } + virtual void mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); } + virtual void mouseWheelEvent(QWheelEvent *event) { Q_UNUSED(event); } + + // static methods: + MouseButton toMouseButton(Qt::MouseButton button); + +protected: + // property members: + QCustomPlot *mParentPlot; + bool mActive; + +private: + Q_DISABLE_COPY(QCPAbstractInteraction) +}; + +#endif // QCP_INTERACTION_H diff --git a/src/interactions/interaction-dragrange.cpp b/src/interactions/interaction-dragrange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d44827235c83fc8616f65fb45d51e9ce8e17942 --- /dev/null +++ b/src/interactions/interaction-dragrange.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction-dragrange.h" + +#include "../axis.h" +#include "../core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPInteractionDragRange +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPInteractionDragRange + \brief Interaction to drag the plot ranges with the mouse + + This interaction works very naturally : the user presses a mouse button over the plot, moves the + cursor around, which moves the axis ranges accordingly, and it stops when the mouse button + is released. You can also set keyboard modifiers to trigger the drag only when some buttons are + pressed. + + To fully activate the interaction, you need to set the dragged axis by calling \ref addAxis. The + standard case is to drag the first X and Y axis, but you can decide to drag only the X axis if you + have a fixed Y range, or drag the secondary X and Y axis at the same time. +*/ + +QCPInteractionDragRange::QCPInteractionDragRange(QCustomPlot *parentPlot) : + QCPAbstractInteractionOneButton(parentPlot), + mAxis(), + mNoAntialiasingOnDrag(false), + mMousePressPos(), + mRangesStart() +{ +} + +QCPInteractionDragRange::~QCPInteractionDragRange() +{ +} + +/*! + Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes + ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves + performance during dragging. Thus it creates a more responsive user experience. As soon as the + user stops dragging, the last replot is done with normal antialiasing, to restore high image + quality. +*/ +void QCPInteractionDragRange::setNoAntialiasingOnDrag(bool enabled) +{ + mNoAntialiasingOnDrag = enabled; +} + +void QCPInteractionDragRange::buttonActivated(QMouseEvent *event) +{ + if (!mAxis.isEmpty()) + { + mMousePressPos = event->pos(); + + // initialize antialiasing backup in case we start dragging: + if (mNoAntialiasingOnDrag) + { + mAADragBackup = mParentPlot->antialiasedElements(); + mNotAADragBackup = mParentPlot->notAntialiasedElements(); + } + + mRangesStart.clear(); + foreach (QPointer axis, mAxis) + { + if (!axis.isNull()) + { + mRangesStart << axis->range(); + } + } + } +} + +void QCPInteractionDragRange::mouseMoveEvent(QMouseEvent *event) +{ + bool ranged = false; + + if (!mMousePressPos.isNull()) + { + int i=0; + foreach (QPointer axis, mAxis) + { + if (!axis.isNull()) + { + QCPRange rangeStart = mRangesStart[i++]; + int dragStart, mousePos; + if (axis->axisType() == QCPAxis::atLeft || axis->axisType() == QCPAxis::atRight) + { + // vertical axis, take the y position of the mouse + dragStart = mMousePressPos.y(); + mousePos = event->pos().y(); + } + else + { + // horizontal axis, take the x position of the mouse + dragStart = mMousePressPos.x(); + mousePos = event->pos().x(); + } + + if (axis->scaleType() == QCPAxis::stLinear) + { + double diff = axis->pixelToCoord(dragStart) - axis->pixelToCoord(mousePos); + axis->setRange(rangeStart.lower + diff, rangeStart.upper + diff); + ranged = true; + } + else if (axis->scaleType() == QCPAxis::stLogarithmic) + { + double diff = axis->pixelToCoord(dragStart) / axis->pixelToCoord(mousePos); + axis->setRange(rangeStart.lower * diff, rangeStart.upper * diff); + ranged = true; + } + } + } + } + + if (ranged) + { + if (mNoAntialiasingOnDrag) + mParentPlot->setNotAntialiasedElements(QCP::aeAll); + mParentPlot->replot(); + } + + QCPAbstractInteractionOneButton::mouseMoveEvent(event); +} + +void QCPInteractionDragRange::buttonDeactivated(QMouseEvent *event) +{ + Q_UNUSED(event) + if (!mMousePressPos.isNull()) + { + mMousePressPos = QPoint(); + if (mNoAntialiasingOnDrag) + { + mParentPlot->setAntialiasedElements(mAADragBackup); + mParentPlot->setNotAntialiasedElements(mNotAADragBackup); + mParentPlot->replot(); + } + } +} diff --git a/src/interactions/interaction-dragrange.h b/src/interactions/interaction-dragrange.h new file mode 100644 index 0000000000000000000000000000000000000000..44bbf1902128a8cc4926b4d97058572252ae415e --- /dev/null +++ b/src/interactions/interaction-dragrange.h @@ -0,0 +1,72 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_INTERACTION_DRAGRANGE_H +#define QCP_INTERACTION_DRAGRANGE_H + +#include "interaction-onebutton.h" + +class QCPAxis; + +class QCP_LIB_DECL QCPInteractionDragRange : public QCPAbstractInteractionOneButton +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag) + /// \endcond +public: + QCPInteractionDragRange(QCustomPlot *parentPlot); + virtual ~QCPInteractionDragRange(); + + // getters: + bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; } + int axisCount() const { return mAxis.count(); } + QCPAxis *axis(int index) const { return mAxis[index]; } + + // setters: + void setNoAntialiasingOnDrag(bool enabled); + void clearAxis() { mAxis.clear(); } + void addAxis(QCPAxis *axis) { mAxis << axis; } + + // reimplemented virtual method: + virtual void buttonActivated(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void buttonDeactivated(QMouseEvent *event); + +protected: + // property members + QList > mAxis; + bool mNoAntialiasingOnDrag; + + // non-property members + QPoint mMousePressPos; + QList mRangesStart; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + +private: + Q_DISABLE_COPY(QCPInteractionDragRange) +}; + +#endif // QCP_INTERACTION_DRAGRANGE_H diff --git a/src/interactions/interaction-onebutton.cpp b/src/interactions/interaction-onebutton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..69fe0fd179cbe6256b36c3684659796ed5d9f8a4 --- /dev/null +++ b/src/interactions/interaction-onebutton.cpp @@ -0,0 +1,88 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 21.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction-onebutton.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPAbstractInteractionOneButton +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPAbstractInteractionOneButton + \brief Base class for interactions which are based on a single mouse button + + This class is provided for convenience, because many interactions work by using a single mouse + button, and keyboard modifiers. The button and the modifiers can be customized easily, so that the + final application suits the user at best. + + The interaction starts when the mouse button is pressed, and only if the pressed modifiers match + the set modifiers at this very moment. It lasts until the mouse button is released, even if the + keyboard modifiers are released in the meantime. This is the most natural way to do things. +*/ + +/* start documentation of inline functions */ + +/*! \fn virtual void buttonActivated(QMouseEvent *event) + + Virtual method called when the mouse button is pressed and the pressed keyboard modifiers match + the set modifiers. The real interaction job should start there. +*/ + +/*! \fn virtual void buttonDeactivated(QMouseEvent *event) + + Virtual method called when the mouse button is released and the interaction is currently active. + The real interaction job should stop here. +*/ + +/* end documentation of inline functions */ + +QCPAbstractInteractionOneButton::QCPAbstractInteractionOneButton(QCustomPlot *parentPlot) : + QCPAbstractInteraction(parentPlot), + mButton(mbLeft), + mModifiers(Qt::NoModifier), + mActive(false) +{ +} + +QCPAbstractInteractionOneButton::~QCPAbstractInteractionOneButton() +{ +} + +void QCPAbstractInteractionOneButton::mousePressEvent(QMouseEvent *event) +{ + if (toMouseButton(event->button()) == mButton && event->modifiers() == mModifiers) + { + mActive = true; + buttonActivated(event); + } +} + +void QCPAbstractInteractionOneButton::mouseReleaseEvent(QMouseEvent *event) +{ + if (mActive && toMouseButton(event->button()) == mButton) + { + mActive = false; + buttonDeactivated(event); + } +} diff --git a/src/interactions/interaction-onebutton.h b/src/interactions/interaction-onebutton.h new file mode 100644 index 0000000000000000000000000000000000000000..9314ae9d0e8662e7b878a281fca38dcccade7f5e --- /dev/null +++ b/src/interactions/interaction-onebutton.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 21.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_ABSTRACTINTERACTION_ONEBUTTON +#define QCP_ABSTRACTINTERACTION_ONEBUTTON + +#include "../interaction.h" + + +class QCP_LIB_DECL QCPAbstractInteractionOneButton : public QCPAbstractInteraction +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPAbstractInteraction::MouseButton button READ button WRITE setButton) + Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers WRITE setModifiers) + /// \endcond +public: + QCPAbstractInteractionOneButton(QCustomPlot *parentPlot); + virtual ~QCPAbstractInteractionOneButton(); + + // getters: + MouseButton button() const { return mButton; } + Qt::KeyboardModifiers modifiers() { return mModifiers; } + + // setters: + void setButton(MouseButton button) { mButton = button; } + void setModifiers(Qt::KeyboardModifiers modifiers) { mModifiers = modifiers; } + +protected: + // property members: + MouseButton mButton; + Qt::KeyboardModifiers mModifiers; + + // non-property members + bool mActive; + + // reimplemented virtual methods: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + + // introduced virtual methods: + virtual void buttonActivated(QMouseEvent *event) = 0; + virtual void buttonDeactivated(QMouseEvent *event) = 0; + + // non-virtual methods: + void updateActive(QMouseEvent *event); + +private: + Q_DISABLE_COPY(QCPAbstractInteractionOneButton) +}; + +#endif // QCP_ABSTRACTINTERACTION_ONEBUTTON diff --git a/src/interactions/interaction-select.cpp b/src/interactions/interaction-select.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1cff6426fe1de2bbadcbf87052987123be596e4 --- /dev/null +++ b/src/interactions/interaction-select.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction-select.h" + +#include "../axis.h" +#include "../core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPInteractionSelect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPInteractionSelect + \brief Interaction which lets the user select one or more elements of the plot + + This interaction is quite simple as it only manages the user mouse click and calls the + \ref selectEvent or \ref deselectEvent of the appropriate elements. When the selection changes in + any way, the \ref selectionChanged signal is emitted. + + The \ref setInteractions methods lets you customise which elements of the plot are to be selected, + and whether many elements may be selected at the same time. +*/ + +/* start documentation of inline functions */ + +/*! \fn void setInteractions(QCP::Interactions interactions) + + This methods allows you to configure which elements of the plot may be selected through this + interaction. You can combine whichever elements you want using the Interactions flags. Of course, + only the iSelect* values of the enumeration are considered. + + You can also set the iMultiSelect flag to indicate that the user may select many elements + simultaneously. +*/ + +/* end documentation of inline functions */ + +QCPInteractionSelect::QCPInteractionSelect(QCustomPlot *parentPlot) : + QCPAbstractInteractionOneButton(parentPlot), + mMultiSelectModifier(Qt::ControlModifier), + mInteractions(0), + mMousePressPos() +{ +} + +QCPInteractionSelect::~QCPInteractionSelect() +{ +} + +/*! + Sets the keyboard modifier that will be recognized as multi-select-modifier. + + If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects + by clicking on them one after the other while holding down \a modifier. + + By default the multi-select-modifier is set to Qt::ControlModifier. +*/ +void QCPInteractionSelect::setMultiSelectModifier(Qt::KeyboardModifier modifier) +{ + mMultiSelectModifier = modifier; +} + +void QCPInteractionSelect::buttonActivated(QMouseEvent *event) +{ + mMousePressPos = event->pos(); +} + +void QCPInteractionSelect::buttonDeactivated(QMouseEvent *event) +{ + bool selectionStateChanged = false; + + if ((mMousePressPos - event->pos()).manhattanLength() < 5) // determine whether it was a click operation + { + QVariant details; + QCPLayerable *clickedLayerable = mParentPlot->layerableAt(event->pos(), true, &details); + bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier); + // deselect all other layerables if not additive selection: + if (!additive) + { + for (int i=0 ; ilayerCount() ; i++) + { + QCPLayer *layer = mParentPlot->layer(i); + + foreach (QCPLayerable *layerable, layer->children()) + { + if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory())) + { + bool selChanged = false; + layerable->deselectEvent(&selChanged); + selectionStateChanged |= selChanged; + } + } + } + } + if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory())) + { + // a layerable was actually clicked, call its selectEvent: + bool selChanged = false; + clickedLayerable->selectEvent(event, additive, details, &selChanged); + selectionStateChanged |= selChanged; + } + } + + if (selectionStateChanged) + { + emit selectionChanged(); + mParentPlot->replot(); + } +} diff --git a/src/interactions/interaction-select.h b/src/interactions/interaction-select.h new file mode 100644 index 0000000000000000000000000000000000000000..f66071853087457275d07c7fd4f14b5125231ddb --- /dev/null +++ b/src/interactions/interaction-select.h @@ -0,0 +1,69 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_INTERACTION_SELECT_H +#define QCP_INTERACTION_SELECT_H + +#include "interaction-onebutton.h" + + +class QCP_LIB_DECL QCPInteractionSelect : public QCPAbstractInteractionOneButton +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier) + /// \endcond +public: + QCPInteractionSelect(QCustomPlot *parentPlot); + virtual ~QCPInteractionSelect(); + + // getters: + Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; } + QCP::Interactions interactions() const { return mInteractions; } + + // setters: + void setMultiSelectModifier(Qt::KeyboardModifier modifier); + void setInteractions(QCP::Interactions interactions) { mInteractions = interactions; } + + // reimplemented virtual method: + virtual void buttonActivated(QMouseEvent *event); + virtual void buttonDeactivated(QMouseEvent *event); + +signals: + void selectionChanged(); + +protected: + // property members + Qt::KeyboardModifier mMultiSelectModifier; + QCP::Interactions mInteractions; + + // non-property members + QPoint mMousePressPos; + +private: + Q_DISABLE_COPY(QCPInteractionSelect) +}; + +#endif // QCP_INTERACTION_SELECT_H diff --git a/src/interactions/interaction-zoomrange.cpp b/src/interactions/interaction-zoomrange.cpp new file mode 100644 index 0000000000000000000000000000000000000000..933942a1a6299335ccf21afe6603050f8ccee932 --- /dev/null +++ b/src/interactions/interaction-zoomrange.cpp @@ -0,0 +1,186 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 10.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction-zoomrange.h" + +#include "../axis.h" +#include "../core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPInteractionZoomRange +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPInteractionZoomRange + \brief Interaction for mouse zoom in/out using the wheel or buttons. + + The axis to be zoomed in/out are to be set using the \ref addAxis method. If no axis is set, the + interaction will do nothing. If many axis are set, they will all be zoomed when the interacton is + triggered. The center of the scaling operation is the current cursor position inside the axis + rect. The strength of the zoom can be controlled for each axis when adding them. However, using + different factors for each axis may be disturbing unless you have a very specific application. +*/ + +/* start documentation of inline functions */ + +/*! \fn void addAxis(QCPAxis *axis, double factor) + + Adds an axis to be zoomed by the user with the mouse. Once the axis has been added, its range will + be scaled each time the interaction is triggered. By default, the interactions contains no axis, + making it useless. + + You can add as many axis as wanted, they will all be treated independantly. + + The \a factor indicates how strong the zoom is. The default value makes a natural zoom, but you can + increase it if your application requires that you move very quickly from a large view to a small + view. Or you can decrease it if you need to set a very specific zoom level. The factor should be + lesser than 1, or the zoom will be inverted. +*/ + +/* end documentation of inline functions */ + +QCPInteractionZoomRange::QCPInteractionZoomRange(QCustomPlot *parentPlot) : + QCPAbstractInteraction(parentPlot), + mButtonZoomIn(mbWheelUp), + mButtonZoomOut(mbWheelDown), + mAxis(), + mMousePressPos() +{ +} + +QCPInteractionZoomRange::~QCPInteractionZoomRange() +{ +} + +/*! + Function provided for convience : it sets the zoom factor for each registered axis, which is the + most common case. + + The \a factor indicates how strong the zoom is. The default value makes a natural zoom, but you can + increase it if your application requires that you move very quickly from a large view to a small + view. Or you can decrease it if you need to set a very specific zoom level. The factor should be + lesser than 1, or the zoom will be inverted. +*/ +void QCPInteractionZoomRange::setFactor(double factor) +{ + QMutableListIterator, double> > iterator(mAxis); + while (iterator.hasNext()) + { + iterator.next().second = factor; + } +} + +void QCPInteractionZoomRange::mousePressEvent(QMouseEvent *event) +{ + MouseButton button = toMouseButton(event->button()); + if (button == mButtonZoomIn || button == mButtonZoomOut) + { + mMousePressPos = event->pos(); + } +} + +void QCPInteractionZoomRange::mouseReleaseEvent(QMouseEvent *event) +{ + MouseButton button = toMouseButton(event->button()); + if (button == mButtonZoomIn || button == mButtonZoomOut) + { + if ((mMousePressPos - event->pos()).manhattanLength() < 5) // determine whether it was a click operation + { + doZoom(event->pos(), button == mButtonZoomIn ? 1 : -1); + } + } +} + +/*! \internal + \brief Method called when the mouse wheel has been rotated up or down. + + Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse + wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be + multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as + exponent of the range zoom factor. This takes care of the wheel direction automatically, by + inverting the factor, when the wheel step is negative (f^-1 = 1/f). + */ +void QCPInteractionZoomRange::mouseWheelEvent(QWheelEvent *event) +{ + double wheelDelta = event->delta() / 120.0; // a single step delta is +/-120 usually + double wise = 0; + + if (wheelDelta > 0) + { + if (mButtonZoomIn == mbWheelUp) + wise = 1; + else if (mButtonZoomOut == mbWheelUp) + wise = -1; + } + else + { + if (mButtonZoomIn == mbWheelDown) + wise = -1; + else if (mButtonZoomOut == mbWheelDown) + wise = 1; + } + + if (!qFuzzyIsNull(wise)) + doZoom(event->pos(), wheelDelta * wise); +} + +/*! \internal + \brief Actually compute the range zoom + + This method computes and applies the new ranges for each registered axis, according to the given + zoom parameters. + + The \a pos is the current mouse position, used to zoom on a specific zone on the graph. + The \a steps indicates how strong the zoom should be made. It may be negative to indicate + a zoom out. + */ +void QCPInteractionZoomRange::doZoom(const QPoint &pos, double steps) +{ + bool scaled = false; + for (int i=0 ; i axis = mAxis[i].first; + if (!axis.isNull()) + { + int mousePos; + + if (axis->axisType() == QCPAxis::atLeft || axis->axisType() == QCPAxis::atRight) + { + // vertical axis, take the y position of the mouse + mousePos = pos.y(); + } + else + { + // horizontal axis, take the x position of the mouse + mousePos = pos.x(); + } + + axis->scaleRange(pow(mAxis[i].second, steps), axis->pixelToCoord(mousePos)); + scaled = true; + } + } + + if (scaled) + parentPlot()->replot(); +} diff --git a/src/interactions/interaction-zoomrange.h b/src/interactions/interaction-zoomrange.h new file mode 100644 index 0000000000000000000000000000000000000000..a13845069b5ba19150855bddf15baa77ad2ec6a2 --- /dev/null +++ b/src/interactions/interaction-zoomrange.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 10.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_INTERACTION_ZOOMRANGE_H +#define QCP_INTERACTION_ZOOMRANGE_H + +#include "../interaction.h" + +class QCPAxis; + + +class QCP_LIB_DECL QCPInteractionZoomRange : public QCPAbstractInteraction +{ + Q_OBJECT + /// \cond INCLUDE_QPROPERTIES + Q_PROPERTY(QCPAbstractInteraction::MouseButton buttonZoomIn READ buttonZoomIn WRITE setButtonZoomIn) + Q_PROPERTY(QCPAbstractInteraction::MouseButton buttonZoomOut READ buttonZoomOut WRITE setButtonZoomOut) + /// \endcond +public: + QCPInteractionZoomRange(QCustomPlot *parentPlot); + virtual ~QCPInteractionZoomRange(); + + // getters: + MouseButton buttonZoomIn() const { return mButtonZoomIn; } + MouseButton buttonZoomOut() const { return mButtonZoomOut; } + int axisCount() const { return mAxis.count(); } + QCPAxis *axis(int index) const { return mAxis[index].first; } + double factor(int index) const { return mAxis[index].second; } + + // setters: + void setButtonZoomIn(MouseButton button) { mButtonZoomIn = button; } + void setButtonZoomOut(MouseButton button) { mButtonZoomOut = button; } + void setFactor(double factor); + void clearAxis() { mAxis.clear(); } + void addAxis(QCPAxis *axis, double factor=0.85) { mAxis << qMakePair(QPointer(axis), factor); } + + // reimplemented virtual method: + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseWheelEvent(QWheelEvent *event); + +protected: + // property members + MouseButton mButtonZoomIn; + MouseButton mButtonZoomOut; + QList, double> > mAxis; + + // non-property members + QPoint mMousePressPos; + QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; + + // non-virtual methods: + void doZoom(const QPoint &pos, double steps); + +private: + Q_DISABLE_COPY(QCPInteractionZoomRange) +}; + +#endif // QCP_INTERACTION_ZOOMRANGE_H diff --git a/src/interactions/interaction-zoomrect.cpp b/src/interactions/interaction-zoomrect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c8072ae6adac647e8d836d77fa73950cb6700de3 --- /dev/null +++ b/src/interactions/interaction-zoomrect.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#include "interaction-zoomrect.h" + +#include "../axis.h" +#include "../core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// QCPInteractionZoomRect +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/*! \class QCPInteractionZoomRect + \brief Interaction for zooming on a specific area by drawing a rectangle around it. + + This interaction works in 3 steps : the user cliks on the plot, then moves the mouse around which + draws a selection rectangle (a QRubberBand), then the mouse button is released. At this time, + the drawn rectangle is considered to be the new axis ranges. This allows to move quickly from a + graph containing many data to an area containing interesting information. It also offers the + ability to zoom separately on the X and Y axis, so that the displayed data may be more relevant. + + To make this interaction fully working, you need to call the \ref addAxis method as many times as + required to insert all the axis you want to be zoomed. + + This interaction only allows for zoom in. If you need to zoom out, use it in parallel with an + other interaction like zoom range, or add an external button to set the axes ranges manually. +*/ + +/* start documentation of inline functions */ + + +/* end documentation of inline functions */ + +QCPInteractionZoomRect::QCPInteractionZoomRect(QCustomPlot *parentPlot) : + QCPAbstractInteractionOneButton(parentPlot), + mAxis(), + mMousePressPos(), + mRubberBand(new QRubberBand(QRubberBand::Rectangle, parentPlot)) +{ + mRubberBand->hide(); +} + +QCPInteractionZoomRect::~QCPInteractionZoomRect() +{ +} + +void QCPInteractionZoomRect::buttonActivated(QMouseEvent *event) +{ + if (!mAxis.isEmpty()) + { + mMousePressPos = event->pos(); + mRubberBand->setGeometry(QRect(mMousePressPos, QSize(0, 0))); + mRubberBand->show(); + } +} + +void QCPInteractionZoomRect::mouseMoveEvent(QMouseEvent *event) +{ + if (mRubberBand->isVisible()) + mRubberBand->setGeometry(QRect(mMousePressPos, event->pos()).normalized()); + + QCPAbstractInteractionOneButton::mouseMoveEvent(event); +} + +void QCPInteractionZoomRect::buttonDeactivated(QMouseEvent *event) +{ + Q_UNUSED(event) + bool ranged = false; + if (mRubberBand->isVisible()) + { + mRubberBand->hide(); + QRect rect = mRubberBand->geometry(); + if (rect.width() > 10 && rect.height() > 10) // protection against involoutary moves + { + foreach (QPointer axis, mAxis) + { + if (!axis.isNull()) + { + double lower; + double upper; + + if (axis->axisType() == QCPAxis::atLeft || axis->axisType() == QCPAxis::atRight) + { + // vertical axis, take the rect height + lower = axis->pixelToCoord(rect.bottom()); + upper = axis->pixelToCoord(rect.top()); + } + else + { + // horizontal axis, take the rect width + lower = axis->pixelToCoord(rect.left()); + upper = axis->pixelToCoord(rect.right()); + } + + axis->setRange(lower, upper); + ranged = true; + } + } + } + } + + if (ranged) + mParentPlot->replot(); +} diff --git a/src/interactions/interaction-zoomrect.h b/src/interactions/interaction-zoomrect.h new file mode 100644 index 0000000000000000000000000000000000000000..0860900477bcccb4afb8ef7373fef7e661ecf784 --- /dev/null +++ b/src/interactions/interaction-zoomrect.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** ** +** QCustomPlot, an easy to use, modern plotting widget for Qt ** +** Copyright (C) 2011, 2012, 2013, 2014 Emanuel Eichhammer ** +** ** +** This program 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. ** +** ** +** This program 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 this program. If not, see http://www.gnu.org/licenses/. ** +** ** +**************************************************************************** +** Author: Erwan Mathieu ** +** Website/Contact: http://www.qcustomplot.com/ ** +** Date: 12.03.14 ** +** Version: 1.2.0-beta ** +****************************************************************************/ + +#ifndef QCP_INTERACTION_ZOOMRECT_H +#define QCP_INTERACTION_ZOOMRECT_H + +#include "interaction-onebutton.h" + +class QRubberBand; +class QCPAxis; + + +class QCP_LIB_DECL QCPInteractionZoomRect : public QCPAbstractInteractionOneButton +{ + Q_OBJECT +public: + QCPInteractionZoomRect(QCustomPlot *parentPlot); + virtual ~QCPInteractionZoomRect(); + + // getters: + int axisCount() const { return mAxis.count(); } + QCPAxis *axis(int index) const { return mAxis[index]; } + + // setters: + void clearAxis() { mAxis.clear(); } + void addAxis(QCPAxis *axis) { mAxis << axis; } + + // reimplemented virtual method: + virtual void buttonActivated(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void buttonDeactivated(QMouseEvent *event); + +protected: + // property members + QList > mAxis; + + // non-property members + QPoint mMousePressPos; + QRubberBand *mRubberBand; + +private: + Q_DISABLE_COPY(QCPInteractionZoomRect) +}; + +#endif // QCP_INTERACTION_ZOOMRECT_H diff --git a/src/layer.h b/src/layer.h index b80db41b1610775b54ce9b1d4ac3e7ac6ff937bd..d17209a803a28f03d69e9f5489a16502b5d949b1 100644 --- a/src/layer.h +++ b/src/layer.h @@ -106,6 +106,10 @@ public: // introduced virtual methods: virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; + virtual QCP::Interaction selectionCategory() const; + // events: + virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); + virtual void deselectEvent(bool *selectionStateChanged); // non-property methods: bool realVisibility() const; @@ -123,13 +127,9 @@ protected: // introduced virtual methods: virtual void parentPlotInitialized(QCustomPlot *parentPlot); - virtual QCP::Interaction selectionCategory() const; virtual QRect clipRect() const; virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0; virtual void draw(QCPPainter *painter) = 0; - // events: - virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged); - virtual void deselectEvent(bool *selectionStateChanged); // non-property methods: void initializeParentPlot(QCustomPlot *parentPlot); diff --git a/src/layoutelements/layoutelement-axisrect.cpp b/src/layoutelements/layoutelement-axisrect.cpp index f524e425d82892888dd09000e9a654740410df6a..019b53dff6c9fc7c7bb96197e8a29c8a6ec38e80 100644 --- a/src/layoutelements/layoutelement-axisrect.cpp +++ b/src/layoutelements/layoutelement-axisrect.cpp @@ -167,12 +167,7 @@ QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) : mBackgroundBrush(Qt::NoBrush), mBackgroundScaled(true), mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding), - mInsetLayout(new QCPLayoutInset), - mRangeDrag(Qt::Horizontal|Qt::Vertical), - mRangeZoom(Qt::Horizontal|Qt::Vertical), - mRangeZoomFactorHorz(0.85), - mRangeZoomFactorVert(0.85), - mDragging(false) + mInsetLayout(new QCPLayoutInset) { mInsetLayout->initializeParentPlot(mParentPlot); mInsetLayout->setParentLayerable(this); @@ -191,8 +186,6 @@ QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) : QCPAxis *yAxis = addAxis(QCPAxis::atLeft); QCPAxis *xAxis2 = addAxis(QCPAxis::atTop); QCPAxis *yAxis2 = addAxis(QCPAxis::atRight); - setRangeDragAxes(xAxis, yAxis); - setRangeZoomAxes(xAxis, yAxis); xAxis2->setVisible(false); yAxis2->setVisible(false); xAxis->grid()->setVisible(true); @@ -693,128 +686,6 @@ void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode) mBackgroundScaledMode = mode; } -/*! - Returns the range drag axis of the \a orientation provided. - - \see setRangeDragAxes -*/ -QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation) -{ - return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data()); -} - -/*! - Returns the range zoom axis of the \a orientation provided. - - \see setRangeZoomAxes -*/ -QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation) -{ - return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data()); -} - -/*! - Returns the range zoom factor of the \a orientation provided. - - \see setRangeZoomFactor -*/ -double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation) -{ - return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert); -} - -/*! - Sets which axis orientation may be range dragged by the user with mouse interaction. - What orientation corresponds to which specific axis can be set with - \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By - default, the horizontal axis is the bottom axis (xAxis) and the vertical axis - is the left axis (yAxis). - - To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref - QCustomPlot::setInteractions. To enable range dragging for both directions, pass Qt::Horizontal | - Qt::Vertical as \a orientations. - - In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions - contains \ref QCP::iRangeDrag to enable the range dragging interaction. - - \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag -*/ -void QCPAxisRect::setRangeDrag(Qt::Orientations orientations) -{ - mRangeDrag = orientations; -} - -/*! - Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation - corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal, - QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical - axis is the left axis (yAxis). - - To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref - QCustomPlot::setInteractions. To enable range zooming for both directions, pass Qt::Horizontal | - Qt::Vertical as \a orientations. - - In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions - contains \ref QCP::iRangeZoom to enable the range zooming interaction. - - \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag -*/ -void QCPAxisRect::setRangeZoom(Qt::Orientations orientations) -{ - mRangeZoom = orientations; -} - -/*! - Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging - on the QCustomPlot widget. - - \see setRangeZoomAxes -*/ -void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical) -{ - mRangeDragHorzAxis = horizontal; - mRangeDragVertAxis = vertical; -} - -/*! - Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the - QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors - are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor). - - \see setRangeDragAxes -*/ -void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical) -{ - mRangeZoomHorzAxis = horizontal; - mRangeZoomVertAxis = vertical; -} - -/*! - Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with - \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to - let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal - and which is vertical, can be set with \ref setRangeZoomAxes. - - When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user) - will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the - same scrolling direction will zoom out. -*/ -void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor) -{ - mRangeZoomFactorHorz = horizontalFactor; - mRangeZoomFactorVert = verticalFactor; -} - -/*! \overload - - Sets both the horizontal and vertical zoom \a factor. -*/ -void QCPAxisRect::setRangeZoomFactor(double factor) -{ - mRangeZoomFactorHorz = factor; - mRangeZoomFactorVert = factor; -} - /*! \internal Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a @@ -902,150 +773,3 @@ int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side) else return 0; } - -/*! \internal - - Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is - pressed, the range dragging interaction is initialized (the actual range manipulation happens in - the \ref mouseMoveEvent). - - The mDragging flag is set to true and some anchor points are set that are needed to determine the - distance the mouse was dragged in the mouse move/release events later. - - \see mouseMoveEvent, mouseReleaseEvent -*/ -void QCPAxisRect::mousePressEvent(QMouseEvent *event) -{ - mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release) - if (event->buttons() & Qt::LeftButton) - { - mDragging = true; - // initialize antialiasing backup in case we start dragging: - if (mParentPlot->noAntialiasingOnDrag()) - { - mAADragBackup = mParentPlot->antialiasedElements(); - mNotAADragBackup = mParentPlot->notAntialiasedElements(); - } - // Mouse range dragging interaction: - if (mParentPlot->interactions().testFlag(QCP::iRangeDrag)) - { - if (mRangeDragHorzAxis) - mDragStartHorzRange = mRangeDragHorzAxis.data()->range(); - if (mRangeDragVertAxis) - mDragStartVertRange = mRangeDragVertAxis.data()->range(); - } - } -} - -/*! \internal - - Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a - preceding \ref mousePressEvent, the range is moved accordingly. - - \see mousePressEvent, mouseReleaseEvent -*/ -void QCPAxisRect::mouseMoveEvent(QMouseEvent *event) -{ - // Mouse range dragging interaction: - if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag)) - { - if (mRangeDrag.testFlag(Qt::Horizontal)) - { - if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data()) - { - if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear) - { - double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x()); - rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff); - } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic) - { - double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x()); - rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff); - } - } - } - if (mRangeDrag.testFlag(Qt::Vertical)) - { - if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data()) - { - if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear) - { - double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y()); - rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff); - } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic) - { - double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y()); - rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff); - } - } - } - if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot - { - if (mParentPlot->noAntialiasingOnDrag()) - mParentPlot->setNotAntialiasedElements(QCP::aeAll); - mParentPlot->replot(); - } - } -} - -/* inherits documentation from base class */ -void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event) -{ - Q_UNUSED(event) - mDragging = false; - if (mParentPlot->noAntialiasingOnDrag()) - { - mParentPlot->setAntialiasedElements(mAADragBackup); - mParentPlot->setNotAntialiasedElements(mNotAADragBackup); - } -} - -/*! \internal - - Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the - ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of - the scaling operation is the current cursor position inside the axis rect. The scaling factor is - dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural - zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor. - - Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse - wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be - multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as - exponent of the range zoom factor. This takes care of the wheel direction automatically, by - inverting the factor, when the wheel step is negative (f^-1 = 1/f). -*/ -void QCPAxisRect::wheelEvent(QWheelEvent *event) -{ - // Mouse range zooming interaction: - if (mParentPlot->interactions().testFlag(QCP::iRangeZoom)) - { - if (mRangeZoom != 0) - { - double factor; - double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually - if (mRangeZoom.testFlag(Qt::Horizontal)) - { - factor = qPow(mRangeZoomFactorHorz, wheelSteps); - if (mRangeZoomHorzAxis.data()) - mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x())); - } - if (mRangeZoom.testFlag(Qt::Vertical)) - { - factor = qPow(mRangeZoomFactorVert, wheelSteps); - if (mRangeZoomVertAxis.data()) - mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y())); - } - mParentPlot->replot(); - } - } -} - - - - - - - - - - diff --git a/src/layoutelements/layoutelement-axisrect.h b/src/layoutelements/layoutelement-axisrect.h index 7e980efa8ceabea2611cedd8dd4fc7f53b4d30f3..c0f44e192ec7de489cc26181e7cf64c7c45b2110 100644 --- a/src/layoutelements/layoutelement-axisrect.h +++ b/src/layoutelements/layoutelement-axisrect.h @@ -44,8 +44,6 @@ class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement Q_PROPERTY(QPixmap background READ background WRITE setBackground) Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled) Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode) - Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag) - Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom) /// \endcond public: explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true); @@ -55,11 +53,6 @@ public: QPixmap background() const { return mBackgroundPixmap; } bool backgroundScaled() const { return mBackgroundScaled; } Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; } - Qt::Orientations rangeDrag() const { return mRangeDrag; } - Qt::Orientations rangeZoom() const { return mRangeZoom; } - QCPAxis *rangeDragAxis(Qt::Orientation orientation); - QCPAxis *rangeZoomAxis(Qt::Orientation orientation); - double rangeZoomFactor(Qt::Orientation orientation); // setters: void setBackground(const QPixmap &pm); @@ -67,12 +60,6 @@ public: void setBackground(const QBrush &brush); void setBackgroundScaled(bool scaled); void setBackgroundScaledMode(Qt::AspectRatioMode mode); - void setRangeDrag(Qt::Orientations orientations); - void setRangeZoom(Qt::Orientations orientations); - void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical); - void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical); - void setRangeZoomFactor(double horizontalFactor, double verticalFactor); - void setRangeZoomFactor(double factor); // non-property methods: int axisCount(QCPAxis::AxisType type) const; @@ -115,25 +102,13 @@ protected: bool mBackgroundScaled; Qt::AspectRatioMode mBackgroundScaledMode; QCPLayoutInset *mInsetLayout; - Qt::Orientations mRangeDrag, mRangeZoom; - QPointer mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis; - double mRangeZoomFactorHorz, mRangeZoomFactorVert; // non-property members: - QCPRange mDragStartHorzRange, mDragStartVertRange; - QCP::AntialiasedElements mAADragBackup, mNotAADragBackup; - QPoint mDragStart; - bool mDragging; QHash > mAxes; // reimplemented virtual methods: virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const; virtual void draw(QCPPainter *painter); virtual int calculateAutoMargin(QCP::MarginSide side); - // events: - virtual void mousePressEvent(QMouseEvent *event); - virtual void mouseMoveEvent(QMouseEvent *event); - virtual void mouseReleaseEvent(QMouseEvent *event); - virtual void wheelEvent(QWheelEvent *event); // non-property methods: void drawBackground(QCPPainter *painter); diff --git a/src/layoutelements/layoutelement-colorscale.cpp b/src/layoutelements/layoutelement-colorscale.cpp index ac4186af10462ba00ecc38c8d0592fcd2f898ad5..c350426bd8faed980c09c442424a64a946fbd07e 100644 --- a/src/layoutelements/layoutelement-colorscale.cpp +++ b/src/layoutelements/layoutelement-colorscale.cpp @@ -147,34 +147,6 @@ QString QCPColorScale::label() const return mColorAxis.data()->label(); } -/* undocumented getter */ -bool QCPColorScale::rangeDrag() const -{ - if (!mAxisRect) - { - qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; - return false; - } - - return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) && - mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) && - mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); -} - -/* undocumented getter */ -bool QCPColorScale::rangeZoom() const -{ - if (!mAxisRect) - { - qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; - return false; - } - - return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) && - mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) && - mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType); -} - /*! Sets at which side of the color scale the axis is placed, and thus also its orientation. @@ -219,8 +191,6 @@ void QCPColorScale::setType(QCPAxis::AxisType type) mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange))); connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType))); - mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0, - QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0); } } @@ -309,126 +279,6 @@ void QCPColorScale::setBarWidth(int width) mBarWidth = width; } -/*! - Sets whether the user can drag the data range (\ref setDataRange). - - Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref - QCustomPlot::setInteractions) to allow range dragging. -*/ -void QCPColorScale::setRangeDrag(bool enabled) -{ - if (!mAxisRect) - { - qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; - return; - } - - if (enabled) - mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType)); - else - mAxisRect.data()->setRangeDrag(0); -} - -/*! - Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel. - - Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref - QCustomPlot::setInteractions) to allow range dragging. -*/ -void QCPColorScale::setRangeZoom(bool enabled) -{ - if (!mAxisRect) - { - qDebug() << Q_FUNC_INFO << "internal axis rect was deleted"; - return; - } - - if (enabled) - mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType)); - else - mAxisRect.data()->setRangeZoom(0); -} - -/*! - Returns a list of all the color maps associated with this color scale. -*/ -QList QCPColorScale::colorMaps() const -{ - QList result; - for (int i=0; iplottableCount(); ++i) - { - if (QCPColorMap *cm = qobject_cast(mParentPlot->plottable(i))) - if (cm->colorScale() == this) - result.append(cm); - } - return result; -} - -/*! - Changes the data range such that all color maps associated with this color scale are fully mapped - to the gradient in the data dimension. - - \see setDataRange -*/ -void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps) -{ - QList maps = colorMaps(); - QCPRange newRange; - bool haveRange = false; - int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace) - if (mDataScaleType == QCPAxis::stLogarithmic) - sign = (mDataRange.upper < 0 ? -1 : 1); - for (int i=0; irealVisibility() && onlyVisibleMaps) - continue; - QCPRange mapRange; - if (maps.at(i)->colorScale() == this) - { - bool currentFoundRange = true; - mapRange = maps.at(i)->data()->dataBounds(); - if (sign == 1) - { - if (mapRange.lower <= 0 && mapRange.upper > 0) - mapRange.lower = mapRange.upper*1e-3; - else if (mapRange.lower <= 0 && mapRange.upper <= 0) - currentFoundRange = false; - } else if (sign == -1) - { - if (mapRange.upper >= 0 && mapRange.lower < 0) - mapRange.upper = mapRange.lower*1e-3; - else if (mapRange.upper >= 0 && mapRange.lower >= 0) - currentFoundRange = false; - } - if (currentFoundRange) - { - if (!haveRange) - newRange = mapRange; - else - newRange.expand(mapRange); - haveRange = true; - } - } - } - if (haveRange) - { - if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data - { - double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason - if (mDataScaleType == QCPAxis::stLinear) - { - newRange.lower = center-mDataRange.size()/2.0; - newRange.upper = center+mDataRange.size()/2.0; - } else // mScaleType == stLogarithmic - { - newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower); - newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower); - } - } - setDataRange(newRange); - } -} - /* inherits documentation from base class */ void QCPColorScale::update(UpdatePhase phase) { diff --git a/src/layoutelements/layoutelement-colorscale.h b/src/layoutelements/layoutelement-colorscale.h index 244f4f6c10d3e144c0d911beb06c7f21fbc82769..93bc273ba2a232c987700fc83a2b4aabb363d519 100644 --- a/src/layoutelements/layoutelement-colorscale.h +++ b/src/layoutelements/layoutelement-colorscale.h @@ -72,8 +72,6 @@ class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged) Q_PROPERTY(QString label READ label WRITE setLabel) Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth) - Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag) - Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom) /// \endcond public: explicit QCPColorScale(QCustomPlot *parentPlot); @@ -87,8 +85,6 @@ public: QCPColorGradient gradient() const { return mGradient; } QString label() const; int barWidth () const { return mBarWidth; } - bool rangeDrag() const; - bool rangeZoom() const; // setters: void setType(QCPAxis::AxisType type); @@ -97,8 +93,6 @@ public: Q_SLOT void setGradient(const QCPColorGradient &gradient); void setLabel(const QString &str); void setBarWidth(int width); - void setRangeDrag(bool enabled); - void setRangeZoom(bool enabled); // non-property methods: QList colorMaps() const; diff --git a/src/qcp-staticlib.pro b/src/qcp-staticlib.pro index 5faaa2f9b8607e4e8616d8cb534356793cd9073f..faf414ca0f9bd1bed3ed4514c4189242d6dae5ba 100644 --- a/src/qcp-staticlib.pro +++ b/src/qcp-staticlib.pro @@ -56,7 +56,13 @@ layoutelements/layoutelement-axisrect.h \ layoutelements/layoutelement-legend.h \ layoutelements/layoutelement-plottitle.h \ layoutelements/layoutelement-colorscale.h \ - colorgradient.h + colorgradient.h \ + interaction.h \ + interactions/interaction-zoomrange.h \ + interactions/interaction-zoomrect.h \ + interactions/interaction-dragrange.h \ + interactions/interaction-select.h \ + interactions/interaction-onebutton.h SOURCES += \ painter.cpp \ @@ -87,7 +93,13 @@ layoutelements/layoutelement-axisrect.cpp \ layoutelements/layoutelement-legend.cpp \ layoutelements/layoutelement-plottitle.cpp \ layoutelements/layoutelement-colorscale.cpp \ - colorgradient.cpp + colorgradient.cpp \ + interaction.cpp \ + interactions/interaction-zoomrange.cpp \ + interactions/interaction-zoomrect.cpp \ + interactions/interaction-dragrange.cpp \ + interactions/interaction-select.cpp \ + interactions/interaction-onebutton.cpp OTHER_FILES += \ ../changelog.txt \ diff --git a/src/qcustomplot.cpp.skeleton b/src/qcustomplot.cpp.skeleton index aa51770e5553233f88058794b0fe78600727c108..6be8c6dca2f1c9d19d374035266a962486271fa5 100644 --- a/src/qcustomplot.cpp.skeleton +++ b/src/qcustomplot.cpp.skeleton @@ -36,6 +36,7 @@ //amalgamation: add item.cpp //amalgamation: add core.cpp //amalgamation: add colorgradient.cpp +//amalgamation: add interaction.cpp //amalgamation: add layoutelements/layoutelement-axisrect.cpp //amalgamation: add layoutelements/layoutelement-legend.cpp //amalgamation: add layoutelements/layoutelement-plottitle.cpp @@ -55,4 +56,9 @@ //amalgamation: add items/item-pixmap.cpp //amalgamation: add items/item-tracer.cpp //amalgamation: add items/item-bracket.cpp +//amalgamation: add interactions/interaction-onebutton.cpp +//amalgamation: add interactions/interaction-dragrange.cpp +//amalgamation: add interactions/interaction-select.cpp +//amalgamation: add interactions/interaction-zoomrange.cpp +//amalgamation: add interactions/interaction-zoomrect.cpp diff --git a/src/qcustomplot.h.skeleton b/src/qcustomplot.h.skeleton index 86b5130bca3e942b7d134268743324b91e8bf7d6..38f1ca7da4b30acd1637592bc771d0636f730dd5 100644 --- a/src/qcustomplot.h.skeleton +++ b/src/qcustomplot.h.skeleton @@ -40,6 +40,7 @@ //amalgamation: add item.h //amalgamation: add core.h //amalgamation: add colorgradient.h +//amalgamation: add interaction.h //amalgamation: add layoutelements/layoutelement-axisrect.h //amalgamation: add layoutelements/layoutelement-legend.h //amalgamation: add layoutelements/layoutelement-plottitle.h @@ -59,6 +60,11 @@ //amalgamation: add items/item-pixmap.h //amalgamation: add items/item-tracer.h //amalgamation: add items/item-bracket.h +//amalgamation: add interactions/interaction-onebutton.h +//amalgamation: add interactions/interaction-dragrange.h +//amalgamation: add interactions/interaction-select.h +//amalgamation: add interactions/interaction-zoomrange.h +//amalgamation: add interactions/interaction-zoomrect.h #endif // QCUSTOMPLOT_H