Salome HOME
Merge branch 'master' into V9_dev
[modules/gui.git] / tools / CurvePlot / src / python / views / XYView.py
1 import matplotlib.pyplot as plt
2 import matplotlib.colors as colors
3 from View import View
4 from CurveView import CurveView
5
6 from utils import Logger, trQ
7 from PlotWidget import PlotWidget
8 from PlotSettings import PlotSettings
9 from pyqtside import QtGui, QtCore
10 from pyqtside.QtCore import QObject
11 from matplotlib.figure import Figure
12 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg, NavigationToolbar2QT
13
14 class EventHandler(QObject):
15   """ Handle the right-click properly so that it only triggers the contextual menu """
16   def __init__(self,parent=None):
17     QObject.__init__(self, parent)
18
19   def eventFilter(self, obj, event):
20     if event.type() == QtCore.QEvent.MouseButtonPress:
21       if event.button() == 2:
22         # Discarding right button press to only keep context menu display
23         return True # Event handled (and hence not passed to matplotlib)
24     return QObject.eventFilter(self, obj, event)
25
26 class XYView(View):
27   AUTOFIT_MARGIN = 0.03  # 3%
28
29   # See http://matplotlib.org/api/markers_api.html:
30   CURVE_MARKERS = [ "o" ,#  circle
31                     "*",  # star
32                     "+",  # plus
33                     "x",  # x
34                     "s",  # square
35                     "p",  # pentagon
36                     "h",  # hexagon1
37                     "8",  # octagon
38                     "D",  # diamond
39                     "^",  # triangle_up
40                     "<",  # triangle_left
41                     ">",  # triangle_right
42                     "1",  # tri_down
43                     "2",  # tri_up
44                     "3",  # tri_left
45                     "4",  # tri_right
46                     "v",  # triangle_down
47                     "H",  # hexagon2
48                     "d",  # thin diamond
49                     "",   # NO MARKER
50                    ]
51
52   _DEFAULT_LEGEND_STATE = False   # for test purposes mainly - initial status of the legend
53
54   def __init__(self, controller):
55     View.__init__(self, controller)
56     self._eventHandler = EventHandler()
57
58     self._curveViews = {}    # key: curve (model) ID, value: CurveView
59     self._salomeViewID = None
60     self._mplFigure = None
61     self._mplAxes = None
62     self._mplCanvas = None
63     self._plotWidget = None
64     self._sgPyQt = self._controller._sgPyQt
65     self._toolbar = None
66     self._mplNavigationActions = {}
67     self._toobarMPL = None
68     self._grid = None
69     self._currCrv = None   # current curve selected in the view
70
71     self._legend = None
72     self._legendLoc = "right"  # "right" or "bottom"
73
74     self._fitArea = False
75     self._zoomPan = False
76     self._dragOnDrop = False
77     self._move = False
78
79     self._patch = None
80     self._xdata = None
81     self._ydata = None
82     self._defaultLineStyle = None
83     self._last_point = None
84     self._lastMarkerID = -1
85     self._blockLogSignal = False
86
87     self._axisXSciNotation = False
88     self._axisYSciNotation = False
89     self._prevTitle = None
90
91   def __repaintOK(self):
92     """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
93     @return False if painting is currently locked, in which case it will also register the current XYView 
94     as needing a refresh when unlocked
95     """
96     ret = self._controller._plotManager.isRepaintLocked()
97     if ret:
98       self._controller._plotManager.registerRepaint(self._model)
99     return (not ret)
100
101   def appendCurve(self, curveID):
102     newC = CurveView(self._controller, self)
103     newC.setModel(self._model._curves[curveID])
104     newC.setMPLAxes(self._mplAxes)
105     newC.draw()
106     newC.setMarker(self.getMarker(go_next=True))
107     self._curveViews[curveID] = newC
108
109   def removeCurve(self, curveID):
110     v = self._curveViews.pop(curveID)
111     v.erase()
112     if self._currCrv is not None and self._currCrv.getID() == curveID:
113       self._currCrv = None
114
115   def cleanBeforeClose(self):
116     """ Clean some items to avoid accumulating stuff in memory """
117     self._mplFigure.clear()
118     plt.close(self._mplFigure)
119     self._plotWidget.clearAll()
120     # For memory debugging only:
121     import gc
122     gc.collect()
123
124   def repaint(self):
125     if self.__repaintOK():
126       Logger.Debug("XYView::draw")
127       self._mplCanvas.draw()
128
129   def onXLabelChange(self):
130     if self.__repaintOK():
131       self._mplAxes.set_xlabel(self._model._xlabel)
132       self.repaint()
133
134   def onYLabelChange(self):
135     if self.__repaintOK():
136       self._mplAxes.set_ylabel(self._model._ylabel)
137       self.repaint()
138
139   def onTitleChange(self):
140     if self.__repaintOK():
141       self._mplAxes.set_title(self._model._title)
142       self.updateViewTitle()
143       self.repaint()
144
145   def onCurveTitleChange(self):
146     # Updating the legend should suffice
147     self.showHideLegend()
148
149   def onClearAll(self):
150     """ Just does an update with a reset of the marker cycle. """
151     if self.__repaintOK():
152       self._lastMarkerID = -1
153       self.update()
154
155   def onPick(self, event):
156     """ MPL callback when picking
157     """
158     if event.mouseevent.button == 1:
159       selected_id = -1
160       a = event.artist
161       for crv_id, cv in list(self._curveViews.items()):
162         if cv._mplLines[0] is a:
163           selected_id = crv_id
164       # Use the plotmanager so that other plot sets get their current reset:
165       self._controller._plotManager.setCurrentCurve(selected_id)
166
167   def createAndAddLocalAction(self, icon_file, short_name):
168     return self._toolbar.addAction(self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)
169
170   def createPlotWidget(self):
171     self._mplFigure = Figure((8.0,5.0), dpi=100)
172     self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
173     self._mplCanvas.installEventFilter(self._eventHandler)
174     self._mplCanvas.mpl_connect('pick_event', self.onPick)
175     self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
176     self._plotWidget = PlotWidget()
177     self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None)
178     for act in self._toobarMPL.actions():
179       actionName = str(act.text()).strip()
180       self._mplNavigationActions[actionName] = act
181     self._plotWidget.setCentralWidget(self._mplCanvas)
182     self._toolbar = self._plotWidget.toolBar
183     self.populateToolbar()
184
185     self._popupMenu = QtGui.QMenu()
186     self._popupMenu.addAction(self._actionLegend)
187
188     # Connect evenement for the graphic scene
189     self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
190     self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu)
191     self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
192     self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)
193
194   def populateToolbar(self):
195     # Action to dump view in a file
196     a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
197     a.triggered.connect(self.dumpView)
198     self._toolbar.addSeparator()
199     # Actions to manipulate the scene
200     a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
201     a.triggered.connect(self.autoFit)
202     #    Zoom and pan are mutually exclusive but can be both de-activated:
203     self._zoomAction = self.createAndAddLocalAction("fit_area.png", trQ("FIT_AREA_TXT"))
204     self._zoomAction.triggered.connect(self.zoomArea)
205     self._zoomAction.setCheckable(True)
206     self._panAction = self.createAndAddLocalAction("zoom_pan.png", trQ("ZOOM_PAN_TXT"))
207     self._panAction.triggered.connect(self.pan)
208     self._panAction.setCheckable(True)
209     self._toolbar.addSeparator()
210     # Actions to change the representation of curves
211     self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
212     self._pointsAction = self.createAndAddLocalAction("draw_points.png", trQ("DRAW_POINTS_TXT"))
213     self._pointsAction.setCheckable(True)
214     self._linesAction = self.createAndAddLocalAction("draw_lines.png", trQ("DRAW_LINES_TXT"))
215     self._linesAction.setCheckable(True)
216     self._curveActionGroup.addAction(self._pointsAction)
217     self._curveActionGroup.addAction(self._linesAction)
218     self._linesAction.setChecked(True)
219     self._curveActionGroup.triggered.connect(self.changeModeCurve)
220     self._curveActionGroup.setExclusive(True)
221     self._toolbar.addSeparator()
222     # Actions to draw horizontal curves as linear or logarithmic
223     self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
224     self._horLinearAction = self.createAndAddLocalAction("hor_linear.png", trQ("HOR_LINEAR_TXT"))
225     self._horLinearAction.setCheckable(True)
226     self._horLogarithmicAction = self.createAndAddLocalAction("hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
227     self._horLogarithmicAction.setCheckable(True)
228     self._horActionGroup.addAction(self._horLinearAction)
229     self._horActionGroup.addAction(self._horLogarithmicAction)
230     self._horLinearAction.setChecked(True)
231     self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
232     self._toolbar.addSeparator()
233     # Actions to draw vertical curves as linear or logarithmic
234     self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
235     self._verLinearAction = self.createAndAddLocalAction("ver_linear.png", trQ("VER_LINEAR_TXT"))
236     self._verLinearAction.setCheckable(True)
237     self._verLogarithmicAction = self.createAndAddLocalAction("ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
238     self._verLogarithmicAction.setCheckable(True)
239     self._verActionGroup.addAction(self._verLinearAction)
240     self._verActionGroup.addAction(self._verLogarithmicAction)
241     self._verLinearAction.setChecked(True)
242     self._verActionGroup.triggered.connect(self.onViewVerticalMode)
243     self._verActionGroup.setExclusive(True)
244     self._toolbar.addSeparator()
245     # Action to show or hide the legend
246     self._actionLegend = self.createAndAddLocalAction("legend.png", trQ("SHOW_LEGEND_TXT"))
247     self._actionLegend.setCheckable(True)
248     self._actionLegend.triggered.connect(self.showHideLegend)
249     if self._DEFAULT_LEGEND_STATE:
250       self._actionLegend.setChecked(True)
251     self._toolbar.addSeparator()
252     # Action to set the preferences
253     a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
254     a.triggered.connect(self.onSettings)
255     pass
256
257   def dumpView(self):
258     # Choice of the view backup file
259     filters = []
260     for form in ["IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES", "ENCAPSULATED_POSTSCRIPT_FILES"]:
261       filters.append(trQ(form))
262     fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(),
263                                         "",
264                                         filters,
265                                         trQ("DUMP_VIEW_FILE"),
266                                         False )
267     name = str(fileName)
268     if name != "":
269       self._mplAxes.figure.savefig(name)
270     pass
271
272   def autoFit(self, check=True, repaint=True):
273     if self.__repaintOK():
274       self._mplAxes.relim()
275       xm, xM = self._mplAxes.xaxis.get_data_interval()
276       ym, yM = self._mplAxes.yaxis.get_data_interval()
277       i = yM-ym
278       self._mplAxes.axis([xm, xM, ym-i*self.AUTOFIT_MARGIN, yM+i*self.AUTOFIT_MARGIN])
279       if repaint:
280         self.repaint()
281
282   def zoomArea(self):
283     if self._panAction.isChecked() and self._zoomAction.isChecked():
284       self._panAction.setChecked(False)
285     # Trigger underlying matplotlib action:
286     self._mplNavigationActions["Zoom"].trigger()
287
288   def pan(self):
289     if self._panAction.isChecked() and self._zoomAction.isChecked():
290       self._zoomAction.setChecked(False)
291     # Trigger underlying matplotlib action:
292     self._mplNavigationActions["Pan"].trigger()
293
294   def getMarker(self, go_next=False):
295     if go_next:
296       self._lastMarkerID = (self._lastMarkerID+1) % len(self.CURVE_MARKERS)
297     return self.CURVE_MARKERS[self._lastMarkerID]
298
299   def changeModeCurve(self, repaint=True):
300     if not self.__repaintOK():
301       return
302     action = self._curveActionGroup.checkedAction()
303     if action is self._pointsAction :
304       for crv_view in list(self._curveViews.values()):
305         crv_view.setLineStyle("None")
306     elif action is self._linesAction :
307       for crv_view in list(self._curveViews.values()):
308         crv_view.setLineStyle("-")
309     else :
310       raise NotImplementedError
311     if repaint:
312       self.repaint()
313
314   def setXLog(self, log, repaint=True):
315     if not self.__repaintOK():
316       return
317     self._blockLogSignal = True
318     if log:
319       self._mplAxes.set_xscale('log')
320       self._horLogarithmicAction.setChecked(True)
321     else:
322       self._mplAxes.set_xscale('linear')
323       self._horLinearAction.setChecked(True)
324     if repaint:
325       self.autoFit()
326       self.repaint()
327     self._blockLogSignal = False
328
329   def setYLog(self, log, repaint=True):
330     if not self.__repaintOK():
331       return
332     self._blockLogSignal = True
333     if log:
334       self._mplAxes.set_yscale('log')
335       self._verLogarithmicAction.setChecked(True)
336     else:
337       self._mplAxes.set_yscale('linear')
338       self._verLinearAction.setChecked(True)
339     if repaint:
340       self.autoFit()
341       self.repaint()
342     self._blockLogSignal = False
343
344   def setXSciNotation(self, sciNotation, repaint=True):
345     self._axisXSciNotation = sciNotation
346     self.changeFormatAxis()
347     if repaint:
348       self.repaint()
349
350   def setYSciNotation(self, sciNotation, repaint=True):
351     self._axisYSciNotation = sciNotation
352     self.changeFormatAxis()
353     if repaint:
354       self.repaint()
355
356   def onViewHorizontalMode(self, checked=True, repaint=True):
357     if self._blockLogSignal:
358       return
359     action = self._horActionGroup.checkedAction()
360     if action is self._horLinearAction:
361       self.setXLog(False, repaint)
362     elif action is self._horLogarithmicAction:
363       self.setXLog(True, repaint)
364     else:
365       raise NotImplementedError
366
367   def onViewVerticalMode(self, checked=True, repaint=True):
368     if self._blockLogSignal:
369       return
370     action = self._verActionGroup.checkedAction()
371     if action is self._verLinearAction:
372       self.setYLog(False, repaint)
373     elif action is self._verLogarithmicAction:
374       self.setYLog(True, repaint)
375     else:
376       raise NotImplementedError
377     if repaint:
378       self.repaint()
379
380   def __adjustFigureMargins(self, withLegend):
381     """ Adjust figure margins to make room for the legend """
382     if withLegend:
383       leg = self._legend
384       bbox = leg.get_window_extent()
385       # In axes coordinates:
386       bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
387       if self._legendLoc == "right":
388         self._mplFigure.subplots_adjust(right=1.0-(bbox2.width+0.02))
389       elif self._legendLoc == "bottom":
390         self._mplFigure.subplots_adjust(bottom=bbox2.height+0.1)
391     else:
392       # Reset to default (rc) values
393       self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)
394
395   def setLegendVisible(self, visible, repaint=True):
396     if visible and not self._actionLegend.isChecked():
397       self._actionLegend.setChecked(True)
398       self.showHideLegend(repaint=repaint)
399     if not visible and self._actionLegend.isChecked():
400       self._actionLegend.setChecked(False)
401       self.showHideLegend(repaint=repaint)
402
403   def showHideLegend(self, actionChecked=None, repaint=True):
404     if not self.__repaintOK():  # Show/hide legend is extremely costly
405       return
406
407     show = self._actionLegend.isChecked()
408     nCurves = len(self._curveViews)
409     if nCurves > 10: fontSize = 'x-small'
410     else:            fontSize = None
411
412     if nCurves == 0:
413       # Remove legend
414       leg = self._mplAxes.legend()
415       if leg is not None: leg.remove()
416     if show and nCurves > 0:
417       # Recreate legend from scratch
418       if self._legend is not None:
419         self._legend = None
420         self._mplAxes._legend = None
421       if self._legendLoc == "bottom":
422         self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(0.0, -0.05, 1.0, -0.05),
423                                             borderaxespad=0.0, mode="expand", fancybox=True,
424                                             shadow=True, ncol=3, prop={'size':fontSize, 'style': 'italic'})
425       elif self._legendLoc == "right":
426         self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(1.02,1.0), borderaxespad=0.0,
427                                             ncol=1, fancybox=True, shadow=True, prop={'size':fontSize, 'style': 'italic'})
428       else:
429         raise Exception("Invalid legend placement! Must be 'bottom' or 'right'")
430       # Canvas must be drawn so we can adjust the figure placement:
431       self._mplCanvas.draw()
432       self.__adjustFigureMargins(withLegend=True)
433     else:
434       if self._legend is None:
435         # Nothing to do
436         return
437       else:
438         self._legend.set_visible(False)
439         self._legend = None
440         self._mplAxes._legend = None
441         self._mplCanvas.draw()
442         self.__adjustFigureMargins(withLegend=False)
443
444     curr_crv = self._model._currentCurve
445     if curr_crv is None: curr_title = None
446     else:                curr_title = curr_crv.getTitle()
447     if self._legend is not None:
448       for label in self._legend.get_texts() :
449         text = label.get_text()
450         if (text == curr_title):
451           label.set_backgroundcolor('0.85')
452         else :
453           label.set_backgroundcolor('white')
454
455     if repaint:
456       self.repaint()
457
458   def onSettings(self, trigger=False, dlg_test=None):
459     dlg = dlg_test or PlotSettings()
460     dlg.titleEdit.setText(self._mplAxes.get_title())
461     dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
462     dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
463     dlg.gridCheckBox.setChecked(self._mplAxes.xaxis._gridOnMajor)  # could not find a relevant API to check this
464     dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
465     dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
466     xmin, xmax = self._mplAxes.get_xlim()
467     ymin, ymax = self._mplAxes.get_ylim()
468     xminText = "%g" %xmin
469     xmaxText = "%g" %xmax
470     yminText = "%g" %ymin
471     ymaxText = "%g" %ymax
472     dlg.axisXMinEdit.setText(xminText)
473     dlg.axisXMaxEdit.setText(xmaxText)
474     dlg.axisYMinEdit.setText(yminText)
475     dlg.axisYMaxEdit.setText(ymaxText)
476     # List of markers
477     dlg.markerCurve.clear()
478     for marker in self.CURVE_MARKERS :
479       dlg.markerCurve.addItem(marker)
480     curr_crv = self._model.getCurrentCurve()
481     if not curr_crv is None:
482       dlg.colorCurve.setEnabled(True)
483       dlg.markerCurve.setEnabled(True)
484       name = curr_crv.getTitle()
485       dlg.nameCurve.setText(name)
486       view = self._curveViews[curr_crv.getID()]
487       marker = view.getMarker()
488       color = view.getColor()
489       index = dlg.markerCurve.findText(marker)
490       dlg.markerCurve.setCurrentIndex(index)
491       rgb = colors.colorConverter.to_rgb(color)
492       dlg.setRGB(rgb[0],rgb[1],rgb[2])
493     else :
494       dlg.colorCurve.setEnabled(False)
495       dlg.markerCurve.setEnabled(False)
496       dlg.nameCurve.setText("")
497       view = None
498     if self._legend is None:
499       dlg.showLegendCheckBox.setChecked(False)
500       dlg.legendPositionComboBox.setEnabled(False)
501     else :
502       if self._legend.get_visible():
503         dlg.showLegendCheckBox.setChecked(True)
504         dlg.legendPositionComboBox.setEnabled(True)
505         if self._legendLoc == "bottom":
506           dlg.legendPositionComboBox.setCurrentIndex(0)
507         elif self._legendLoc == "right" :
508           dlg.legendPositionComboBox.setCurrentIndex(1)
509       else :
510         dlg.showLegendCheckBox.setChecked(False)
511         dlg.legendPositionComboBox.setEnabled(False)
512
513     if dlg.exec_():
514       # Title
515       self._model.setTitle(dlg.titleEdit.text())
516       # Axis
517       self._model.setXLabel(dlg.axisXTitleEdit.text())
518       self._model.setYLabel(dlg.axisYTitleEdit.text())
519       # Grid
520       if dlg.gridCheckBox.isChecked() :
521         self._mplAxes.grid(True)
522       else :
523         self._mplAxes.grid(False)
524       # Legend
525       if  dlg.showLegendCheckBox.isChecked():
526         self._actionLegend.setChecked(True)
527         if dlg.legendPositionComboBox.currentIndex() == 0 :
528           self._legendLoc = "bottom"
529         elif dlg.legendPositionComboBox.currentIndex() == 1 :
530           self._legendLoc = "right"
531       else :
532         self._actionLegend.setChecked(False)
533       xminText = dlg.axisXMinEdit.text()
534       xmaxText = dlg.axisXMaxEdit.text()
535       yminText = dlg.axisYMinEdit.text()
536       ymaxText = dlg.axisYMaxEdit.text()
537       self._mplAxes.axis([float(xminText), float(xmaxText), float(yminText), float(ymaxText)] )
538       self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
539       self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
540       self.changeFormatAxis()
541       # Color and marker of the curve
542       if view:
543         view.setColor(dlg.getRGB())
544         view.setMarker(self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
545       self.showHideLegend(repaint=True)
546       self._mplCanvas.draw()
547     pass
548
549   def updateViewTitle(self):
550     s = ""
551     if self._model._title != "":
552       s = " - %s" % self._model._title
553     title = "CurvePlot (%d)%s" % (self._model.getID(), s)
554     self._sgPyQt.setViewTitle(self._salomeViewID, title)
555
556   def onCurrentPlotSetChange(self):
557     """ Avoid a unnecessary call to update() when just switching current plot set! """
558     pass
559
560   def onCurrentCurveChange(self):
561     curr_crv2 = self._model.getCurrentCurve()
562     if curr_crv2 != self._currCrv:
563       if self._currCrv is not None:
564         view = self._curveViews[self._currCrv.getID()]
565         view.toggleHighlight(False)
566       if not curr_crv2 is None:
567         view = self._curveViews[curr_crv2.getID()]
568         view.toggleHighlight(True)
569       self._currCrv = curr_crv2
570       self.showHideLegend(repaint=False) # redo legend
571       self.repaint()
572
573   def changeFormatAxis(self) :
574     if not self.__repaintOK():
575       return
576
577     # don't try to switch to sci notation if we are not using the
578     # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
579     if self._horLinearAction.isChecked():
580       if self._axisXSciNotation :
581         self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='x')
582       else :
583         self._mplAxes.ticklabel_format(style='plain',axis='x')
584     if self._verLinearAction.isChecked():
585       if self._axisYSciNotation :
586         self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='y')
587       else :
588         self._mplAxes.ticklabel_format(style='plain',axis='y')
589
590   def update(self):
591     if self._salomeViewID is None:
592       self.createPlotWidget()
593       self._salomeViewID = self._sgPyQt.createView("CurvePlot", self._plotWidget)
594       Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
595       self._sgPyQt.setViewVisible(self._salomeViewID, True)
596
597     self.updateViewTitle()
598
599     # Check list of curve views:
600     set_mod = set(self._model._curves.keys())
601     set_view = set(self._curveViews.keys())
602
603     # Deleted/Added curves:
604     dels = set_view - set_mod
605     added = set_mod - set_view
606
607     for d in dels:
608       self.removeCurve(d)
609
610     if not len(self._curveViews):
611       # Reset color cycle
612       self._mplAxes.set_color_cycle(None)
613
614     for a in added:
615       self.appendCurve(a)
616
617     # Axes labels and title
618     self._mplAxes.set_xlabel(self._model._xlabel)
619     self._mplAxes.set_ylabel(self._model._ylabel)
620     self._mplAxes.set_title(self._model._title)
621
622     self.onViewHorizontalMode(repaint=False)
623     self.onViewVerticalMode(repaint=False)
624     self.changeModeCurve(repaint=False)
625     self.showHideLegend(repaint=False)   # The canvas is repainted anyway (needed to get legend bounding box)
626     self.changeFormatAxis()
627
628     # Redo auto-fit
629     self.autoFit(repaint=False)
630     self.repaint()
631
632   def onDataChange(self):
633     # the rest is done in the CurveView:
634     self.autoFit(repaint=True)
635
636   def onMousePress(self, event):
637     if event.button == 3 :
638       if self._panAction.isChecked():
639         self._panAction.setChecked(False)
640       if self._zoomAction.isChecked():
641         self._zoomAction.setChecked(False)
642
643   def onContextMenu(self, position):
644     pos = self._mplCanvas.mapToGlobal(QtCore.QPoint(position.x(),position.y()))
645     self._popupMenu.exec_(pos)
646
647   def onScroll(self, event):
648     # Event location (x and y)
649     xdata = event.xdata
650     ydata = event.ydata
651
652     cur_xlim = self._mplAxes.get_xlim()
653     cur_ylim = self._mplAxes.get_ylim()
654
655     base_scale = 2.
656     if event.button == 'down':
657       # deal with zoom in
658       scale_factor = 1 / base_scale
659     elif event.button == 'up':
660       # deal with zoom out
661       scale_factor = base_scale
662     else:
663       # deal with something that should never happen
664       scale_factor = 1
665
666     new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
667     new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor
668
669     relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
670     rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])
671
672     self._mplAxes.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
673     self._mplAxes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
674
675     self.repaint()
676     pass
677
678   def onPressEvent(self, event):
679     if event.button == 3 :
680       #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
681       canvasSize = event.canvas.geometry()
682       point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
683       self._popupMenu.exec_(point)
684     else :
685       print("Press event on the other button")
686     #if event.button == 3 :
687     #  canvasSize = event.canvas.geometry()
688     #  point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
689     #  self._popupMenu.move(point)
690     #  self._popupMenu.show()
691
692   def onMotionEvent(self, event):
693     print("OnMotionEvent ",event.button)
694     #if event.button == 3 :
695     #  event.button = None
696     #  return True
697
698   def onReleaseEvent(self, event):
699     print("OnReleaseEvent ",event.button)
700     #if event.button == 3 :
701     #  event.button = None
702     #  return False