diff --git a/examples/test_user_interaction/test_user_interaction.cpp b/examples/test_user_interaction/test_user_interaction.cpp index 2b1be02476..b576720617 100644 --- a/examples/test_user_interaction/test_user_interaction.cpp +++ b/examples/test_user_interaction/test_user_interaction.cpp @@ -19,12 +19,14 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) : layout=new QGridLayout; layForm=new QFormLayout; layChk=new QHBoxLayout; + layChk2=new QHBoxLayout; layout->addLayout(layChk,0,0); - layout->addLayout(layForm,1,0); + layout->addLayout(layChk2,1,0); + layout->addLayout(layForm,2,0); // generate a JKQTPlotter and initialize some plot data plot=new JKQTPlotter(this); - layout->addWidget(plot,2,0); + layout->addWidget(plot,3,0); initPlot(); // add some of the default QActions from the JKQTPlotter to the window menu @@ -66,11 +68,40 @@ TestUserInteraction::TestUserInteraction(QWidget *parent) : connect(chkGrid, SIGNAL(toggled(bool)), plot, SLOT(setGrid(bool))); layChk->addWidget(chkGrid); + // add a checkbox to switch the grid on and off + chkLogX=new QCheckBox(tr("X log-scale"), this); + chkLogX->setChecked(false); + connect(chkLogX, SIGNAL(toggled(bool)), plot->getXAxis(), SLOT(setLogAxis(bool))); + layChk2->addWidget(chkLogX); + chkLogY=new QCheckBox(tr("Y log-scale"), this); + chkLogY->setChecked(false); + connect(chkLogY, SIGNAL(toggled(bool)), plot->getYAxis(), SLOT(setLogAxis(bool))); + layChk2->addWidget(chkLogY); + + // add a spin box for plot magnification + cmbMagnification=new QComboBox(this); + cmbMagnification->addItem("25%", 0.25); + cmbMagnification->addItem("50%", 0.50); + cmbMagnification->addItem("75%", 0.75); + cmbMagnification->addItem("100%", 1); + cmbMagnification->addItem("150%", 1.5); + cmbMagnification->addItem("200%", 2); + layForm->addRow("plot magnification:", cmbMagnification); + connect(cmbMagnification, SIGNAL(currentIndexChanged(int)), this, SLOT(setPlotMagnification(int))); + cmbMagnification->setCurrentIndex(3); + + // add a QComboBox that allows to set the left mouse button action for the JKQTPlotter + chkZoomByMouseWheel=new QCheckBox(this); + chkZoomByMouseWheel->setChecked(plot->getZoomByMouseWheel()); + layForm->addRow("zoom by mouse wheel:", chkZoomByMouseWheel); + connect(chkZoomByMouseWheel, SIGNAL(toggled(bool)), plot, SLOT(setZoomByMouseWheel(bool))); + // add a QComboBox that allows to set the left mouse button action for the JKQTPlotter cmbMouseAction=new QComboBox(this); layForm->addRow("mouse action:", cmbMouseAction); cmbMouseAction->addItem("NoMouseAction"); - cmbMouseAction->addItem("PanPlot=DragPlotWindow"); + cmbMouseAction->addItem("PanPlotOnMove"); + cmbMouseAction->addItem("PanPlotOnRelease"); cmbMouseAction->addItem("ZoomRectangle"); cmbMouseAction->addItem("RectangleEvents"); cmbMouseAction->addItem("CircleEvents"); @@ -110,6 +141,11 @@ void TestUserInteraction::setLeftMouseAction(int index) plot->setMouseActionMode(static_cast(index)); } +void TestUserInteraction::setPlotMagnification(int index) +{ + plot->setMagnification(cmbMagnification->itemData(index).toDouble()); +} + void TestUserInteraction::plotMouseMove(double x, double y) { labMouseMoved->setText(QString("plotMouseMove(%1, %2)").arg(x).arg(y)); diff --git a/examples/test_user_interaction/test_user_interaction.h b/examples/test_user_interaction/test_user_interaction.h index 27014b0157..0127b91d7b 100644 --- a/examples/test_user_interaction/test_user_interaction.h +++ b/examples/test_user_interaction/test_user_interaction.h @@ -22,6 +22,7 @@ class TestUserInteraction : public QMainWindow public slots: void setLeftMouseAction(int index); + void setPlotMagnification(int index); void plotMouseMove(double x, double y); void plotMouseClicked(double x, double y, Qt::KeyboardModifiers modifiers, Qt::MouseButton button); @@ -40,15 +41,20 @@ class TestUserInteraction : public QMainWindow JKQTPlotter* plot; QGridLayout* layout; QHBoxLayout* layChk; + QHBoxLayout* layChk2; QFormLayout* layForm; QCheckBox* chkPositionDisplay; QCheckBox* chkShowToolbar; QCheckBox* chkToolbarAlwaysOn; QCheckBox* chkGrid; QComboBox* cmbMouseAction; + QCheckBox* chkLogX; + QCheckBox* chkLogY; + QCheckBox* chkZoomByMouseWheel; QLabel* labMouseAction; QLabel* labMouseMoved; QLabel* labMouseClicked; + QComboBox* cmbMagnification; JKQTPXYLineGraph* graph1; JKQTPXYLineGraph* graph2; diff --git a/lib/jkqtplotter/jkqtpbaseelements.cpp b/lib/jkqtplotter/jkqtpbaseelements.cpp index 64480c5a06..c79743c328 100644 --- a/lib/jkqtplotter/jkqtpbaseelements.cpp +++ b/lib/jkqtplotter/jkqtpbaseelements.cpp @@ -557,6 +557,11 @@ QString JKQTPCoordinateAxis::floattolabel(double data) { QLocale loc=QLocale::system(); loc.setNumberOptions(QLocale::OmitGroupSeparator); + double belowIsZero=1e-300; + if (!getLogAxis()) { + belowIsZero=fabs(getMax()-getMin())*1e-6; + } + switch(labelType) { case JKQTPCALTdefault: { QString res=loc.toString(data, 'f', past_comma); @@ -569,7 +574,7 @@ QString JKQTPCoordinateAxis::floattolabel(double data) { return res; }; break; case JKQTPCALTexponent: { - return QString(jkqtp_floattolatexstr(data, past_comma, remove_trail0, 1e-300, pow(10, -past_comma), pow(10, past_comma+1)).c_str()); + return QString(jkqtp_floattolatexstr(data, past_comma, remove_trail0, belowIsZero, pow(10, -past_comma), pow(10, past_comma+1)).c_str()); }; break; case JKQTPCALTexponentCharacter: { return QString(jkqtp_floattounitstr(data, past_comma, remove_trail0).c_str()); diff --git a/lib/jkqtplotter/jkqtpbaseplotter.h b/lib/jkqtplotter/jkqtpbaseplotter.h index 7faa1e92ed..61dee9cd4e 100644 --- a/lib/jkqtplotter/jkqtpbaseplotter.h +++ b/lib/jkqtplotter/jkqtpbaseplotter.h @@ -1101,12 +1101,12 @@ class LIB_EXPORT JKQTBasePlotter: public QObject { } /** \brief return time coordinate coordinate from x-pixel */ - inline double p2x(long x) const { + inline double p2x(double x) const { return xAxis->p2x(x); } /** \brief return y coordinate coordinate from y-pixel */ - inline double p2y(long y) const { + inline double p2y(double y) const { return yAxis->p2x(y); } diff --git a/lib/jkqtplotter/jkqtplotter.cpp b/lib/jkqtplotter/jkqtplotter.cpp index 1e1bca48ce..48ddf9c4c7 100644 --- a/lib/jkqtplotter/jkqtplotter.cpp +++ b/lib/jkqtplotter/jkqtplotter.cpp @@ -334,15 +334,22 @@ void JKQTPlotter::mouseMoveEvent ( QMouseEvent * event ) { (mouseActionMode==JKQTPlotter::CircleEvents) || (mouseActionMode==JKQTPlotter::EllipseEvents) || (mouseActionMode==JKQTPlotter::ScribbleEvents) || + (mouseActionMode==JKQTPlotter::PanPlotOnMove) || + (mouseActionMode==JKQTPlotter::PanPlotOnRelease) || (mouseActionMode==JKQTPlotter::LineEvents) ) && mouseDragingRectangle && (event->buttons() & Qt::LeftButton)) { - if (mouseActionMode==JKQTPlotter::ScribbleEvents) { + if (mouseActionMode==JKQTPlotter::ScribbleEvents || mouseActionMode==JKQTPlotter::PanPlotOnMove) { + // start is last event position mouseDragRectXStart=mouseDragRectXEnd; mouseDragRectYStart=mouseDragRectYEnd; - } + mouseDragRectXStartPixel=mouseDragRectXEndPixel; + mouseDragRectYStartPixel=mouseDragRectYEndPixel; + } mouseDragRectXEnd=plotter->p2x(event->x()/magnification); mouseDragRectYEnd=plotter->p2y((event->y()-getPlotYOffset())/magnification); + mouseDragRectXEndPixel=event->x(); + mouseDragRectYEndPixel=event->y(); paintUserAction(); event->accept(); //std::cout<modifiers(), false, false); } + if ((mouseActionMode==JKQTPlotter::PanPlotOnMove) && ((mouseDragRectXStart!=mouseDragRectXEnd) || (mouseDragRectYStart!=mouseDragRectYEnd)) ) { + QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin()))); + if ( (mouseLastClickX/magnificationgetInternalPlotBorderLeft()) || (mouseLastClickX/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + zoomRect.translate(0, mouseDragRectYStartPixel-mouseDragRectYEndPixel); + } else if (((mouseLastClickY-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((mouseLastClickY-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + zoomRect.translate(mouseDragRectXStartPixel-mouseDragRectXEndPixel, 0); + } else { + zoomRect.translate(mouseDragRectXStartPixel-mouseDragRectXEndPixel, mouseDragRectYStartPixel-mouseDragRectYEndPixel); + } + setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top())); + } } else { event->accept(); /*if (emitSignals)*/ //emit plotMouseMove(x, y); @@ -374,6 +392,8 @@ void JKQTPlotter::mousePressEvent ( QMouseEvent * event ){ { mouseDragRectXStart=plotter->p2x(event->x()/magnification); mouseDragRectYStart=plotter->p2y((event->y()-getPlotYOffset())/magnification); + mouseDragRectXEndPixel=mouseDragRectXStartPixel=event->x(); + mouseDragRectYEndPixel=mouseDragRectYStartPixel=event->y(); mouseDragingRectangle=true; oldImage=image; event->accept(); @@ -425,6 +445,8 @@ void JKQTPlotter::mouseReleaseEvent ( QMouseEvent * event ){ if (mouseDragingRectangle && event->button()==Qt::LeftButton) { mouseDragRectXEnd=plotter->p2x(event->x()/magnification); mouseDragRectYEnd=plotter->p2y((event->y()-getPlotYOffset())/magnification); + mouseDragRectXEndPixel=event->x(); + mouseDragRectYEndPixel=event->y(); image=oldImage; //update(); mouseDragingRectangle=false; @@ -445,6 +467,16 @@ void JKQTPlotter::mouseReleaseEvent ( QMouseEvent * event ){ emit zoomChangedLocally(xmin, xmax, ymin, ymax, this); plotter->setXY(xmin, xmax, ymin, ymax); + } else if (mouseActionMode==JKQTPlotter::PanPlotOnRelease) { + QRectF zoomRect= QRectF(QPointF(plotter->x2p(getXAxis()->getMin()),plotter->y2p(getYAxis()->getMax())), QPointF(plotter->x2p(getXAxis()->getMax()),plotter->y2p(getYAxis()->getMin()))); + if ( (mouseLastClickX/magnificationgetInternalPlotBorderLeft()) || (mouseLastClickX/magnification>plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) ) { + zoomRect.translate(0, mouseDragRectYStartPixel-mouseDragRectYEndPixel); + } else if (((mouseLastClickY-getPlotYOffset())/magnificationgetInternalPlotBorderTop()) || ((mouseLastClickY-getPlotYOffset())/magnification>plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + zoomRect.translate(mouseDragRectXStartPixel-mouseDragRectXEndPixel, 0); + } else { + zoomRect.translate(mouseDragRectXStartPixel-mouseDragRectXEndPixel, mouseDragRectYStartPixel-mouseDragRectYEndPixel); + } + setXY(plotter->p2x(zoomRect.left()), plotter->p2x(zoomRect.right()), plotter->p2y(zoomRect.bottom()), plotter->p2y(zoomRect.top())); } else if (mouseActionMode==JKQTPlotter::RectangleEvents) { emit userRectangleFinished(x1, y1, x2-x1, y2-y1, event->modifiers()); } else if (mouseActionMode==JKQTPlotter::CircleEvents) { @@ -515,8 +547,8 @@ void JKQTPlotter::keyReleaseEvent(QKeyEvent *event) { void JKQTPlotter::wheelEvent ( QWheelEvent * event ) { // only react on wheel turns inside the widget, turning forward will zoom out and turning backwards will zoom in by a factor of 2 - if ( (event->x()/magnification>=plotter->getInternalPlotBorderLeft()) && (event->x()/magnification<=plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) && - ((event->y()-getPlotYOffset())/magnification>=plotter->getInternalPlotBorderTop()) && ((event->y()-getPlotYOffset())/magnification<=plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) { + if ( zoomByMouseWheel && ((event->x()/magnification>=plotter->getInternalPlotBorderLeft()) && (event->x()/magnification<=plotter->getPlotWidth()+plotter->getInternalPlotBorderLeft()) && + ((event->y()-getPlotYOffset())/magnification>=plotter->getInternalPlotBorderTop()) && ((event->y()-getPlotYOffset())/magnification<=plotter->getPlotHeight()+plotter->getInternalPlotBorderTop()) ) ) { double factor=pow(2.0, 1.0*static_cast(event->delta())/120.0)*2.0; double xmin=plotter->p2x((long)round(static_cast(event->x())/magnification-static_cast(plotter->getPlotWidth())/factor)); double xmax=plotter->p2x((long)round(static_cast(event->x())/magnification+static_cast(plotter->getPlotWidth())/factor)); @@ -620,7 +652,9 @@ void JKQTPlotter::updateCursor() { QBitmap cursor(":/JKQTPlotter/jkqtp_cursor_rectangle.png"); QBitmap mask(":/JKQTPlotter/jkqtp_cursor_rectangle_mask.png"); setCursor(QCursor(cursor, mask, 9, 14)); - } else if (mouseActionMode==JKQTPlotter::CircleEvents) { + } else if (mouseActionMode==JKQTPlotter::PanPlotOnMove || mouseActionMode==JKQTPlotter::PanPlotOnRelease) { + setCursor(QCursor(Qt::OpenHandCursor)); + } else if (mouseActionMode==JKQTPlotter::CircleEvents) { QBitmap cursor(":/JKQTPlotter/jkqtp_cursor_circle.png"); QBitmap mask(":/JKQTPlotter/jkqtp_cursor_circle_mask.png"); setCursor(QCursor(cursor, mask, 9, 14)); diff --git a/lib/jkqtplotter/jkqtplotter.h b/lib/jkqtplotter/jkqtplotter.h index 4e3f4ae473..378a88756e 100644 --- a/lib/jkqtplotter/jkqtplotter.h +++ b/lib/jkqtplotter/jkqtplotter.h @@ -191,15 +191,15 @@ class LIB_EXPORT JKQTPlotter: public QWidget { * This allows you to e.g. draw rectangles or lines over the plot and receive a signal, when the drawing finishes */ enum MouseActionModes { NoMouseAction=0, /*!< \brief no action is to be performed */ - DragPlotWindow=1, /*!< \brief the user can draw the current plot window while keeping the left mouse-button pushed down (=panning) */ - PanPlot=DragPlotWindow, /*!< \copydoc DragPlotWindow */ - ZoomRectangle=2, /*!< \brief draw a rectangle and when finish zoom to that rectangle */ - RectangleEvents=3, /*!< \brief draw a rectangle and when finished execute the signal userRectangleFinished() */ - CircleEvents=4, /*!< \brief draw a circle and when finished execute the signal userCircleFinished() */ - EllipseEvents=5, /*!< \brief draw an ellipse and when finished execute the signal userEllipseFinished() */ - LineEvents=6, /*!< \brief draw a line and when finished execute the signal userLineFinished() */ - ScribbleEvents=7, /*!< \brief let the user scribble on the plot (left mouse button is kept pressed) and call userScribbleClick() for each new position */ - ClickEvents=8 /*!< \brief sinply call userClickFinished() for every single-click of the mouse button */ + PanPlotOnMove, /*!< \brief the user can drag the current plot window while keeping the left mouse-button pushed down (=panning), the new widow is applied/displayed whenever the mouse moves */ + PanPlotOnRelease, /*!< \brief the user can drag the current plot window while keeping the left mouse-button pushed down (=panning), the new widow is applied/displayed when the left mouse button is released */ + ZoomRectangle, /*!< \brief draw a rectangle and when finish zoom to that rectangle */ + RectangleEvents, /*!< \brief draw a rectangle and when finished execute the signal userRectangleFinished() */ + CircleEvents, /*!< \brief draw a circle and when finished execute the signal userCircleFinished() */ + EllipseEvents, /*!< \brief draw an ellipse and when finished execute the signal userEllipseFinished() */ + LineEvents, /*!< \brief draw a line and when finished execute the signal userLineFinished() */ + ScribbleEvents, /*!< \brief let the user scribble on the plot (left mouse button is kept pressed) and call userScribbleClick() for each new position */ + ClickEvents /*!< \brief sinply call userClickFinished() for every single-click of the mouse button */ }; /** \brief options of how to react to a right mouse button click */ @@ -365,11 +365,6 @@ class LIB_EXPORT JKQTPlotter: public QWidget { /*! \brief set the property menuSpecialContextMenu ( \copybrief menuSpecialContextMenu ). \details Description of the parameter menuSpecialContextMenu is:
\copydoc menuSpecialContextMenu
. \see menuSpecialContextMenu for more information */ void setMenuSpecialContextMenu(QMenu* menu); - - /*! \brief sets the property zoomByMouseWheel ( \copybrief zoomByMouseWheel ) to the specified \a __value. - \details Description of the parameter zoomByMouseWheel is:
\copydoc zoomByMouseWheel
- \see zoomByMouseWheel for more information */ - void setZoomByMouseWheel(bool __value); /*! \brief returns the property zoomByMouseWheel ( \copybrief zoomByMouseWheel ). \details Description of the parameter zoomByMouseWheel is:
\copydoc zoomByMouseWheel
\see zoomByMouseWheel for more information */ @@ -591,6 +586,12 @@ class LIB_EXPORT JKQTPlotter: public QWidget { void setMouseActionMode(const MouseActionModes & __value); /*! \brief equivalent to \c setMouseActionMode(JKQTPlotter::ZoomRectangle) */ void setZoomByMouseRectangle(bool zomByrectangle); + + /*! \brief sets the property zoomByMouseWheel ( \copybrief zoomByMouseWheel ) to the specified \a __value. + \details Description of the parameter zoomByMouseWheel is:
\copydoc zoomByMouseWheel
+ \see zoomByMouseWheel for more information */ + void setZoomByMouseWheel(bool __value); + /*! \brief sets the property leftDoubleClickAction ( \copybrief leftDoubleClickAction ) to the specified \a __value. \details Description of the parameter leftDoubleClickAction is:
\copydoc leftDoubleClickAction
\see leftDoubleClickAction for more information */ @@ -798,17 +799,31 @@ class LIB_EXPORT JKQTPlotter: public QWidget { /** \brief this is set \c true if we are drawing a zoom rectangle */ bool mouseDragingRectangle; - /** \brief when zooming by moving the mouse this contains the x-coordinate the user clicked on */ + /** \brief when draging the mouse this contains the x-coordinate the user clicked on (in plot coordinates) */ double mouseDragRectXStart; - /** \brief when zooming by moving the mouse this contains the x-coordinate the mouse is currently + /** \brief when draging the mouse this contains the x-coordinate the user clicked on (in pixels) */ + int mouseDragRectXStartPixel; + + /** \brief when draging the mouse this contains the x-coordinate the mouse is currently + * pointing to (in pixels) */ + int mouseDragRectXEndPixel; + + /** \brief when draging the mouse this contains the y-coordinate the mouse is currently + * pointing to (in pixels) */ + int mouseDragRectYEndPixel; + + /** \brief when draging the mouse this contains the x-coordinate the mouse is currently * pointing to */ double mouseDragRectXEnd; - /** \brief when zooming by moving the mouse this contains the y-coordinate the user clicked on */ + /** \brief when draging the mouse this contains the y-coordinate the user clicked on (in plot coordinates) */ double mouseDragRectYStart; + /** \brief when zooming by moving the mouse this contains the y-coordinate the user clicked on (in pixels) */ + int mouseDragRectYStartPixel; + /** \brief when zooming by moving the mouse this contains the y-coordinate the mouse is currently * pointing to */