1 # Copyright (C) 2016-2019 CEA/DEN, EDF R&D
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 import matplotlib.pyplot as plt
21 import matplotlib.colors as colors
23 from CurveView import CurveView
25 from utils import Logger, trQ
26 from PlotWidget import PlotWidget
27 from PlotSettings import PlotSettings
28 from pyqtside import QtGui, QtCore
29 from pyqtside.QtCore import QObject
30 from matplotlib.figure import Figure
31 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg, NavigationToolbar2QT
33 class EventHandler(QObject):
34 """ Handle the right-click properly so that it only triggers the contextual menu """
35 def __init__(self,parent=None):
36 QObject.__init__(self, parent)
38 def eventFilter(self, obj, event):
39 if event.type() == QtCore.QEvent.MouseButtonPress:
40 if event.button() == 2:
41 # Discarding right button press to only keep context menu display
42 return True # Event handled (and hence not passed to matplotlib)
43 return QObject.eventFilter(self, obj, event)
46 AUTOFIT_MARGIN = 0.03 # 3%
48 # See http://matplotlib.org/api/markers_api.html:
49 CURVE_MARKERS = [ "o" ,# circle
71 _DEFAULT_LEGEND_STATE = False # for test purposes mainly - initial status of the legend
73 def __init__(self, controller):
74 View.__init__(self, controller)
75 self._eventHandler = EventHandler()
77 self._curveViews = {} # key: curve (model) ID, value: CurveView
78 self._salomeViewID = None
79 self._mplFigure = None
81 self._mplCanvas = None
82 self._plotWidget = None
83 self._sgPyQt = self._controller._sgPyQt
85 self._mplNavigationActions = {}
86 self._toobarMPL = None
88 self._currCrv = None # current curve selected in the view
91 self._legendLoc = "right" # "right" or "bottom"
95 self._dragOnDrop = False
101 self._defaultLineStyle = None
102 self._last_point = None
103 self._lastMarkerID = -1
104 self._blockLogSignal = False
106 self._axisXSciNotation = False
107 self._axisYSciNotation = False
108 self._prevTitle = None
110 def __repaintOK(self):
111 """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
112 @return False if painting is currently locked, in which case it will also register the current XYView
113 as needing a refresh when unlocked
115 ret = self._controller._plotManager.isRepaintLocked()
117 self._controller._plotManager.registerRepaint(self._model)
120 def appendCurve(self, curveID):
121 newC = CurveView(self._controller, self)
122 newC.setModel(self._model._curves[curveID])
123 newC.setMPLAxes(self._mplAxes)
125 newC.setMarker(self.getMarker(go_next=True))
126 self._curveViews[curveID] = newC
128 def removeCurve(self, curveID):
129 v = self._curveViews.pop(curveID)
131 if self._currCrv is not None and self._currCrv.getID() == curveID:
134 def cleanBeforeClose(self):
135 """ Clean some items to avoid accumulating stuff in memory """
136 self._mplFigure.clear()
137 plt.close(self._mplFigure)
138 self._plotWidget.clearAll()
139 # For memory debugging only:
144 if self.__repaintOK():
145 Logger.Debug("XYView::draw")
146 self._mplCanvas.draw()
148 def onXLabelChange(self):
149 if self.__repaintOK():
150 self._mplAxes.set_xlabel(self._model._xlabel)
153 def onYLabelChange(self):
154 if self.__repaintOK():
155 self._mplAxes.set_ylabel(self._model._ylabel)
158 def onTitleChange(self):
159 if self.__repaintOK():
160 self._mplAxes.set_title(self._model._title)
161 self.updateViewTitle()
164 def onCurveTitleChange(self):
165 # Updating the legend should suffice
166 self.showHideLegend()
168 def onClearAll(self):
169 """ Just does an update with a reset of the marker cycle. """
170 if self.__repaintOK():
171 self._lastMarkerID = -1
174 def onPick(self, event):
175 """ MPL callback when picking
177 if event.mouseevent.button == 1:
180 for crv_id, cv in list(self._curveViews.items()):
181 if cv._mplLines[0] is a:
183 # Use the plotmanager so that other plot sets get their current reset:
184 self._controller._plotManager.setCurrentCurve(selected_id)
186 def createAndAddLocalAction(self, icon_file, short_name):
187 return self._toolbar.addAction(self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)
189 def createPlotWidget(self):
190 self._mplFigure = Figure((8.0,5.0), dpi=100)
191 self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
192 self._mplCanvas.installEventFilter(self._eventHandler)
193 self._mplCanvas.mpl_connect('pick_event', self.onPick)
194 self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
195 self._plotWidget = PlotWidget()
196 self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None)
197 for act in self._toobarMPL.actions():
198 actionName = str(act.text()).strip()
199 self._mplNavigationActions[actionName] = act
200 self._plotWidget.setCentralWidget(self._mplCanvas)
201 self._toolbar = self._plotWidget.toolBar
202 self.populateToolbar()
204 self._popupMenu = QtGui.QMenu()
205 self._popupMenu.addAction(self._actionLegend)
207 # Connect evenement for the graphic scene
208 self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
209 self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu)
210 self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
211 self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)
213 def populateToolbar(self):
214 # Action to dump view in a file
215 a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
216 a.triggered.connect(self.dumpView)
217 self._toolbar.addSeparator()
218 # Actions to manipulate the scene
219 a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
220 a.triggered.connect(self.autoFit)
221 # Zoom and pan are mutually exclusive but can be both de-activated:
222 self._zoomAction = self.createAndAddLocalAction("fit_area.png", trQ("FIT_AREA_TXT"))
223 self._zoomAction.triggered.connect(self.zoomArea)
224 self._zoomAction.setCheckable(True)
225 self._panAction = self.createAndAddLocalAction("zoom_pan.png", trQ("ZOOM_PAN_TXT"))
226 self._panAction.triggered.connect(self.pan)
227 self._panAction.setCheckable(True)
228 self._toolbar.addSeparator()
229 # Actions to change the representation of curves
230 self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
231 self._pointsAction = self.createAndAddLocalAction("draw_points.png", trQ("DRAW_POINTS_TXT"))
232 self._pointsAction.setCheckable(True)
233 self._linesAction = self.createAndAddLocalAction("draw_lines.png", trQ("DRAW_LINES_TXT"))
234 self._linesAction.setCheckable(True)
235 self._curveActionGroup.addAction(self._pointsAction)
236 self._curveActionGroup.addAction(self._linesAction)
237 self._linesAction.setChecked(True)
238 self._curveActionGroup.triggered.connect(self.changeModeCurve)
239 self._curveActionGroup.setExclusive(True)
240 self._toolbar.addSeparator()
241 # Actions to draw horizontal curves as linear or logarithmic
242 self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
243 self._horLinearAction = self.createAndAddLocalAction("hor_linear.png", trQ("HOR_LINEAR_TXT"))
244 self._horLinearAction.setCheckable(True)
245 self._horLogarithmicAction = self.createAndAddLocalAction("hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
246 self._horLogarithmicAction.setCheckable(True)
247 self._horActionGroup.addAction(self._horLinearAction)
248 self._horActionGroup.addAction(self._horLogarithmicAction)
249 self._horLinearAction.setChecked(True)
250 self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
251 self._toolbar.addSeparator()
252 # Actions to draw vertical curves as linear or logarithmic
253 self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
254 self._verLinearAction = self.createAndAddLocalAction("ver_linear.png", trQ("VER_LINEAR_TXT"))
255 self._verLinearAction.setCheckable(True)
256 self._verLogarithmicAction = self.createAndAddLocalAction("ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
257 self._verLogarithmicAction.setCheckable(True)
258 self._verActionGroup.addAction(self._verLinearAction)
259 self._verActionGroup.addAction(self._verLogarithmicAction)
260 self._verLinearAction.setChecked(True)
261 self._verActionGroup.triggered.connect(self.onViewVerticalMode)
262 self._verActionGroup.setExclusive(True)
263 self._toolbar.addSeparator()
264 # Action to show or hide the legend
265 self._actionLegend = self.createAndAddLocalAction("legend.png", trQ("SHOW_LEGEND_TXT"))
266 self._actionLegend.setCheckable(True)
267 self._actionLegend.triggered.connect(self.showHideLegend)
268 if self._DEFAULT_LEGEND_STATE:
269 self._actionLegend.setChecked(True)
270 self._toolbar.addSeparator()
271 # Action to set the preferences
272 a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
273 a.triggered.connect(self.onSettings)
277 # Choice of the view backup file
279 for form in ["IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES", "ENCAPSULATED_POSTSCRIPT_FILES"]:
280 filters.append(trQ(form))
281 fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(),
284 trQ("DUMP_VIEW_FILE"),
288 self._mplAxes.figure.savefig(name)
291 def autoFit(self, check=True, repaint=True):
292 if self.__repaintOK():
293 self._mplAxes.relim()
294 xm, xM = self._mplAxes.xaxis.get_data_interval()
295 ym, yM = self._mplAxes.yaxis.get_data_interval()
297 self._mplAxes.axis([xm, xM, ym-i*self.AUTOFIT_MARGIN, yM+i*self.AUTOFIT_MARGIN])
302 if self._panAction.isChecked() and self._zoomAction.isChecked():
303 self._panAction.setChecked(False)
304 # Trigger underlying matplotlib action:
305 self._mplNavigationActions["Zoom"].trigger()
308 if self._panAction.isChecked() and self._zoomAction.isChecked():
309 self._zoomAction.setChecked(False)
310 # Trigger underlying matplotlib action:
311 self._mplNavigationActions["Pan"].trigger()
313 def getMarker(self, go_next=False):
315 self._lastMarkerID = (self._lastMarkerID+1) % len(self.CURVE_MARKERS)
316 return self.CURVE_MARKERS[self._lastMarkerID]
318 def changeModeCurve(self, repaint=True):
319 if not self.__repaintOK():
321 action = self._curveActionGroup.checkedAction()
322 if action is self._pointsAction :
323 for crv_view in list(self._curveViews.values()):
324 crv_view.setLineStyle("None")
325 elif action is self._linesAction :
326 for crv_view in list(self._curveViews.values()):
327 crv_view.setLineStyle("-")
329 raise NotImplementedError
333 def setXLog(self, log, repaint=True):
334 if not self.__repaintOK():
336 self._blockLogSignal = True
338 self._mplAxes.set_xscale('log')
339 self._horLogarithmicAction.setChecked(True)
341 self._mplAxes.set_xscale('linear')
342 self._horLinearAction.setChecked(True)
346 self._blockLogSignal = False
348 def setYLog(self, log, repaint=True):
349 if not self.__repaintOK():
351 self._blockLogSignal = True
353 self._mplAxes.set_yscale('log')
354 self._verLogarithmicAction.setChecked(True)
356 self._mplAxes.set_yscale('linear')
357 self._verLinearAction.setChecked(True)
361 self._blockLogSignal = False
363 def setXSciNotation(self, sciNotation, repaint=True):
364 self._axisXSciNotation = sciNotation
365 self.changeFormatAxis()
369 def setYSciNotation(self, sciNotation, repaint=True):
370 self._axisYSciNotation = sciNotation
371 self.changeFormatAxis()
375 def onViewHorizontalMode(self, checked=True, repaint=True):
376 if self._blockLogSignal:
378 action = self._horActionGroup.checkedAction()
379 if action is self._horLinearAction:
380 self.setXLog(False, repaint)
381 elif action is self._horLogarithmicAction:
382 self.setXLog(True, repaint)
384 raise NotImplementedError
386 def onViewVerticalMode(self, checked=True, repaint=True):
387 if self._blockLogSignal:
389 action = self._verActionGroup.checkedAction()
390 if action is self._verLinearAction:
391 self.setYLog(False, repaint)
392 elif action is self._verLogarithmicAction:
393 self.setYLog(True, repaint)
395 raise NotImplementedError
399 def __adjustFigureMargins(self, withLegend):
400 """ Adjust figure margins to make room for the legend """
403 bbox = leg.get_window_extent()
404 # In axes coordinates:
405 bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
406 if self._legendLoc == "right":
407 self._mplFigure.subplots_adjust(right=1.0-(bbox2.width+0.02))
408 elif self._legendLoc == "bottom":
409 self._mplFigure.subplots_adjust(bottom=bbox2.height+0.1)
411 # Reset to default (rc) values
412 self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)
414 def setLegendVisible(self, visible, repaint=True):
415 if visible and not self._actionLegend.isChecked():
416 self._actionLegend.setChecked(True)
417 self.showHideLegend(repaint=repaint)
418 if not visible and self._actionLegend.isChecked():
419 self._actionLegend.setChecked(False)
420 self.showHideLegend(repaint=repaint)
422 def showHideLegend(self, actionChecked=None, repaint=True):
423 if not self.__repaintOK(): # Show/hide legend is extremely costly
426 show = self._actionLegend.isChecked()
427 nCurves = len(self._curveViews)
428 if nCurves > 10: fontSize = 'x-small'
429 else: fontSize = None
433 leg = self._mplAxes.legend()
434 if leg is not None: leg.remove()
435 if show and nCurves > 0:
436 # Recreate legend from scratch
437 if self._legend is not None:
439 self._mplAxes._legend = None
440 if self._legendLoc == "bottom":
441 self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(0.0, -0.05, 1.0, -0.05),
442 borderaxespad=0.0, mode="expand", fancybox=True,
443 shadow=True, ncol=3, prop={'size':fontSize, 'style': 'italic'})
444 elif self._legendLoc == "right":
445 self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(1.02,1.0), borderaxespad=0.0,
446 ncol=1, fancybox=True, shadow=True, prop={'size':fontSize, 'style': 'italic'})
448 raise Exception("Invalid legend placement! Must be 'bottom' or 'right'")
449 # Canvas must be drawn so we can adjust the figure placement:
450 self._mplCanvas.draw()
451 self.__adjustFigureMargins(withLegend=True)
453 if self._legend is None:
457 self._legend.set_visible(False)
459 self._mplAxes._legend = None
460 self._mplCanvas.draw()
461 self.__adjustFigureMargins(withLegend=False)
463 curr_crv = self._model._currentCurve
464 if curr_crv is None: curr_title = None
465 else: curr_title = curr_crv.getTitle()
466 if self._legend is not None:
467 for label in self._legend.get_texts() :
468 text = label.get_text()
469 if (text == curr_title):
470 label.set_backgroundcolor('0.85')
472 label.set_backgroundcolor('white')
477 def onSettings(self, trigger=False, dlg_test=None):
478 dlg = dlg_test or PlotSettings()
479 dlg.titleEdit.setText(self._mplAxes.get_title())
480 dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
481 dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
482 dlg.gridCheckBox.setChecked(self._mplAxes.xaxis._gridOnMajor) # could not find a relevant API to check this
483 dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
484 dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
485 xmin, xmax = self._mplAxes.get_xlim()
486 ymin, ymax = self._mplAxes.get_ylim()
487 xminText = "%g" %xmin
488 xmaxText = "%g" %xmax
489 yminText = "%g" %ymin
490 ymaxText = "%g" %ymax
491 dlg.axisXMinEdit.setText(xminText)
492 dlg.axisXMaxEdit.setText(xmaxText)
493 dlg.axisYMinEdit.setText(yminText)
494 dlg.axisYMaxEdit.setText(ymaxText)
496 dlg.markerCurve.clear()
497 for marker in self.CURVE_MARKERS :
498 dlg.markerCurve.addItem(marker)
499 curr_crv = self._model.getCurrentCurve()
500 if not curr_crv is None:
501 dlg.colorCurve.setEnabled(True)
502 dlg.markerCurve.setEnabled(True)
503 name = curr_crv.getTitle()
504 dlg.nameCurve.setText(name)
505 view = self._curveViews[curr_crv.getID()]
506 marker = view.getMarker()
507 color = view.getColor()
508 index = dlg.markerCurve.findText(marker)
509 dlg.markerCurve.setCurrentIndex(index)
510 rgb = colors.colorConverter.to_rgb(color)
511 dlg.setRGB(rgb[0],rgb[1],rgb[2])
513 dlg.colorCurve.setEnabled(False)
514 dlg.markerCurve.setEnabled(False)
515 dlg.nameCurve.setText("")
517 if self._legend is None:
518 dlg.showLegendCheckBox.setChecked(False)
519 dlg.legendPositionComboBox.setEnabled(False)
521 if self._legend.get_visible():
522 dlg.showLegendCheckBox.setChecked(True)
523 dlg.legendPositionComboBox.setEnabled(True)
524 if self._legendLoc == "bottom":
525 dlg.legendPositionComboBox.setCurrentIndex(0)
526 elif self._legendLoc == "right" :
527 dlg.legendPositionComboBox.setCurrentIndex(1)
529 dlg.showLegendCheckBox.setChecked(False)
530 dlg.legendPositionComboBox.setEnabled(False)
534 self._model.setTitle(dlg.titleEdit.text())
536 self._model.setXLabel(dlg.axisXTitleEdit.text())
537 self._model.setYLabel(dlg.axisYTitleEdit.text())
539 if dlg.gridCheckBox.isChecked() :
540 self._mplAxes.grid(True)
542 self._mplAxes.grid(False)
544 if dlg.showLegendCheckBox.isChecked():
545 self._actionLegend.setChecked(True)
546 if dlg.legendPositionComboBox.currentIndex() == 0 :
547 self._legendLoc = "bottom"
548 elif dlg.legendPositionComboBox.currentIndex() == 1 :
549 self._legendLoc = "right"
551 self._actionLegend.setChecked(False)
552 xminText = dlg.axisXMinEdit.text()
553 xmaxText = dlg.axisXMaxEdit.text()
554 yminText = dlg.axisYMinEdit.text()
555 ymaxText = dlg.axisYMaxEdit.text()
556 self._mplAxes.axis([float(xminText), float(xmaxText), float(yminText), float(ymaxText)] )
557 self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
558 self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
559 self.changeFormatAxis()
560 # Color and marker of the curve
562 view.setColor(dlg.getRGB())
563 view.setMarker(self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
564 self.showHideLegend(repaint=True)
565 self._mplCanvas.draw()
568 def updateViewTitle(self):
570 if self._model._title != "":
571 s = " - %s" % self._model._title
572 title = "CurvePlot (%d)%s" % (self._model.getID(), s)
573 self._sgPyQt.setViewTitle(self._salomeViewID, title)
575 def onCurrentPlotSetChange(self):
576 """ Avoid a unnecessary call to update() when just switching current plot set! """
579 def onCurrentCurveChange(self):
580 curr_crv2 = self._model.getCurrentCurve()
581 if curr_crv2 != self._currCrv:
582 if self._currCrv is not None:
583 view = self._curveViews[self._currCrv.getID()]
584 view.toggleHighlight(False)
585 if not curr_crv2 is None:
586 view = self._curveViews[curr_crv2.getID()]
587 view.toggleHighlight(True)
588 self._currCrv = curr_crv2
589 self.showHideLegend(repaint=False) # redo legend
592 def changeFormatAxis(self) :
593 if not self.__repaintOK():
596 # don't try to switch to sci notation if we are not using the
597 # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
598 if self._horLinearAction.isChecked():
599 if self._axisXSciNotation :
600 self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='x')
602 self._mplAxes.ticklabel_format(style='plain',axis='x')
603 if self._verLinearAction.isChecked():
604 if self._axisYSciNotation :
605 self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='y')
607 self._mplAxes.ticklabel_format(style='plain',axis='y')
610 if self._salomeViewID is None:
611 self.createPlotWidget()
612 self._salomeViewID = self._sgPyQt.createView("CurvePlot", self._plotWidget)
613 Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
614 self._sgPyQt.setViewVisible(self._salomeViewID, True)
616 self.updateViewTitle()
618 # Check list of curve views:
619 set_mod = set(self._model._curves.keys())
620 set_view = set(self._curveViews.keys())
622 # Deleted/Added curves:
623 dels = set_view - set_mod
624 added = set_mod - set_view
629 if not len(self._curveViews):
631 self._mplAxes.set_color_cycle(None)
636 # Axes labels and title
637 self._mplAxes.set_xlabel(self._model._xlabel)
638 self._mplAxes.set_ylabel(self._model._ylabel)
639 self._mplAxes.set_title(self._model._title)
641 self.onViewHorizontalMode(repaint=False)
642 self.onViewVerticalMode(repaint=False)
643 self.changeModeCurve(repaint=False)
644 self.showHideLegend(repaint=False) # The canvas is repainted anyway (needed to get legend bounding box)
645 self.changeFormatAxis()
648 self.autoFit(repaint=False)
651 def onDataChange(self):
652 # the rest is done in the CurveView:
653 self.autoFit(repaint=True)
655 def onMousePress(self, event):
656 if event.button == 3 :
657 if self._panAction.isChecked():
658 self._panAction.setChecked(False)
659 if self._zoomAction.isChecked():
660 self._zoomAction.setChecked(False)
662 def onContextMenu(self, position):
663 pos = self._mplCanvas.mapToGlobal(QtCore.QPoint(position.x(),position.y()))
664 self._popupMenu.exec_(pos)
666 def onScroll(self, event):
667 # Event location (x and y)
671 cur_xlim = self._mplAxes.get_xlim()
672 cur_ylim = self._mplAxes.get_ylim()
675 if event.button == 'down':
677 scale_factor = 1 / base_scale
678 elif event.button == 'up':
680 scale_factor = base_scale
682 # deal with something that should never happen
685 new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
686 new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
688 relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
689 rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
691 self._mplAxes.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
692 self._mplAxes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
697 def onPressEvent(self, event):
698 if event.button == 3 :
699 #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
700 canvasSize = event.canvas.geometry()
701 point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
702 self._popupMenu.exec_(point)
704 print("Press event on the other button")
705 #if event.button == 3 :
706 # canvasSize = event.canvas.geometry()
707 # point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
708 # self._popupMenu.move(point)
709 # self._popupMenu.show()
711 def onMotionEvent(self, event):
712 print("OnMotionEvent ",event.button)
713 #if event.button == 3 :
714 # event.button = None
717 def onReleaseEvent(self, event):
718 print("OnReleaseEvent ",event.button)
719 #if event.button == 3 :
720 # event.button = None