mirror of
https://github.com/jkriege2/JKQtPlotter.git
synced 2024-12-25 10:01:38 +08:00
IMPROVED/REWORKED: zomm/pan by mouse-wheel: now there are modes that support zoomin AND panning by trakpad and mouse-wheel simultaneously!
This commit is contained in:
parent
5240cf8681
commit
9762b44b7d
@ -35,6 +35,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
|
||||
<li>IMPROVED/REWORKED: reworked JKQTPErrorPlotstyle and error indicator plotting so error-inidcators can be specified as ORed combination of flags from JKQTPErrorPlotstyleElements, added additional error indicator styles (half-bars, arrows...)</li>
|
||||
<li>IMPROVED/REWORKED: reworked JKQTPCADrawMode and coordinate axis drawing so the draw mide can be specified as ORed combination of flags from JKQTPCADrawModeElements, added flags to draw arrows at the end of the axis line</li>
|
||||
<li>IMPROVED/REWORKED: coordinate axis code was refactored</li>
|
||||
<li>IMPROVED/REWORKED: zomm/pan by mouse-wheel: now there are modes that support zoomin AND panning by trakpad and mouse-wheel simultaneously! This can only be implemented using heuristics, due to the way that Qt handles track-pad events, but the current solution should at least improve the behaviour seen before. Mainly <code>jkqtpmwaZoomByWheelAndTrackpadPan</code> was introduced into <code>JKQTPMouseWheelActions</code> und is set as default mode: Here JKQTPlotter tries to distinguish the QWheelEvent s sent by an actual mouse wheel and a trackpad.</li>
|
||||
<li>NEW: JKQTPFilledCurveXGraph and JKQTPFilledCurveYGraph can now plot wiggle plots with different fill styles above and below the baseline (feature request <a href="https://github.com/jkriege2/JKQtPlotter/issues/68">#68 Wiggle Plots</a> from <a href="https://github.com/xichaoqiang">user:xichaoqiang</a> </li>
|
||||
<li>NEW/BREAKING CHANGE: data tooltip can now also be shown when "just" moving the mouse (so far this was only possible when dragging the mouse with a button pressed). This also removes JKQtPlotter::getActMouseLeftAsToolTip() and adds JKQtPlotter::getActMouseMoveToolTip() instead! Also the default toolbars and context menus changed!</li>
|
||||
<li>NEW: new "seaborn" style for plots, see \ref jkqtpplotter_styling </li>
|
||||
|
@ -5,7 +5,7 @@
|
||||
* \see \ref JKQTPLOTTER_USERINTERACTION
|
||||
*/
|
||||
|
||||
#include "jkqtpexampleapplication.h"
|
||||
#include "jkqtpexampleapplication.h"
|
||||
#include "test_user_interaction.h"
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
@ -211,6 +211,7 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) :
|
||||
layForm->addRow("mouse action: mouse wheel, no modifiers", cmbMouseWheelAction);
|
||||
cmbMouseWheelAction->addItem("jkqtpmwaZoomByWheel");
|
||||
cmbMouseWheelAction->addItem("jkqtpmwaPanByWheel");
|
||||
cmbMouseWheelAction->addItem("jkqtpmwaZoomByWheelAndTrackpadPan");
|
||||
cmbMouseWheelAction->addItem("NoAction");
|
||||
cmbMouseWheelAction->setCurrentIndex(0);
|
||||
connect(cmbMouseWheelAction, SIGNAL(currentIndexChanged(int)), this, SLOT(setMouseWheelNoModAction(int)));
|
||||
|
@ -1010,67 +1010,106 @@ void JKQTPlotter::wheelEvent ( QWheelEvent * event ) {
|
||||
const double wheel_x=event->position().x();
|
||||
const double wheel_y=event->position().y();
|
||||
#else
|
||||
const int wheel_x=event->pos().x();
|
||||
const int wheel_y=event->pos().y();
|
||||
const double wheel_x=event->pos().x();
|
||||
const double wheel_y=event->pos().y();
|
||||
#endif
|
||||
|
||||
//qDebug()<<"wheelEvent("<<event->modifiers()<<"): plotterStyle.registeredMouseWheelActions="<<plotterStyle.registeredMouseWheelActions;
|
||||
const QPoint angleDelta=event->angleDelta();
|
||||
QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin())));
|
||||
//qDebug()<<"wheelEvent: "<<event->type()<<", "<<event->modifiers()<<", "<<angleDelta<<", "<<event->pixelDelta()<<", "<<event->inverted()<<", "<<event->source()<<", "<<event->buttons();
|
||||
bool foundIT=false;
|
||||
auto itAction=findMatchingMouseWheelAction(event->modifiers(), &foundIT);
|
||||
//qDebug()<<"wheelEvent("<<event->modifiers()<<"): plotterStyle.registeredMouseWheelActions="<<plotterStyle.registeredMouseWheelActions;
|
||||
//qDebug()<<"wheelEvent("<<event->modifiers()<<"): itAction="<<itAction.key()<<","<<itAction.value()<<" !=end:"<<(itAction!=plotterStyle.registeredMouseWheelActions.end())<<" ==end:"<<(itAction==plotterStyle.registeredMouseWheelActions.end());
|
||||
|
||||
enum {
|
||||
acZoom,
|
||||
acPan,
|
||||
acNone
|
||||
} acTodo=acNone;
|
||||
QPointF d(0,0); // for acPan, the shift of the ROI
|
||||
double factor=1; // for zooming, the zoom-factor
|
||||
|
||||
if (foundIT) {
|
||||
if (itAction.value()==JKQTPMouseWheelActions::jkqtpmwaZoomByWheel) {
|
||||
//if (act==JKQTPMouseWheelActions::jkqtpmwaZoomByWheel) {
|
||||
//qDebug()<<"wheelEvent("<<event->modifiers()<<"):ZoomByWheel";
|
||||
const double factor=pow(2.0, 1.0*static_cast<double>(event->angleDelta().y())/120.0)*2.0;
|
||||
double xmin=plotter->p2x(static_cast<double>(wheel_x)/magnification-static_cast<double>(plotter->getPlotWidth())/factor);
|
||||
double xmax=plotter->p2x(static_cast<double>(wheel_x)/magnification+static_cast<double>(plotter->getPlotWidth())/factor);
|
||||
double ymin=plotter->p2y(static_cast<double>(wheel_y)/magnification-static_cast<double>(getPlotYOffset())+static_cast<double>(plotter->getPlotHeight())/factor);
|
||||
double ymax=plotter->p2y(static_cast<double>(wheel_y)/magnification-static_cast<double>(getPlotYOffset())-static_cast<double>(plotter->getPlotHeight())/factor);
|
||||
if ( (wheel_x/magnification<plotter->getInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) {
|
||||
xmin=getXMin();
|
||||
xmax=getXMax();
|
||||
} else if (((wheel_y-getPlotYOffset())/magnification<plotter->getInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) {
|
||||
ymin=getYMin();
|
||||
ymax=getYMax();
|
||||
acTodo=acZoom;
|
||||
if (abs(angleDelta.y())>30 && angleDelta.x()==0) {
|
||||
factor=pow(2.0, 1.0*static_cast<double>(angleDelta.y())/120.0);
|
||||
} else if (abs(angleDelta.x())>30 && angleDelta.y()==0) {
|
||||
factor=pow(2.0, 1.0*static_cast<double>(angleDelta.x())/120.0);
|
||||
}
|
||||
plotter->setXY(xmin, xmax, ymin, ymax, true);
|
||||
} else if (itAction.value()==JKQTPMouseWheelActions::jkqtpmwaPanByWheel) {
|
||||
//} else if (act==JKQTPMouseWheelActions::jkqtpmwaPanByWheel) {
|
||||
//qDebug()<<"wheelEvent("<<event->modifiers()<<"):PanByWheel";
|
||||
QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin())));
|
||||
QPointF d=QPointF(event->angleDelta().x()/120.0*zoomRect.width()/10.0,
|
||||
event->angleDelta().y()/120.0*zoomRect.height()/10.0);
|
||||
if (d.x()<-100) d.setX(-100);
|
||||
if (d.x()>100) d.setX(100);
|
||||
if (d.y()<-100) d.setY(-100);
|
||||
if (d.y()>100) d.setY(100);
|
||||
if (d.x()>=0 && d.x()<10) d.setX(10);
|
||||
if (d.x()<0 && d.x()>-10) d.setX(-10);
|
||||
if (d.y()>=0 && d.y()<10) d.setY(10);
|
||||
if (d.y()<0 && d.y()>-10) d.setY(-10);
|
||||
if ( (wheel_x/magnification<plotter->getInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) {
|
||||
zoomRect.translate(0, d.y());
|
||||
} else if (((wheel_y-getPlotYOffset())/magnification<plotter->getInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) {
|
||||
zoomRect.translate(d.x(), 0);
|
||||
} else if (itAction.value()==JKQTPMouseWheelActions::jkqtpmwaZoomByWheelAndTrackpadPan) {
|
||||
if (abs(angleDelta.x())<30 && abs(angleDelta.y())<30) {
|
||||
// this heuristics recognizes pan-gestures on a track-pad.
|
||||
// These are converted by Qt to wheelEvents with small angleDelta()-Values
|
||||
// typical angleDelta values are 120 (1/10 degree) or above, here we use 30
|
||||
// to accomodate for finer resolved mouse-wheels
|
||||
//
|
||||
// unfortunately there is no other way to distinguish these cases, as Qt does not transport
|
||||
// the source of the QWheelEvent!
|
||||
acTodo=acPan;
|
||||
d=angleDelta;
|
||||
} else {
|
||||
zoomRect.translate(d.x(), d.y());
|
||||
acTodo=acZoom;
|
||||
if (abs(angleDelta.y())>30 && angleDelta.x()==0) {
|
||||
factor=pow(2.0, 1.0*static_cast<double>(angleDelta.y())/120.0);
|
||||
} else if (abs(angleDelta.x())>30 && angleDelta.y()==0) {
|
||||
factor=pow(2.0, 1.0*static_cast<double>(angleDelta.x())/120.0);
|
||||
}
|
||||
}
|
||||
setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top()), true);
|
||||
} else if (itAction.value()==JKQTPMouseWheelActions::jkqtpmwaPanByWheel) {
|
||||
acTodo=acPan;
|
||||
d=QPointF(angleDelta.x()/120.0*zoomRect.width()/10.0,
|
||||
angleDelta.y()/120.0*zoomRect.height()/10.0);
|
||||
// maximum shoft is 100 Pixels in either direction
|
||||
d.setX(jkqtp_bounded<double>(-100, d.x(), 100));
|
||||
d.setY(jkqtp_bounded<double>(-100, d.y(), 100));
|
||||
// minimmum shift is 10 pixels, unles |shift|<1
|
||||
if (d.x()>=1 && d.x()<10) d.setX(10);
|
||||
if (d.x()<=-1 && d.x()>-10) d.setX(-10);
|
||||
if (d.y()>=1 && d.y()<10) d.setY(10);
|
||||
if (d.y()<=-1 && d.y()>-10) d.setY(-10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//qDebug()<<" action: "<<acTodo<<" factor="<<factor<<" d="<<d;
|
||||
if (acTodo==acZoom && factor!=1.0) {
|
||||
double xmin=plotter->p2x(static_cast<double>(wheel_x)/magnification-static_cast<double>(plotter->getPlotWidth()*0.5)/factor);
|
||||
double xmax=plotter->p2x(static_cast<double>(wheel_x)/magnification+static_cast<double>(plotter->getPlotWidth()*0.5)/factor);
|
||||
double ymin=plotter->p2y(static_cast<double>(wheel_y)/magnification-static_cast<double>(getPlotYOffset())+static_cast<double>(plotter->getPlotHeight()*0.5)/factor);
|
||||
double ymax=plotter->p2y(static_cast<double>(wheel_y)/magnification-static_cast<double>(getPlotYOffset())-static_cast<double>(plotter->getPlotHeight()*0.5)/factor);
|
||||
if ( (wheel_x/magnification<plotter->getInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) {
|
||||
xmin=getXMin();
|
||||
xmax=getXMax();
|
||||
} else if (((wheel_y-getPlotYOffset())/magnification<plotter->getInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) {
|
||||
ymin=getYMin();
|
||||
ymax=getYMax();
|
||||
}
|
||||
//qDebug()<<" zoom: factor="<<factor;
|
||||
plotter->setXY(xmin, xmax, ymin, ymax, true);
|
||||
} else if (acTodo==acPan && (d.x()!=0 || d.y()!=0)) {
|
||||
//qDebug()<<" pan: d="<<d;
|
||||
if ( (wheel_x/magnification<plotter->getInternalPlotBorderLeft()) || (wheel_x/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) {
|
||||
zoomRect.translate(0, d.y());
|
||||
} else if (((wheel_y-getPlotYOffset())/magnification<plotter->getInternalPlotBorderTop()) || ((wheel_y-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) {
|
||||
zoomRect.translate(d.x(), 0);
|
||||
} else {
|
||||
zoomRect.translate(d.x(), d.y());
|
||||
}
|
||||
setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top()), true);
|
||||
}
|
||||
|
||||
event->accept();
|
||||
|
||||
emit plotMouseWheelOperated(plotter->p2x(wheel_x/magnification), plotter->p2y((wheel_y-getPlotYOffset())/magnification), event->modifiers(), event->angleDelta().x(), event->angleDelta().y());
|
||||
emit plotMouseWheelOperated(plotter->p2x(wheel_x/magnification), plotter->p2y((wheel_y-getPlotYOffset())/magnification), event->modifiers(), angleDelta.x(), angleDelta.y());
|
||||
|
||||
updateCursor();
|
||||
currentMouseDragAction.clear();
|
||||
}
|
||||
|
||||
bool JKQTPlotter::event(QEvent* event) {
|
||||
//qDebug()<<"event: "<<event->type()<<", "<<event->isAccepted();
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
int JKQTPlotter::getPlotYOffset() {
|
||||
int plotYOffset=0;
|
||||
if (plotterStyle.displayMousePosition) {
|
||||
|
@ -1704,7 +1704,8 @@ class JKQTPLOTTER_LIB_EXPORT JKQTPlotter: public QWidget {
|
||||
/** \brief action that activates the pan view tool (override!) */
|
||||
QAction* actMouseLeftAsPanView;
|
||||
|
||||
protected slots:
|
||||
virtual bool event(QEvent *event) override;
|
||||
protected slots:
|
||||
/** \brief while the window is resized, the plot is only redrawn after a restartable delay, implemented by this function and resizeTimer
|
||||
* \internal
|
||||
* \see resizeTimer
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "jkqtplotterstyle.h"
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include "jkqtcommon/jkqttools.h"
|
||||
#include "jkqtplotter/jkqtptools.h"
|
||||
|
||||
JKQTPlotterStyle::JKQTPlotterStyle():
|
||||
@ -32,7 +31,8 @@ JKQTPlotterStyle::JKQTPlotterStyle():
|
||||
registeredMouseDragActionModes[qMakePair<Qt::MouseButton, Qt::KeyboardModifiers>(Qt::LeftButton, Qt::NoModifier)]=JKQTPMouseDragActions::jkqtpmdaZoomByRectangle;
|
||||
registeredMouseDragActionModes[qMakePair<Qt::MouseButton, Qt::KeyboardModifiers>(Qt::LeftButton, Qt::ControlModifier)]=JKQTPMouseDragActions::jkqtpmdaPanPlotOnMove;
|
||||
registeredMouseDoubleClickActions[qMakePair<Qt::MouseButton, Qt::KeyboardModifiers>(Qt::LeftButton, Qt::NoModifier)]=JKQTPMouseDoubleClickActions::jkqtpdcaClickMovesViewport;
|
||||
registeredMouseWheelActions[Qt::NoModifier]=JKQTPMouseWheelActions::jkqtpmwaZoomByWheel;
|
||||
registeredMouseWheelActions[Qt::NoModifier]=JKQTPMouseWheelActions::jkqtpmwaZoomByWheelAndTrackpadPan;
|
||||
registeredMouseWheelActions[Qt::ControlModifier]=JKQTPMouseWheelActions::jkqtpmwaZoomByWheel;
|
||||
//qDebug()<<"JKQTPlotterStyle(): registeredMouseWheelActions="<<registeredMouseWheelActions;
|
||||
}
|
||||
|
||||
@ -124,13 +124,13 @@ void JKQTPlotterStyle::loadSettings(const QSettings &settings, const QString &gr
|
||||
id=readID(k, group+"actions/mouse_wheel");
|
||||
if (id>=0) {
|
||||
auto modifiers=jkqtp_String2KeyboardModifiers(settings.value(group+"actions/mouse_wheel"+QString::number(id)+"/modifiers", Qt::NoModifier).toString());
|
||||
auto action=String2JKQTPMouseWheelActions(settings.value(group+"actions/mouse_wheel"+QString::number(id)+"/action", jkqtpmwaZoomByWheel).toString());
|
||||
auto action=String2JKQTPMouseWheelActions(settings.value(group+"actions/mouse_wheel"+QString::number(id)+"/action", jkqtpmwaZoomByWheelAndTrackpadPan).toString());
|
||||
registeredMouseWheelActions[modifiers]=action;
|
||||
}
|
||||
id=readID(k, group+"actions/mouse_move");
|
||||
if (id>=0) {
|
||||
auto modifiers=jkqtp_String2KeyboardModifiers(settings.value(group+"actions/mouse_move"+QString::number(id)+"/modifiers", Qt::NoModifier).toString());
|
||||
auto action=String2JKQTPMouseMoveActions(settings.value(group+"actions/mouse_move"+QString::number(id)+"/action", jkqtpmwaZoomByWheel).toString());
|
||||
auto action=String2JKQTPMouseMoveActions(settings.value(group+"actions/mouse_move"+QString::number(id)+"/action", jkqtpmmaToolTipForClosestDataPoint).toString());
|
||||
registeredMouseMoveActions[modifiers]=action;
|
||||
}
|
||||
}
|
||||
|
@ -331,6 +331,7 @@ JKQTPMouseWheelActions String2JKQTPMouseWheelActions(const QString &act)
|
||||
QString s=act.trimmed().toLower();
|
||||
if (s=="jkqtpmwazoombywheel"||s=="zoombywheel" ||s=="zoom") return jkqtpmwaZoomByWheel;
|
||||
if (s=="jkqtpmwapanbywheel"||s=="panbywheel"||s=="pan") return jkqtpmwaPanByWheel;
|
||||
if (s=="jkqtpmwazoombywheelandtrackpadpan"||s=="zoombywheelortrackpan"||s=="zoomortrackpan") return jkqtpmwaZoomByWheelAndTrackpadPan;
|
||||
return jkqtpmwaZoomByWheel;
|
||||
|
||||
}
|
||||
@ -339,6 +340,7 @@ QString JKQTPMouseWheelActions2String(JKQTPMouseWheelActions act)
|
||||
{
|
||||
if (act==jkqtpmwaZoomByWheel) return "zoom";
|
||||
if (act==jkqtpmwaPanByWheel) return "pan";
|
||||
if (act==jkqtpmwaZoomByWheelAndTrackpadPan) return "zoomortrackpan";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,7 @@ JKQTPLOTTER_LIB_EXPORT JKQTPMouseDoubleClickActions String2JKQTPMouseDoubleClick
|
||||
enum JKQTPMouseWheelActions {
|
||||
jkqtpmwaZoomByWheel=0, /*!< \brief use the mouse-wheel for zooming */
|
||||
jkqtpmwaPanByWheel, /*!< \brief use the mouse-wheel for panning the plot */
|
||||
jkqtpmwaZoomByWheelAndTrackpadPan, /*!< \brief use the mouse-wheel for zooming. In addition, this tries to recognize track-pad pan gestures and applies them. \note This is needed, because Qt converts track-pad zoom AND pan gestures to wheelEvents, but does not provide the source. Therefore a heuristics is required to interpret both! */
|
||||
};
|
||||
|
||||
/** \brief convert a JKQTPMouseWheelActions to a <a href="http://doc.qt.io/qt-5/qstring.html">QString</a>
|
||||
|
Loading…
Reference in New Issue
Block a user