예제 #1
0
 def __init__(self, parent=None, width=9, height=6, dpi=None):
     self.fig = Figure(figsize=(width, height), facecolor=(.94,.94,.94), dpi=dpi)
     FigureCanvas.__init__(self, self.fig)
     self.setParent(parent)    
     FigureCanvas.setSizePolicy(self,
                                QSizePolicy.Expanding,
                                QSizePolicy.Expanding)
     FigureCanvas.updateGeometry(self)
     
     self.axes = self.fig.add_axes([0.06, 0.10, 0.9, 0.85], facecolor=(.94,.94,.94))
     
     self.compute_initial_figure()
     
     # horizontal x range span
     axxspan = self.fig.add_axes([0.06, 0.05, 0.9, 0.02])
     axxspan.axis([-0.2, 1.2, -0.2, 1.2])
     axxspan.tick_params('y', labelright = False ,labelleft = False ,length=0)
     self.xspan = SpanSelector(axxspan, self.onselectX, 'horizontal',useblit=True, span_stays=True,  rectprops=dict(alpha=0.5, facecolor='blue'))
     
     # vertical y range span
     axyspan = self.fig.add_axes([0.02, 0.10, 0.01, 0.85])
     axyspan.axis([-0.2, 1.2, -0.2, 1.2])
     axyspan.tick_params('x', labelbottom = False ,labeltop = False ,length=0)
     self.yspan = SpanSelector(axyspan, self.onselectY, 'vertical',useblit=True, span_stays=True,  rectprops=dict(alpha=0.5, facecolor='blue'))
     # reset x y spans
     axReset = self.fig.add_axes([0.01, 0.05, 0.03, 0.03],frameon=False, )
     self.bnReset = Button(axReset, 'Reset')
     self.bnReset.on_clicked( self.xyReset )
     # contextMenu
     acExportPlot = QAction(self.tr("Export plot"), self)
     FigureCanvas.connect(acExportPlot,SIGNAL('triggered()'), self, SLOT('exportPlot()') )
     FigureCanvas.addAction(self, acExportPlot )
     FigureCanvas.setContextMenuPolicy(self, Qt.ActionsContextMenu )
예제 #2
0
    def __init__(self, parent=None, width=9, height=6, dpi=None):
        self.fig = Figure(figsize=(width, height), facecolor=(.94,.94,.94), dpi=dpi)

        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)    
        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)
        
        # contextMenu
        acExportPlot = QAction(self.tr("Export plot"), self)
        FigureCanvas.connect(acExportPlot,SIGNAL('triggered()'), self, SLOT('exportPlot()') )
        FigureCanvas.addAction(self, acExportPlot )
        FigureCanvas.setContextMenuPolicy(self, Qt.ActionsContextMenu )
예제 #3
0
    def __init__(self, parent=None, width=9, height=6, dpi=None):
        self.fig = Figure(figsize=(width, height),
                          facecolor=(.94, .94, .94),
                          dpi=dpi)

        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)
        FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        # contextMenu
        acExportPlot = QAction(self.tr("Export plot"), self)
        FigureCanvas.connect(acExportPlot, SIGNAL('triggered()'), self,
                             SLOT('exportPlot()'))
        FigureCanvas.addAction(self, acExportPlot)
        FigureCanvas.setContextMenuPolicy(self, Qt.ActionsContextMenu)
예제 #4
0
class XYView(View):
  AUTOFIT_MARGIN = 0.03  # 3%
  
  # See http://matplotlib.org/api/markers_api.html:
  CURVE_MARKERS = [ "o" ,#  circle
                    "*",  # star
                    "+",  # plus
                    "x",  # x
                    "s",  # square
                    "p",  # pentagon
                    "h",  # hexagon1
                    "8",  # octagon
                    "D",  # diamond
                    "^",  # triangle_up
                    "<",  # triangle_left
                    ">",  # triangle_right
                    "1",  # tri_down
                    "2",  # tri_up
                    "3",  # tri_left
                    "4",  # tri_right
                    "v",  # triangle_down
                    "H",  # hexagon2
                    "d",  # thin diamond
                    "",   # NO MARKER
                   ]
  
  _DEFAULT_LEGEND_STATE = False   # for test purposes mainly - initial status of the legend
  
  def __init__(self, controller):
    View.__init__(self, controller)
    self._eventHandler = EventHandler()
    
    self._curveViews = {}    # key: curve (model) ID, value: CurveView
    self._salomeViewID = None
    self._mplFigure = None
    self._mplAxes = None
    self._mplCanvas = None
    self._plotWidget = None
    self._sgPyQt = self._controller._sgPyQt
    self._toolbar = None
    self._mplNavigationActions = {}
    self._toobarMPL = None
    self._grid = None
    self._currCrv = None   # current curve selected in the view
    
    self._legend = None
    self._legendLoc = "right"  # "right" or "bottom"
    
    self._fitArea = False
    self._zoomPan = False
    self._dragOnDrop = False
    self._move = False
    
    self._patch = None
    self._xdata = None
    self._ydata = None
    self._defaultLineStyle = None
    self._last_point = None
    self._lastMarkerID = -1
    self._blockLogSignal = False
    
    self._axisXSciNotation = False
    self._axisYSciNotation = False
    self._prevTitle = None
    
  def __repaintOK(self):
    """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
    @return False if painting is currently locked, in which case it will also register the current XYView 
    as needing a refresh when unlocked
    """
    ret = self._controller._plotManager.isRepaintLocked()
    if ret:
      self._controller._plotManager.registerRepaint(self._model)
    return (not ret)
    
  def appendCurve(self, curveID):
    newC = CurveView(self._controller, self)
    newC.setModel(self._model._curves[curveID])
    newC.setMPLAxes(self._mplAxes) 
    newC.draw()
    newC.setMarker(self.getMarker(go_next=True))
    self._curveViews[curveID] = newC 
    
  def removeCurve(self, curveID):
    v = self._curveViews.pop(curveID)
    v.erase()
    if self._currCrv is not None and self._currCrv.getID() == curveID:
      self._currCrv = None
  
  def cleanBeforeClose(self):
    """ Clean some items to avoid accumulating stuff in memory """
    self._mplFigure.clear()
    plt.close(self._mplFigure)
    self._plotWidget.clearAll()
    # For memory debugging only:
    import gc
    gc.collect()
  
  def repaint(self):
    if self.__repaintOK():
      Logger.Debug("XYView::draw")
      self._mplCanvas.draw()

  def onXLabelChange(self):
    if self.__repaintOK():
      self._mplAxes.set_xlabel(self._model._xlabel)
      self.repaint()
    
  def onYLabelChange(self):
    if self.__repaintOK():
      self._mplAxes.set_ylabel(self._model._ylabel)
      self.repaint()
  
  def onTitleChange(self):
    if self.__repaintOK():
      self._mplAxes.set_title(self._model._title)
      self.updateViewTitle()
      self.repaint()
  
  def onCurveTitleChange(self):
    # Updating the legend should suffice
    self.showHideLegend()
  
  def onClearAll(self):
    """ Just does an update with a reset of the marker cycle. """
    if self.__repaintOK():
      self._lastMarkerID = -1
      self.update()
  
  def onPick(self, event):
    """ MPL callback when picking
    """
    if event.mouseevent.button == 1:
      selected_id = -1
      a = event.artist
      for crv_id, cv in self._curveViews.items():
        if cv._mplLines[0] is a:
          selected_id = crv_id
      # Use the plotmanager so that other plot sets get their current reset:
      self._controller._plotManager.setCurrentCurve(selected_id)
  
  def createAndAddLocalAction(self, icon_file, short_name):
    return self._toolbar.addAction(self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)
    
  def createPlotWidget(self):
    self._mplFigure = Figure((8.0,5.0), dpi=100)
    self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
    self._mplCanvas.installEventFilter(self._eventHandler)
    self._mplCanvas.mpl_connect('pick_event', self.onPick)
    self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
    self._plotWidget = PlotWidget()
    self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None) 
    for act in self._toobarMPL.actions():
      actionName = str(act.text()).strip()
      self._mplNavigationActions[actionName] = act
    self._plotWidget.setCentralWidget(self._mplCanvas)
    self._toolbar = self._plotWidget.toolBar
    self.populateToolbar()
     
    self._popupMenu = QtGui.QMenu()
    self._popupMenu.addAction(self._actionLegend)
    
    # Connect evenement for the graphic scene
    self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
    self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu) 
    self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
    self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)
  
  def populateToolbar(self):
    # Action to dump view in a file
    a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
    a.triggered.connect(self.dumpView)
    self._toolbar.addSeparator()
    # Actions to manipulate the scene
    a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
    a.triggered.connect(self.autoFit)
    #    Zoom and pan are mutually exclusive but can be both de-activated: 
    self._zoomAction = self.createAndAddLocalAction("fit_area.png", trQ("FIT_AREA_TXT"))
    self._zoomAction.triggered.connect(self.zoomArea)
    self._zoomAction.setCheckable(True)
    self._panAction = self.createAndAddLocalAction("zoom_pan.png", trQ("ZOOM_PAN_TXT"))
    self._panAction.triggered.connect(self.pan)
    self._panAction.setCheckable(True)
    self._toolbar.addSeparator()
    # Actions to change the representation of curves
    self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._pointsAction = self.createAndAddLocalAction("draw_points.png", trQ("DRAW_POINTS_TXT"))
    self._pointsAction.setCheckable(True)
    self._linesAction = self.createAndAddLocalAction("draw_lines.png", trQ("DRAW_LINES_TXT"))
    self._linesAction.setCheckable(True)
    self._curveActionGroup.addAction(self._pointsAction)
    self._curveActionGroup.addAction(self._linesAction)
    self._linesAction.setChecked(True)
    self._curveActionGroup.triggered.connect(self.changeModeCurve)
    self._curveActionGroup.setExclusive(True)
    self._toolbar.addSeparator()
    # Actions to draw horizontal curves as linear or logarithmic
    self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._horLinearAction = self.createAndAddLocalAction("hor_linear.png", trQ("HOR_LINEAR_TXT"))
    self._horLinearAction.setCheckable(True)
    self._horLogarithmicAction = self.createAndAddLocalAction("hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
    self._horLogarithmicAction.setCheckable(True)
    self._horActionGroup.addAction(self._horLinearAction)
    self._horActionGroup.addAction(self._horLogarithmicAction)
    self._horLinearAction.setChecked(True)
    self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
    self._toolbar.addSeparator()
    # Actions to draw vertical curves as linear or logarithmic
    self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._verLinearAction = self.createAndAddLocalAction("ver_linear.png", trQ("VER_LINEAR_TXT"))
    self._verLinearAction.setCheckable(True)
    self._verLogarithmicAction = self.createAndAddLocalAction("ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
    self._verLogarithmicAction.setCheckable(True)
    self._verActionGroup.addAction(self._verLinearAction)
    self._verActionGroup.addAction(self._verLogarithmicAction)
    self._verLinearAction.setChecked(True)
    self._verActionGroup.triggered.connect(self.onViewVerticalMode)
    self._verActionGroup.setExclusive(True)
    self._toolbar.addSeparator()
    # Action to show or hide the legend 
    self._actionLegend = self.createAndAddLocalAction("legend.png", trQ("SHOW_LEGEND_TXT"))
    self._actionLegend.setCheckable(True)
    self._actionLegend.triggered.connect(self.showHideLegend)
    if self._DEFAULT_LEGEND_STATE:
      self._actionLegend.setChecked(True)
    self._toolbar.addSeparator()
    # Action to set the preferences
    a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
    a.triggered.connect(self.onSettings)
    pass
    
  def dumpView(self):
    # Choice of the view backup file
    filters = []
    for form in ["IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES", "ENCAPSULATED_POSTSCRIPT_FILES"]:
      filters.append(trQ(form))
    fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(),
                                        "",
                                        filters,
                                        trQ("DUMP_VIEW_FILE"),
                                        False )
    if not fileName.isEmpty():
      name = str(fileName)
      self._mplAxes.figure.savefig(name)
    pass
    
  def autoFit(self, check=True, repaint=True):
    if self.__repaintOK():
      self._mplAxes.relim()
      xm, xM = self._mplAxes.xaxis.get_data_interval()
      ym, yM = self._mplAxes.yaxis.get_data_interval()
      i = yM-ym
      self._mplAxes.axis([xm, xM, ym-i*self.AUTOFIT_MARGIN, yM+i*self.AUTOFIT_MARGIN])
      if repaint:
        self.repaint()
  
  def zoomArea(self):
    if self._panAction.isChecked() and self._zoomAction.isChecked():
      self._panAction.setChecked(False)
    # Trigger underlying matplotlib action:
    self._mplNavigationActions["Zoom"].trigger()
  
  def pan(self):
    if self._panAction.isChecked() and self._zoomAction.isChecked():
      self._zoomAction.setChecked(False)
    # Trigger underlying matplotlib action:
    self._mplNavigationActions["Pan"].trigger()

  def getMarker(self, go_next=False):
    if go_next:
      self._lastMarkerID = (self._lastMarkerID+1) % len(self.CURVE_MARKERS)
    return self.CURVE_MARKERS[self._lastMarkerID]

  def changeModeCurve(self, repaint=True):
    if not self.__repaintOK():
      return
    action = self._curveActionGroup.checkedAction()
    if action is self._pointsAction :
      for crv_view in self._curveViews.values():
        crv_view.setLineStyle("None")
    elif action is self._linesAction :
      for crv_view in self._curveViews.values():
        crv_view.setLineStyle("-")
    else :
      raise NotImplementedError
    if repaint:
      self.repaint()
  
  def setXLog(self, log, repaint=True):
    if not self.__repaintOK():
      return
    self._blockLogSignal = True
    if log:
      self._mplAxes.set_xscale('log')
      self._horLogarithmicAction.setChecked(True)
    else:
      self._mplAxes.set_xscale('linear')
      self._horLinearAction.setChecked(True)
    if repaint:
      self.autoFit()
      self.repaint()
    self._blockLogSignal = False

  def setYLog(self, log, repaint=True):
    if not self.__repaintOK():
      return
    self._blockLogSignal = True
    if log:
      self._mplAxes.set_yscale('log')
      self._verLogarithmicAction.setChecked(True)
    else:
      self._mplAxes.set_yscale('linear')
      self._verLinearAction.setChecked(True)
    if repaint:
      self.autoFit()
      self.repaint()
    self._blockLogSignal = False
    
  def setXSciNotation(self, sciNotation, repaint=True):
    self._axisXSciNotation = sciNotation
    self.changeFormatAxis()
    if repaint:
      self.repaint()
   
  def setYSciNotation(self, sciNotation, repaint=True):
    self._axisYSciNotation = sciNotation
    self.changeFormatAxis()
    if repaint:
      self.repaint()
    
  def onViewHorizontalMode(self, checked=True, repaint=True):
    if self._blockLogSignal:
      return
    action = self._horActionGroup.checkedAction()  
    if action is self._horLinearAction:
      self.setXLog(False, repaint)
    elif action is self._horLogarithmicAction:
      self.setXLog(True, repaint)
    else:
      raise NotImplementedError
  
  def onViewVerticalMode(self, checked=True, repaint=True):
    if self._blockLogSignal:
      return
    action = self._verActionGroup.checkedAction()  
    if action is self._verLinearAction:
      self.setYLog(False, repaint)
    elif action is self._verLogarithmicAction:
      self.setYLog(True, repaint)
    else:
      raise NotImplementedError
    if repaint:
      self.repaint()
  
  def __adjustFigureMargins(self, withLegend):
    """ Adjust figure margins to make room for the legend """
    if withLegend:
      leg = self._legend
      bbox = leg.get_window_extent()
      # In axes coordinates: 
      bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
      if self._legendLoc == "right":
        self._mplFigure.subplots_adjust(right=1.0-(bbox2.width+0.02))
      elif self._legendLoc == "bottom":
        self._mplFigure.subplots_adjust(bottom=bbox2.height+0.1)
    else:
      # Reset to default (rc) values
      self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)
  
  def setLegendVisible(self, visible, repaint=True):
    if visible and not self._actionLegend.isChecked():
      self._actionLegend.setChecked(True)
      self.showHideLegend(repaint=repaint)
    if not visible and self._actionLegend.isChecked():
      self._actionLegend.setChecked(False)
      self.showHideLegend(repaint=repaint)
  
  def showHideLegend(self, actionChecked=None, repaint=True):
    if not self.__repaintOK():  # Show/hide legend is extremely costly
      return
    
    show = self._actionLegend.isChecked()
    nCurves = len(self._curveViews)
    if nCurves > 10: fontSize = 'x-small'
    else:            fontSize = None
    
    if nCurves == 0:
      # Remove legend 
      leg = self._mplAxes.legend()
      if leg is not None: leg.remove()
    if show and nCurves > 0:
      # Recreate legend from scratch
      if self._legend is not None:
        self._legend = None
        self._mplAxes._legend = None
      if self._legendLoc == "bottom":
        self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(0.0, -0.05, 1.0, -0.05), 
                                            borderaxespad=0.0, mode="expand", fancybox=True, 
                                            shadow=True, ncol=3, prop={'size':fontSize, 'style': 'italic'})
      elif self._legendLoc == "right":
        self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(1.02,1.0), borderaxespad=0.0,
                                            ncol=1, fancybox=True, shadow=True, prop={'size':fontSize, 'style': 'italic'})
      else:
        raise Exception("Invalid legend placement! Must be 'bottom' or 'right'")
      # Canvas must be drawn so we can adjust the figure placement:
      self._mplCanvas.draw()
      self.__adjustFigureMargins(withLegend=True)
    else:
      if self._legend is None:
        # Nothing to do
        return
      else:
        self._legend.set_visible(False)
        self._legend = None
        self._mplAxes._legend = None
        self._mplCanvas.draw()
        self.__adjustFigureMargins(withLegend=False)
    
    curr_crv = self._model._currentCurve
    if curr_crv is None: curr_title = None
    else:                curr_title = curr_crv.getTitle()
    if self._legend is not None:
      for label in self._legend.get_texts() :
        text = label.get_text()
        if (text == curr_title):
          label.set_backgroundcolor('0.85')
        else :
          label.set_backgroundcolor('white')
        
    if repaint:
      self.repaint()
   
  def onSettings(self, trigger=False, dlg_test=None):
    dlg = dlg_test or PlotSettings()
    dlg.titleEdit.setText(self._mplAxes.get_title())
    dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
    dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
    dlg.gridCheckBox.setChecked(self._mplAxes.xaxis._gridOnMajor)  # could not find a relevant API to check this
    dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
    dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
    xmin, xmax = self._mplAxes.get_xlim()
    ymin, ymax = self._mplAxes.get_ylim()
    xminText = "%g" %xmin
    xmaxText = "%g" %xmax
    yminText = "%g" %ymin
    ymaxText = "%g" %ymax
    dlg.axisXMinEdit.setText(xminText)
    dlg.axisXMaxEdit.setText(xmaxText)
    dlg.axisYMinEdit.setText(yminText)
    dlg.axisYMaxEdit.setText(ymaxText)
    # List of markers
    dlg.markerCurve.clear()
    for marker in self.CURVE_MARKERS :
      dlg.markerCurve.addItem(marker)
    curr_crv = self._model.getCurrentCurve()
    if not curr_crv is None:
      dlg.colorCurve.setEnabled(True)
      dlg.markerCurve.setEnabled(True)
      name = curr_crv.getTitle()
      dlg.nameCurve.setText(name)
      view = self._curveViews[curr_crv.getID()] 
      marker = view.getMarker()
      color = view.getColor()
      index = dlg.markerCurve.findText(marker)
      dlg.markerCurve.setCurrentIndex(index)
      rgb = colors.colorConverter.to_rgb(color)
      dlg.setRGB(rgb[0],rgb[1],rgb[2])
    else :
      dlg.colorCurve.setEnabled(False)
      dlg.markerCurve.setEnabled(False)
      dlg.nameCurve.setText("")
      view = None
    if self._legend is None:
      dlg.showLegendCheckBox.setChecked(False)
      dlg.legendPositionComboBox.setEnabled(False)
    else :
      if self._legend.get_visible():
        dlg.showLegendCheckBox.setChecked(True)
        dlg.legendPositionComboBox.setEnabled(True)
        if self._legendLoc == "bottom":
          dlg.legendPositionComboBox.setCurrentIndex(0)
        elif self._legendLoc == "right" :
          dlg.legendPositionComboBox.setCurrentIndex(1)
      else :
        dlg.showLegendCheckBox.setChecked(False)
        dlg.legendPositionComboBox.setEnabled(False)    
             
    if dlg.exec_():
      # Title
      self._model.setTitle(dlg.titleEdit.text())
      # Axis
      self._model.setXLabel(dlg.axisXTitleEdit.text())
      self._model.setYLabel(dlg.axisYTitleEdit.text())
      # Grid
      if dlg.gridCheckBox.isChecked() :
        self._mplAxes.grid(True)
      else :
        self._mplAxes.grid(False)
      # Legend
      if  dlg.showLegendCheckBox.isChecked():
        self._actionLegend.setChecked(True)
        if dlg.legendPositionComboBox.currentIndex() == 0 :
          self._legendLoc = "bottom"
        elif dlg.legendPositionComboBox.currentIndex() == 1 :
          self._legendLoc = "right"
      else :
        self._actionLegend.setChecked(False)
      xminText = dlg.axisXMinEdit.text()
      xmaxText = dlg.axisXMaxEdit.text()
      yminText = dlg.axisYMinEdit.text()
      ymaxText = dlg.axisYMaxEdit.text()
      self._mplAxes.axis([float(xminText), float(xmaxText), float(yminText), float(ymaxText)] )
      self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
      self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
      self.changeFormatAxis()
      # Color and marker of the curve
      if view:
        view.setColor(dlg.getRGB()) 
        view.setMarker(self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
      self.showHideLegend(repaint=True)
      self._mplCanvas.draw()
    pass
    
  def updateViewTitle(self):
    s = ""
    if self._model._title != "":
      s = " - %s" % self._model._title
    title = "CurvePlot (%d)%s" % (self._model.getID(), s)
    self._sgPyQt.setViewTitle(self._salomeViewID, title)
    
  def onCurrentPlotSetChange(self):
    """ Avoid a unnecessary call to update() when just switching current plot set! """ 
    pass
  
  def onCurrentCurveChange(self):
    curr_crv2 = self._model.getCurrentCurve()
    if curr_crv2 != self._currCrv:
      if self._currCrv is not None:
        view = self._curveViews[self._currCrv.getID()]
        view.toggleHighlight(False)
      if not curr_crv2 is None:
        view = self._curveViews[curr_crv2.getID()] 
        view.toggleHighlight(True)
      self._currCrv = curr_crv2
      self.showHideLegend(repaint=False) # redo legend
      self.repaint() 
      
  def changeFormatAxis(self) :
    if not self.__repaintOK():
      return
    
    # don't try to switch to sci notation if we are not using the 
    # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
    if self._horLinearAction.isChecked():
      if self._axisXSciNotation :
        self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='x')
      else :
        self._mplAxes.ticklabel_format(style='plain',axis='x')
    if self._verLinearAction.isChecked():    
      if self._axisYSciNotation :
        self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='y')
      else :
        self._mplAxes.ticklabel_format(style='plain',axis='y')
    
  def update(self):
    if self._salomeViewID is None:
      self.createPlotWidget()
      self._salomeViewID = self._sgPyQt.createView("CurvePlot", self._plotWidget)
      Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
      self._sgPyQt.setViewVisible(self._salomeViewID, True)
    
    self.updateViewTitle()
    
    # Check list of curve views:
    set_mod = set(self._model._curves.keys())
    set_view = set(self._curveViews.keys())
    
    # Deleted/Added curves:
    dels = set_view - set_mod
    added = set_mod - set_view
    
    for d in dels:
      self.removeCurve(d)

    if not len(self._curveViews):
      # Reset color cycle 
      self._mplAxes.set_color_cycle(None)

    for a in added:
      self.appendCurve(a)

    # Axes labels and title
    self._mplAxes.set_xlabel(self._model._xlabel)
    self._mplAxes.set_ylabel(self._model._ylabel)
    self._mplAxes.set_title(self._model._title)

    self.onViewHorizontalMode(repaint=False)
    self.onViewVerticalMode(repaint=False)
    self.changeModeCurve(repaint=False)
    self.showHideLegend(repaint=False)   # The canvas is repainted anyway (needed to get legend bounding box)
    self.changeFormatAxis()

    # Redo auto-fit
    self.autoFit(repaint=False)
    self.repaint()
  
  def onDataChange(self):
    # the rest is done in the CurveView:
    self.autoFit(repaint=True)
    
  def onMousePress(self, event):
    if event.button == 3 :
      if self._panAction.isChecked():
        self._panAction.setChecked(False)
      if self._zoomAction.isChecked():
        self._zoomAction.setChecked(False)
    
  def onContextMenu(self, position):
    pos = self._mplCanvas.mapToGlobal(QtCore.QPoint(position.x(),position.y()))
    self._popupMenu.exec_(pos)
    
  def onScroll(self, event):
    # Event location (x and y)
    xdata = event.xdata
    ydata = event.ydata
    
    cur_xlim = self._mplAxes.get_xlim()
    cur_ylim = self._mplAxes.get_ylim()
    
    base_scale = 2.
    if event.button == 'down':
      # deal with zoom in
      scale_factor = 1 / base_scale
    elif event.button == 'up':
      # deal with zoom out
      scale_factor = base_scale
    else:
      # deal with something that should never happen
      scale_factor = 1
    
    new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
    new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

    relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
    rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])

    self._mplAxes.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
    self._mplAxes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
    
    self.repaint()
    pass
    
  def onPressEvent(self, event):
    if event.button == 3 :
      #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
      canvasSize = event.canvas.geometry()
      point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
      self._popupMenu.exec_(point)
    else :
      print "Press event on the other button"
    #if event.button == 3 :
    #  canvasSize = event.canvas.geometry()
    #  point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
    #  self._popupMenu.move(point)
    #  self._popupMenu.show()
   
  def onMotionEvent(self, event):
    print "OnMotionEvent ",event.button
    #if event.button == 3 :
    #  event.button = None
    #  return True
   
  def onReleaseEvent(self, event):
    print "OnReleaseEvent ",event.button
예제 #5
0
파일: __init__.py 프로젝트: Grahack/geophar
class Calculatrice(Panel_simple):
    __titre__ = u"Calculatrice" # Donner un titre a chaque module

    def __init__(self, *args, **kw):
        Panel_simple.__init__(self, *args, **kw)
        self.interprete = Interprete(calcul_exact = self.param("calcul_exact"),
                                ecriture_scientifique = self.param("ecriture_scientifique"),
                                changer_separateurs = self.param("changer_separateurs"),
                                separateurs_personnels = self.param("separateurs_personnels"),
                                copie_automatique = self.param("copie_automatique"),
                                formatage_OOo = self.param("formatage_OOo"),
                                formatage_LaTeX = self.param("formatage_LaTeX"),
                                ecriture_scientifique_decimales = self.param("ecriture_scientifique_decimales"),
                                precision_calcul = self.param("precision_calcul"),
                                precision_affichage = self.param("precision_affichage"),
                                simpify = True,
                                )

##        self.entrees = wx.BoxSizer(wx.HORIZONTAL)
##        self.entree = wx.TextCtrl(self, size = (550, -1), style = wx.TE_PROCESS_ENTER)
##        self.entrees.Add(self.entree, 1, wx.ALL|wx.GROW, 5)
##        self.valider = wx.Button(self, wx.ID_OK)
##        self.entrees.Add(self.valider, 0, wx.ALL, 5)
        self.entree = entree = LigneCommande(self, longueur = 550, action = self.affichage_resultat)
        entree.setToolTip(u"[Maj]+[Entrée] pour une valeur approchée.")
        self.entree.texte.setContextMenuPolicy(Qt.CustomContextMenu)
        self.entree.texte.customContextMenuRequested.connect(self.EvtMenu)


        self.sizer = sizer = QVBoxLayout()
        sizer.addWidget(entree)
        self.corps = corps = QHBoxLayout()
        sizer.addLayout(corps)
        self.gauche = gauche = QVBoxLayout()
        corps.addLayout(gauche, 1)
        self.resultats = resultats = QTextEdit(self)
        resultats.setMinimumSize(450, 310)
        resultats.setReadOnly(True)
        gauche.addWidget(resultats)

        self.figure = Figure(figsize=(5,1.3), frameon=True, facecolor="w")
        self.visualisation = FigureCanvas(self.figure)
        self.axes = axes = self.figure.add_axes([0, 0, 1, 1], frameon=False)
        axes.axison = False
        self.pp_texte = axes.text(0.5, 0.5, "", horizontalalignment='center', verticalalignment='center', transform = axes.transAxes, size=18)
        gauche.addWidget(self.visualisation) # wx.ALL|wx.ALIGN_CENTER
        self.visualisation.setContextMenuPolicy(Qt.CustomContextMenu)
        self.visualisation.customContextMenuRequested.connect(self.EvtMenuVisualisation)

        ### Pave numerique de la calculatrice ###
        # On construit le pavé de la calculatrice.
        # Chaque bouton du pavé doit provoquer l'insertion de la commande correspondante.

        self.pave = pave = QVBoxLayout()
        corps.addLayout(pave)
#        pave.setSpacing(1)
        boutons = ["2nde", "ans", "ouv", "ferm", "egal", "7", "8", "9", "div", "x", "4", "5", "6", "mul", "y", "1", "2", "3", "minus", "z", "0", "pt", "pow", "plus", "t", "rac", "sin", "cos", "tan", "exp", "i", "pi", "e", "abs", "mod"]
        inserer = ["", "ans()", "(", ")", "=", "7", "8", "9",  "/", "x", "4", "5", "6", "*", "y", "1", "2", "3", "-", "z", "0", ".", "^", "+", "t", "sqrt(", ("sin(", "asin(", "sinus / arcsinus"), ("cos(", "acos(", "cosinus / arccosinus"), ("tan(", "atan(", "tangente / arctangente"), ("exp(", "ln(", "exponentielle / logarithme neperien"), ("i", "cbrt(", "i / racine cubique"), ("pi", "sinh(", "pi / sinus hyperbolique"), ("e", "cosh", "e / cosinus hyperbolique"), ("abs(", "tanh", "valeur absolue / tangente hyperbolique"), (" mod ", "log10(", "modulo / logarithme decimal")]

        self.seconde = False # indique si la touche 2nde est activee.

        def action(event = None):
            self.seconde = not self.seconde
            if self.seconde:
                self.message(u"Touche [2nde] activée.")
            else:
                self.message("")

        self.actions = [action]

        for i in range(len(boutons)):
            # On aligne les boutons de la calculatrice par rangées de 5.
            if i%5 == 0:
                self.rangee = rangee = QHBoxLayout()
                rangee.addStretch(1)
                pave.addLayout(rangee)

            # Ensuite, on construit une liste de fonctions, parallèlement à la liste des boutons.
            if i > 0:
                #XXX: ce serait plus propre en utilisant partial().
                def action(event = None, entree = self.entree, j = i):
                    if type(inserer[j]) == tuple:
                        entree.insert(inserer[j][self.seconde])
                    else:
                        entree.insert(inserer[j])
                    n = entree.cursorPosition()
                    entree.setFocus()
                    entree.setCursorPosition(n)
                    self.seconde = False
                    self.message("")
                self.actions.append(action)

            bouton = QPushButton()
            pix = png('btn_' + boutons[i])
            bouton.setIcon(QIcon(pix))
            bouton.setIconSize(pix.size())
            bouton.setFlat(True)
#            bouton.setSize()
#            bouton.SetBackgroundColour(self.GetBackgroundColour())
            rangee.addWidget(bouton)
            if i%5 == 4:
                rangee.addStretch(2)
            # A chaque bouton, on associe une fonction de la liste.
            bouton.clicked.connect(self.actions[i])
            if type(inserer[i]) == tuple:
                bouton.setToolTip(inserer[i][2])

#        self.pave.Add(QHBoxLayout())


        ### Liste des options ###
        # En dessous du pavé apparait la liste des différents modes de fonctionnement de la calculatrice.

        # Calcul exact
        ligne = QHBoxLayout()
        pave.addLayout(ligne)
        self.cb_calcul_exact = QCheckBox(self)
        self.cb_calcul_exact.setChecked(not self.param("calcul_exact"))
        ligne.addWidget(self.cb_calcul_exact) #, flag = wx.ALIGN_CENTER_VERTICAL)
        ligne.addWidget(QLabel(u"Valeur approchée."))#, flag = wx.ALIGN_CENTER_VERTICAL)
        ligne.addStretch()

        self.cb_calcul_exact.stateChanged.connect(self.EvtCalculExact)

        # Notation scientifique
        ligne = QHBoxLayout()
        pave.addLayout(ligne)
        self.cb_notation_sci = QCheckBox(self)
        self.cb_notation_sci.setChecked(self.param("ecriture_scientifique"))
        ligne.addWidget(self.cb_notation_sci)#, flag = wx.ALIGN_CENTER_VERTICAL)
        self.st_notation_sci = QLabel(u"Écriture scientifique (arrondie à ")
        ligne.addWidget(self.st_notation_sci)#, flag = wx.ALIGN_CENTER_VERTICAL)
        self.sc_decimales = sd = QSpinBox(self)
        # size = (45, -1)
        sd.setRange(0, 11)
        self.sc_decimales.setValue(self.param("ecriture_scientifique_decimales"))
        ligne.addWidget(self.sc_decimales)#, flag = wx.ALIGN_CENTER_VERTICAL)
        self.st_decimales = QLabel(u" décimales).")
        ligne.addWidget(self.st_decimales)#, flag = wx.ALIGN_CENTER_VERTICAL)
        ligne.addStretch()
        self.EvtCalculExact()

        self.cb_notation_sci.stateChanged.connect(self.EvtNotationScientifique)
        self.sc_decimales.valueChanged.connect(self.EvtNotationScientifique)

        # Copie du résultat dans le presse-papier
        ligne = QHBoxLayout()
        pave.addLayout(ligne)
        self.cb_copie_automatique = QCheckBox(self)
        self.cb_copie_automatique.setChecked(self.param("copie_automatique"))
        ligne.addWidget(self.cb_copie_automatique)#, flag = wx.ALIGN_CENTER_VERTICAL)
        ligne.addWidget(QLabel(u"Copie du résultat dans le presse-papier."))#, flag = wx.ALIGN_CENTER_VERTICAL)
        ligne.addStretch()

        self.cb_copie_automatique.stateChanged.connect(self.EvtCopieAutomatique)

        # En mode LaTeX
        ligne = QHBoxLayout()
        pave.addLayout(ligne)
        self.cb_copie_automatique_LaTeX = QCheckBox(self)
        self.cb_copie_automatique_LaTeX.setChecked(self.param("copie_automatique_LaTeX"))
        ligne.addWidget(self.cb_copie_automatique_LaTeX)
        self.st_copie_automatique_LaTeX = QLabel(u"Copie au format LaTeX (si possible).")
        ligne.addWidget(self.st_copie_automatique_LaTeX)
        ligne.addStretch()

        self.EvtCopieAutomatique()
        self.cb_copie_automatique_LaTeX.stateChanged.connect(self.EvtCopieAutomatiqueLatex)

        # Autres options
        self.options = [(u"Virgule comme séparateur décimal.", u"changer_separateurs"),
##                    (u"Copie du résultat dans le presse-papier.", u"copie_automatique"),
                    (u"Accepter la syntaxe OpenOffice.org", u"formatage_OOo"),
                    (u"Accepter la syntaxe LaTeX", u"formatage_LaTeX"),
                        ]
        self.options_box = []
        for i in range(len(self.options)):
            ligne = QHBoxLayout()
            pave.addLayout(ligne)
            self.options_box.append(QCheckBox(self))
            ligne.addWidget(self.options_box[i])
            self.options_box[i].setChecked(self.param(self.options[i][1]))
            def action(event, chaine = self.options[i][1], entree = self.entree, self = self):
                self.param(chaine, not self.param(chaine))
                entree.setFocus()
            self.options_box[i].stateChanged.connect(action)
            ligne.addWidget(QLabel(self.options[i][0]))
            ligne.addStretch()

        self.setLayout(self.sizer)
#        self.adjustSize()

        #TODO:
#        self.entree.texte.Bind(wx.EVT_RIGHT_DOWN, self.EvtMenu)
#        self.visualisation.Bind(wx.EVT_RIGHT_DOWN, self.EvtMenuVisualisation)

        self.initialiser()


    def activer(self):
        # Actions à effectuer lorsque l'onglet devient actif
        self.entree.setFocus()


    def _sauvegarder(self, fgeo):
        fgeo.contenu["Calculatrice"] = [{}]
        fgeo.contenu["Calculatrice"][0]["Historique"] = [repr(self.entree.historique)]
#        fgeo.contenu["Calculatrice"][0]["Resultats"] = [repr(self.interprete.derniers_resultats)]
        fgeo.contenu["Calculatrice"][0]["Affichage"] = [self.resultats.toPlainText()]
        fgeo.contenu["Calculatrice"][0]["Etat_interne"] = [self.interprete.save_state()]
        fgeo.contenu["Calculatrice"][0]["Options"] = [{}]
        for i in range(len(self.options)):
            fgeo.contenu["Calculatrice"][0]["Options"][0][self.options[i][1]] = [str(self.options_box[i].isChecked())]



    def _ouvrir(self, fgeo):
        if fgeo.contenu.has_key("Calculatrice"):
            calc = fgeo.contenu["Calculatrice"][0]
            self.initialiser()

            self.entree.historique = eval_safe(calc["Historique"][0])
#            self.interprete.derniers_resultats = securite.eval_safe(calc["Resultats"][0])
            self.resultats.setPlainText(calc["Affichage"][0] + "\n")
            self.interprete.load_state(calc["Etat_interne"][0])

            liste = calc["Options"][0].items()
            options = [option for aide, option in self.options]
            for key, value in liste:
                value = eval_safe(value[0])
                self.param(key, value)
                if key in options:
                    self.options_box[options.index(key)].setChecked(value)
            # il faudrait encore sauvegarder les variables, mais la encore, 2 problemes :
            # - pb de securite pour evaluer les variables
            # - pb pour obtenir le code source d'une fonction.
            # Pour remedier a cela, il faut envisager de :
            # - creer un module d'interpretation securisee.
            # - rajouter a chaque fonction un attribut __code__, ou creer une nouvelle classe.


    def modifier_pp_texte(self, chaine):
        u"""Modifier le résultat affiché en LaTeX (pretty print)."""
        if self.param("latex"):
            chaine = "$" + chaine + "$"
        else:
            chaine = chaine.replace("\\mapsto", "\\rightarrow")
            if chaine.startswith(r"$\begin{bmatrix}"):
                chaine = chaine.replace(r"\begin{bmatrix}", r'\left({')
                chaine = chaine.replace(r"\end{bmatrix}", r'}\right)')
                chaine = chaine.replace(r"&", r'\,')
        self.pp_texte.set_text(chaine)
        self.visualisation.draw()

    def vers_presse_papier(self, event = None, texte = None):
        if texte is None:
            texte = self.dernier_resultat
        Panel_simple.vers_presse_papier(texte)

    def copier_latex(self, event = None):
        self.vers_presse_papier(texte = self.interprete.latex_dernier_resultat.strip("$"))

    def initialiser(self, event = None):
        self.dernier_resultat = "" # dernier resultat, sous forme de chaine formatee pour l'affichage
        self.entree.initialiser()
        self.interprete.initialiser()
        self.resultats.clear()

    def affichage_resultat(self, commande, **kw):
        # Commandes spéciales:
        if commande in ('clear', 'clear()', 'efface', 'efface()'):
            self.initialiser()
            self.modifier_pp_texte(u"Calculatrice réinitialisée.")
            return

        self.modifie = True
        try:
            try:
                if kw["shift"]:
                    self.interprete.calcul_exact = False
                resultat, latex = self.interprete.evaluer(commande)
                if latex == "$?$": # provoque une erreur (matplotlib 0.99.1.1)
                    latex = u"Désolé, je ne sais pas faire..."
            finally:
                self.interprete.calcul_exact = self.param('calcul_exact')
            aide = resultat.startswith("\n== Aide sur ")
            #LaTeX
            debug("Expression LaTeX: " + latex)
            try:
                self.modifier_pp_texte((latex or resultat) if not aide else '')
            except Exception:
                print_error()
                self.modifier_pp_texte("<Affichage impossible>")
            #Presse-papier
            self.dernier_resultat = resultat
            if self.param("copie_automatique"):
                if self.param("copie_automatique_LaTeX"):
                    self.copier_latex()
                else:
                    self.vers_presse_papier()
            # TextCtrl
            numero = str(len(self.interprete.derniers_resultats))
            # Évite le décalage entre la première ligne et les suivantes (matrices)
            if "\n" in resultat and not aide:
                resultat = "\n" + "\n".join(20*" " + ligne for ligne in resultat.split("\n"))
            self.resultats.insertPlainText(u" Calcul n\xb0" + numero + " :   "
                                                        + uu(commande) + u"\n Résultat :"
                                                        + " "*(4+len(numero))
                                                        + resultat + "\n__________________\n\n")
            self.message(u"Calcul effectué." + self.interprete.warning)
            self.entree.clear()
#            self.resultats.setCursorPosition(len(self.resultats.plainText()))
#            self.resultats.setFocus()
#            self.resultats.ScrollLines(1)
            self.entree.setFocus()
        except Exception:
            self.message(u"Calcul impossible.")
            self.entree.setFocus()
            if param.debug:
                raise


    def insere(self, event=None, nom='', parentheses=True):
        entree = self.entree
        deb, fin = entree.getSelection()
        if parentheses:
            entree.setCursorPosition(fin)
            entree.insert(")")
            entree.setCursorPosition(deb)
            entree.insert(nom + "(")
            entree.setFocus()
            if deb == fin:
                final = fin + len(nom) + 1
            else:
                final = fin + len(nom) + 2
        else:
            entree.insert(nom)
            final = fin + len(nom)
        entree.setFocus()
        entree.setCursorPosition(final)


    def EvtMenu(self, event):
#        if not event.ControlDown():
#            event.Skip()
#            return
#            entree.setSelection(final, final)
        menu = QMenu()
        menu.setWindowTitle(u"Fonctions mathématiques")
        debut = True
        for rubrique in __classement__:
            if not debut:
                menu.addSeparator()
            debut = False
            for titre, nom, doc in filter(None, __classement__[rubrique]):
                action = menu.addAction(titre, partial(self.insere, nom=nom, parentheses=(rubrique != "Symboles")))
                # Pas de parenthèses après un symbole.
                action.setToolTip(doc)
        menu.exec_(QCursor.pos())


    def EvtMenuVisualisation(self, event):
        menu = QMenu()
        action = menu.addAction("Copier LaTeX", self.copier_latex)
        action.setToolTip("Copier le code LaTeX dans le presse-papier.")
        menu.exec_(QCursor.pos())
#        self.PopupMenu(menu)
#        menu.Destroy()



    def param(self, parametre, valeur = no_argument, defaut = False):
        if valeur is not no_argument:
            setattr(self.interprete, parametre, valeur)
        return Panel_simple.param(self, parametre = parametre, valeur = valeur, defaut = defaut)

    def EvtCalculExact(self, event = None):
        valeur = self.cb_calcul_exact.isChecked()
        self.param("calcul_exact", not valeur)
        if valeur:
            self.cb_notation_sci.setEnabled(True)
            self.st_notation_sci.setEnabled(True)
            self.sc_decimales.setEnabled(True)
            self.st_decimales.setEnabled(True)
        else:
            self.cb_notation_sci.setEnabled(False)
            self.st_notation_sci.setEnabled(False)
            self.sc_decimales.setEnabled(False)
            self.st_decimales.setEnabled(False)


    def EvtNotationScientifique(self, event = None):
        self.param("ecriture_scientifique", self.cb_notation_sci.isChecked())
        self.param("ecriture_scientifique_decimales", self.sc_decimales.value())

    def EvtCopieAutomatique(self, event = None):
        valeur = self.cb_copie_automatique.isChecked()
        self.param("copie_automatique", valeur)
        if valeur:
            self.cb_copie_automatique_LaTeX.setEnabled(True)
            self.st_copie_automatique_LaTeX.setEnabled(True)
        else:
            self.cb_copie_automatique_LaTeX.setEnabled(False)
            self.st_copie_automatique_LaTeX.setEnabled(False)

    def EvtCopieAutomatiqueLatex(self, event = None):
        self.param("copie_automatique_LaTeX", self.cb_copie_automatique_LaTeX.isChecked())

    def EtatInterne(self, event):
        contenu = self.interprete.save_state()
        h = FenCode(self, u"État interne de l'inteprète", contenu, self.interprete.load_state)
        h.show()
예제 #6
0
class Calculatrice(Panel_simple):
    titre = u"Calculatrice"  # Donner un titre a chaque module

    def __init__(self, *args, **kw):
        Panel_simple.__init__(self, *args, **kw)
        self.interprete = Interprete(
            calcul_exact=self.param("calcul_exact"),
            ecriture_scientifique=self.param("ecriture_scientifique"),
            formatage_OOo=self.param("formatage_OOo"),
            formatage_LaTeX=self.param("formatage_LaTeX"),
            ecriture_scientifique_decimales=self.param("ecriture_scientifique_decimales"),
            precision_calcul=self.param("precision_calcul"),
            precision_affichage=self.param("precision_affichage"),
            simpify=True,
        )

        bouton = BoutonValider(self)
        bouton.setToolTip(u"Laissez appuyé pour changer de mode.")
        self.entree = entree = LigneCommande(self, longueur=550, action=self.affichage_resultat, bouton=bouton)
        entree.setToolTip(u"[Maj]+[Entrée] pour une valeur approchée.")
        self.entree.texte.setContextMenuPolicy(Qt.CustomContextMenu)
        self.entree.texte.customContextMenuRequested.connect(self.EvtMenu)

        self.sizer = sizer = QVBoxLayout()
        sizer.addWidget(entree)
        self.corps = corps = QHBoxLayout()
        sizer.addLayout(corps)
        self.resultats = resultats = QTextEdit(self)
        resultats.setMinimumSize(450, 310)
        resultats.setReadOnly(True)
        corps.addWidget(resultats, 1)
        corps.addWidget(OngletsCalc(self))

        self.figure = Figure(figsize=(5, 1.3), frameon=True, facecolor="w")
        self.visualisation = FigureCanvas(self.figure)
        self.axes = axes = self.figure.add_axes([0, 0, 1, 1], frameon=False)
        axes.axison = False
        self.pp_texte = axes.text(
            0.5, 0.5, "", horizontalalignment="center", verticalalignment="center", transform=axes.transAxes, size=18
        )
        self.visualisation.setContextMenuPolicy(Qt.CustomContextMenu)
        self.visualisation.customContextMenuRequested.connect(self.EvtMenuVisualisation)
        sizer.addWidget(self.visualisation)

        self.setLayout(self.sizer)
        self.initialiser()

    def activer(self):
        Panel_simple.activer(self)
        # Actions à effectuer lorsque l'onglet devient actif
        self.entree.setFocus()

    def _sauvegarder(self, fgeo):
        fgeo.contenu["Calculatrice"] = [{}]
        fgeo.contenu["Calculatrice"][0]["Historique"] = [repr(self.entree.historique)]
        #        fgeo.contenu["Calculatrice"][0]["Resultats"] = [repr(self.interprete.derniers_resultats)]
        fgeo.contenu["Calculatrice"][0]["Affichage"] = [self.resultats.toPlainText()]
        fgeo.contenu["Calculatrice"][0]["Etat_interne"] = [self.interprete.save_state()]
        ##fgeo.contenu["Calculatrice"][0]["Options"] = [{}]
        ##for i in range(len(self.options)):
        ##fgeo.contenu["Calculatrice"][0]["Options"][0][self.options[i][1]] = [str(self.options_box[i].isChecked())]

    def _ouvrir(self, fgeo):
        if fgeo.contenu.has_key("Calculatrice"):
            calc = fgeo.contenu["Calculatrice"][0]
            self.initialiser()

            self.entree.historique = eval_safe(calc["Historique"][0])
            #            self.interprete.derniers_resultats = securite.eval_safe(calc["Resultats"][0])
            resultats = calc["Affichage"][0]
            if resultats:
                resultats += "\n\n"
            self.resultats.setPlainText(resultats)
            self.resultats.moveCursor(QTextCursor.End)
            self.interprete.load_state(calc["Etat_interne"][0])

            ##liste = calc["Options"][0].items()
            ##options = [option for aide, option in self.options]
            ##for key, value in liste:
            ##value = eval_safe(value[0])
            ##self.param(key, value)
            ##if key in options:
            ##self.options_box[options.index(key)].setChecked(value)
            # il faudrait encore sauvegarder les variables, mais la encore, 2 problemes :
            # - pb de securite pour evaluer les variables
            # - pb pour obtenir le code source d'une fonction.
            # Pour remedier a cela, il faut envisager de :
            # - creer un module d'interpretation securisee.
            # - rajouter a chaque fonction un attribut __code__, ou creer une nouvelle classe.

    def modifier_pp_texte(self, chaine):
        u"""Modifier le résultat affiché en LaTeX (pretty print)."""
        if self.param("latex"):
            # On utilise directement LaTeX pour le rendu
            chaine = "$" + chaine + "$"
        else:
            # On utilise le parser matplotlib.mathtext, moins complet mais bien
            # plus rapide. Certaines adaptations doivent être faites.
            chaine = latex2mathtext(chaine)
        self.pp_texte.set_text(chaine)
        self.visualisation.draw()

    def vers_presse_papier(self, event=None, texte=None):
        if texte is None:
            texte = self.dernier_resultat
        Panel_simple.vers_presse_papier(texte)

    def copier_latex(self, event=None):
        self.vers_presse_papier(texte=self.interprete.latex_dernier_resultat.strip("$"))

    def initialiser(self, event=None):
        self.dernier_resultat = ""  # dernier resultat, sous forme de chaine formatee pour l'affichage
        self.entree.initialiser()
        self.interprete.initialiser()
        self.resultats.clear()

    def affichage_resultat(self, commande, **kw):
        # Commandes spéciales:
        if commande in ("clear", "clear()", "efface", "efface()"):
            self.initialiser()
            self.modifier_pp_texte(u"Calculatrice réinitialisée.")
            return

        self.modifie = True
        try:
            try:
                ##self.parent.parent.application.processEvents()
                if kw.get("shift"):
                    self.interprete.calcul_exact = False
                resultat, latex = self.interprete.evaluer(commande)
                if latex == "$?$":  # provoque une erreur (matplotlib 0.99.1.1)
                    latex = u"Désolé, je ne sais pas faire..."
            finally:
                self.interprete.calcul_exact = self.param("calcul_exact")
                self.entree.bouton.mode_normal()
            aide = resultat.startswith("\n== Aide sur ")
            if aide:
                latex = ""
            elif not latex:
                latex = resultat
            # LaTeX
            debug("Expression LaTeX: " + latex)
            try:
                try:
                    # Affichage en LaTeX si possible.
                    self.modifier_pp_texte(latex)
                except Exception:
                    print_error()
                    # Sinon, affichage en texte simple.
                    #  `matplotlib.mathtext` est encore loin d'être
                    # pleinement compatible avec LaTeX !
                    self.modifier_pp_texte(resultat)
            except Exception:
                print_error()
                # Si tout a raté... mais ça ne devrait jamais arrivé.
                self.modifier_pp_texte("<Affichage impossible>")
            # Presse-papier
            self.dernier_resultat = resultat
            if self.param("copie_automatique"):
                if self.param("copie_automatique_LaTeX"):
                    self.copier_latex()
                else:
                    self.vers_presse_papier()
            # TextCtrl
            numero = str(len(self.interprete.derniers_resultats))
            # Évite le décalage entre la première ligne et les suivantes (matrices)
            if "\n" in resultat and not aide:
                resultat = "\n" + "\n".join(20 * " " + ligne for ligne in resultat.split("\n"))
            self.resultats.moveCursor(QTextCursor.End)
            self.resultats.insertPlainText(
                u" Calcul n\xb0"
                + numero
                + " :   "
                + uu(commande)
                + u"\n Résultat :"
                + " " * (4 + len(numero))
                + resultat
                + "\n__________________\n\n"
            )
            self.resultats.moveCursor(QTextCursor.End)
            self.message(u"Calcul effectué." + self.interprete.warning)
            self.entree.clear()
            #            self.resultats.setCursorPosition(len(self.resultats.plainText()))
            #            self.resultats.setFocus()
            #            self.resultats.ScrollLines(1)
            self.entree.setFocus()
        except Exception:
            self.message(u"Calcul impossible.")
            self.entree.setFocus()
            if param.debug:
                raise

    def insere(self, event=None, nom="", parentheses=True):
        entree = self.entree
        deb, fin = entree.getSelection()
        if parentheses:
            entree.setCursorPosition(fin)
            entree.insert(")")
            entree.setCursorPosition(deb)
            entree.insert(nom + "(")
            entree.setFocus()
            if deb == fin:
                final = fin + len(nom) + 1
            else:
                final = fin + len(nom) + 2
        else:
            entree.insert(nom)
            final = fin + len(nom)
        entree.setFocus()
        entree.setCursorPosition(final)

    def EvtMenu(self, event):
        #        if not event.ControlDown():
        #            event.Skip()
        #            return
        #            entree.setSelection(final, final)
        menu = QMenu()
        menu.setWindowTitle(u"Fonctions mathématiques")
        debut = True
        for rubrique in __classement__:
            if not debut:
                menu.addSeparator()
            debut = False
            for titre, nom, doc in filter(None, __classement__[rubrique]):
                action = menu.addAction(titre, partial(self.insere, nom=nom, parentheses=(rubrique != "Symboles")))
                # Pas de parenthèses après un symbole.
                action.setToolTip(doc)
        menu.exec_(QCursor.pos())

    def EvtMenuVisualisation(self, event):
        menu = QMenu()
        action = menu.addAction("Copier LaTeX", self.copier_latex)
        action.setToolTip("Copier le code LaTeX dans le presse-papier.")
        menu.exec_(QCursor.pos())

    #        self.PopupMenu(menu)
    #        menu.Destroy()

    def param(self, parametre, valeur=no_argument, defaut=False):
        if valeur is not no_argument:
            setattr(self.interprete, parametre, valeur)
        return Panel_simple.param(self, parametre=parametre, valeur=valeur, defaut=defaut)

    def EtatInterne(self, event):
        contenu = self.interprete.save_state()
        h = FenCode(self, u"État interne de l'inteprète", contenu, self.interprete.load_state)
        h.show()
예제 #7
0
파일: XYView.py 프로젝트: Sunqia/salome-gui
class XYView(View):
    AUTOFIT_MARGIN = 0.03  # 3%

    # See http://matplotlib.org/api/markers_api.html:
    CURVE_MARKERS = [
        "o",  #  circle
        "*",  # star
        "+",  # plus
        "x",  # x
        "s",  # square
        "p",  # pentagon
        "h",  # hexagon1
        "8",  # octagon
        "D",  # diamond
        "^",  # triangle_up
        "<",  # triangle_left
        ">",  # triangle_right
        "1",  # tri_down
        "2",  # tri_up
        "3",  # tri_left
        "4",  # tri_right
        "v",  # triangle_down
        "H",  # hexagon2
        "d",  # thin diamond
        "",  # NO MARKER
    ]

    _DEFAULT_LEGEND_STATE = False  # for test purposes mainly - initial status of the legend

    def __init__(self, controller):
        View.__init__(self, controller)
        self._eventHandler = EventHandler()

        self._curveViews = {}  # key: curve (model) ID, value: CurveView
        self._salomeViewID = None
        self._mplFigure = None
        self._mplAxes = None
        self._mplCanvas = None
        self._plotWidget = None
        self._sgPyQt = self._controller._sgPyQt
        self._toolbar = None
        self._mplNavigationActions = {}
        self._toobarMPL = None
        self._grid = None
        self._currCrv = None  # current curve selected in the view

        self._legend = None
        self._legendLoc = "right"  # "right" or "bottom"

        self._fitArea = False
        self._zoomPan = False
        self._dragOnDrop = False
        self._move = False

        self._patch = None
        self._xdata = None
        self._ydata = None
        self._defaultLineStyle = None
        self._last_point = None
        self._lastMarkerID = -1
        self._blockLogSignal = False

        self._axisXSciNotation = False
        self._axisYSciNotation = False
        self._prevTitle = None

    def __repaintOK(self):
        """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
    @return False if painting is currently locked, in which case it will also register the current XYView 
    as needing a refresh when unlocked
    """
        ret = self._controller._plotManager.isRepaintLocked()
        if ret:
            self._controller._plotManager.registerRepaint(self._model)
        return (not ret)

    def appendCurve(self, curveID):
        newC = CurveView(self._controller, self)
        newC.setModel(self._model._curves[curveID])
        newC.setMPLAxes(self._mplAxes)
        newC.draw()
        newC.setMarker(self.getMarker(go_next=True))
        self._curveViews[curveID] = newC

    def removeCurve(self, curveID):
        v = self._curveViews.pop(curveID)
        v.erase()
        if self._currCrv is not None and self._currCrv.getID() == curveID:
            self._currCrv = None

    def cleanBeforeClose(self):
        """ Clean some items to avoid accumulating stuff in memory """
        self._mplFigure.clear()
        plt.close(self._mplFigure)
        self._plotWidget.clearAll()
        # For memory debugging only:
        import gc
        gc.collect()

    def repaint(self):
        if self.__repaintOK():
            Logger.Debug("XYView::draw")
            self._mplCanvas.draw()

    def onXLabelChange(self):
        if self.__repaintOK():
            self._mplAxes.set_xlabel(self._model._xlabel)
            self.repaint()

    def onYLabelChange(self):
        if self.__repaintOK():
            self._mplAxes.set_ylabel(self._model._ylabel)
            self.repaint()

    def onTitleChange(self):
        if self.__repaintOK():
            self._mplAxes.set_title(self._model._title)
            self.updateViewTitle()
            self.repaint()

    def onCurveTitleChange(self):
        # Updating the legend should suffice
        self.showHideLegend()

    def onClearAll(self):
        """ Just does an update with a reset of the marker cycle. """
        if self.__repaintOK():
            self._lastMarkerID = -1
            self.update()

    def onPick(self, event):
        """ MPL callback when picking
    """
        if event.mouseevent.button == 1:
            selected_id = -1
            a = event.artist
            for crv_id, cv in self._curveViews.items():
                if cv._mplLines[0] is a:
                    selected_id = crv_id
            # Use the plotmanager so that other plot sets get their current reset:
            self._controller._plotManager.setCurrentCurve(selected_id)

    def createAndAddLocalAction(self, icon_file, short_name):
        return self._toolbar.addAction(
            self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)

    def createPlotWidget(self):
        self._mplFigure = Figure((8.0, 5.0), dpi=100)
        self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
        self._mplCanvas.installEventFilter(self._eventHandler)
        self._mplCanvas.mpl_connect('pick_event', self.onPick)
        self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
        self._plotWidget = PlotWidget()
        self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None)
        for act in self._toobarMPL.actions():
            actionName = str(act.text()).strip()
            self._mplNavigationActions[actionName] = act
        self._plotWidget.setCentralWidget(self._mplCanvas)
        self._toolbar = self._plotWidget.toolBar
        self.populateToolbar()

        self._popupMenu = QtGui.QMenu()
        self._popupMenu.addAction(self._actionLegend)

        # Connect evenement for the graphic scene
        self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu)
        self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
        self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)

    def populateToolbar(self):
        # Action to dump view in a file
        a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
        a.triggered.connect(self.dumpView)
        self._toolbar.addSeparator()
        # Actions to manipulate the scene
        a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
        a.triggered.connect(self.autoFit)
        #    Zoom and pan are mutually exclusive but can be both de-activated:
        self._zoomAction = self.createAndAddLocalAction(
            "fit_area.png", trQ("FIT_AREA_TXT"))
        self._zoomAction.triggered.connect(self.zoomArea)
        self._zoomAction.setCheckable(True)
        self._panAction = self.createAndAddLocalAction("zoom_pan.png",
                                                       trQ("ZOOM_PAN_TXT"))
        self._panAction.triggered.connect(self.pan)
        self._panAction.setCheckable(True)
        self._toolbar.addSeparator()
        # Actions to change the representation of curves
        self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._pointsAction = self.createAndAddLocalAction(
            "draw_points.png", trQ("DRAW_POINTS_TXT"))
        self._pointsAction.setCheckable(True)
        self._linesAction = self.createAndAddLocalAction(
            "draw_lines.png", trQ("DRAW_LINES_TXT"))
        self._linesAction.setCheckable(True)
        self._curveActionGroup.addAction(self._pointsAction)
        self._curveActionGroup.addAction(self._linesAction)
        self._linesAction.setChecked(True)
        self._curveActionGroup.triggered.connect(self.changeModeCurve)
        self._curveActionGroup.setExclusive(True)
        self._toolbar.addSeparator()
        # Actions to draw horizontal curves as linear or logarithmic
        self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._horLinearAction = self.createAndAddLocalAction(
            "hor_linear.png", trQ("HOR_LINEAR_TXT"))
        self._horLinearAction.setCheckable(True)
        self._horLogarithmicAction = self.createAndAddLocalAction(
            "hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
        self._horLogarithmicAction.setCheckable(True)
        self._horActionGroup.addAction(self._horLinearAction)
        self._horActionGroup.addAction(self._horLogarithmicAction)
        self._horLinearAction.setChecked(True)
        self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
        self._toolbar.addSeparator()
        # Actions to draw vertical curves as linear or logarithmic
        self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._verLinearAction = self.createAndAddLocalAction(
            "ver_linear.png", trQ("VER_LINEAR_TXT"))
        self._verLinearAction.setCheckable(True)
        self._verLogarithmicAction = self.createAndAddLocalAction(
            "ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
        self._verLogarithmicAction.setCheckable(True)
        self._verActionGroup.addAction(self._verLinearAction)
        self._verActionGroup.addAction(self._verLogarithmicAction)
        self._verLinearAction.setChecked(True)
        self._verActionGroup.triggered.connect(self.onViewVerticalMode)
        self._verActionGroup.setExclusive(True)
        self._toolbar.addSeparator()
        # Action to show or hide the legend
        self._actionLegend = self.createAndAddLocalAction(
            "legend.png", trQ("SHOW_LEGEND_TXT"))
        self._actionLegend.setCheckable(True)
        self._actionLegend.triggered.connect(self.showHideLegend)
        if self._DEFAULT_LEGEND_STATE:
            self._actionLegend.setChecked(True)
        self._toolbar.addSeparator()
        # Action to set the preferences
        a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
        a.triggered.connect(self.onSettings)
        pass

    def dumpView(self):
        # Choice of the view backup file
        filters = []
        for form in [
                "IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES",
                "ENCAPSULATED_POSTSCRIPT_FILES"
        ]:
            filters.append(trQ(form))
        fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(), "",
                                            filters, trQ("DUMP_VIEW_FILE"),
                                            False)
        if not fileName.isEmpty():
            name = str(fileName)
            self._mplAxes.figure.savefig(name)
        pass

    def autoFit(self, check=True, repaint=True):
        if self.__repaintOK():
            self._mplAxes.relim()
            xm, xM = self._mplAxes.xaxis.get_data_interval()
            ym, yM = self._mplAxes.yaxis.get_data_interval()
            i = yM - ym
            self._mplAxes.axis([
                xm, xM, ym - i * self.AUTOFIT_MARGIN,
                yM + i * self.AUTOFIT_MARGIN
            ])
            if repaint:
                self.repaint()

    def zoomArea(self):
        if self._panAction.isChecked() and self._zoomAction.isChecked():
            self._panAction.setChecked(False)
        # Trigger underlying matplotlib action:
        self._mplNavigationActions["Zoom"].trigger()

    def pan(self):
        if self._panAction.isChecked() and self._zoomAction.isChecked():
            self._zoomAction.setChecked(False)
        # Trigger underlying matplotlib action:
        self._mplNavigationActions["Pan"].trigger()

    def getMarker(self, go_next=False):
        if go_next:
            self._lastMarkerID = (self._lastMarkerID + 1) % len(
                self.CURVE_MARKERS)
        return self.CURVE_MARKERS[self._lastMarkerID]

    def changeModeCurve(self, repaint=True):
        if not self.__repaintOK():
            return
        action = self._curveActionGroup.checkedAction()
        if action is self._pointsAction:
            for crv_view in self._curveViews.values():
                crv_view.setLineStyle("None")
        elif action is self._linesAction:
            for crv_view in self._curveViews.values():
                crv_view.setLineStyle("-")
        else:
            raise NotImplementedError
        if repaint:
            self.repaint()

    def setXLog(self, log, repaint=True):
        if not self.__repaintOK():
            return
        self._blockLogSignal = True
        if log:
            self._mplAxes.set_xscale('log')
            self._horLogarithmicAction.setChecked(True)
        else:
            self._mplAxes.set_xscale('linear')
            self._horLinearAction.setChecked(True)
        if repaint:
            self.autoFit()
            self.repaint()
        self._blockLogSignal = False

    def setYLog(self, log, repaint=True):
        if not self.__repaintOK():
            return
        self._blockLogSignal = True
        if log:
            self._mplAxes.set_yscale('log')
            self._verLogarithmicAction.setChecked(True)
        else:
            self._mplAxes.set_yscale('linear')
            self._verLinearAction.setChecked(True)
        if repaint:
            self.autoFit()
            self.repaint()
        self._blockLogSignal = False

    def setXSciNotation(self, sciNotation, repaint=True):
        self._axisXSciNotation = sciNotation
        self.changeFormatAxis()
        if repaint:
            self.repaint()

    def setYSciNotation(self, sciNotation, repaint=True):
        self._axisYSciNotation = sciNotation
        self.changeFormatAxis()
        if repaint:
            self.repaint()

    def onViewHorizontalMode(self, checked=True, repaint=True):
        if self._blockLogSignal:
            return
        action = self._horActionGroup.checkedAction()
        if action is self._horLinearAction:
            self.setXLog(False, repaint)
        elif action is self._horLogarithmicAction:
            self.setXLog(True, repaint)
        else:
            raise NotImplementedError

    def onViewVerticalMode(self, checked=True, repaint=True):
        if self._blockLogSignal:
            return
        action = self._verActionGroup.checkedAction()
        if action is self._verLinearAction:
            self.setYLog(False, repaint)
        elif action is self._verLogarithmicAction:
            self.setYLog(True, repaint)
        else:
            raise NotImplementedError
        if repaint:
            self.repaint()

    def __adjustFigureMargins(self, withLegend):
        """ Adjust figure margins to make room for the legend """
        if withLegend:
            leg = self._legend
            bbox = leg.get_window_extent()
            # In axes coordinates:
            bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
            if self._legendLoc == "right":
                self._mplFigure.subplots_adjust(right=1.0 -
                                                (bbox2.width + 0.02))
            elif self._legendLoc == "bottom":
                self._mplFigure.subplots_adjust(bottom=bbox2.height + 0.1)
        else:
            # Reset to default (rc) values
            self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)

    def setLegendVisible(self, visible, repaint=True):
        if visible and not self._actionLegend.isChecked():
            self._actionLegend.setChecked(True)
            self.showHideLegend(repaint=repaint)
        if not visible and self._actionLegend.isChecked():
            self._actionLegend.setChecked(False)
            self.showHideLegend(repaint=repaint)

    def showHideLegend(self, actionChecked=None, repaint=True):
        if not self.__repaintOK():  # Show/hide legend is extremely costly
            return

        show = self._actionLegend.isChecked()
        nCurves = len(self._curveViews)
        if nCurves > 10: fontSize = 'x-small'
        else: fontSize = None

        if nCurves == 0:
            # Remove legend
            leg = self._mplAxes.legend()
            if leg is not None: leg.remove()
        if show and nCurves > 0:
            # Recreate legend from scratch
            if self._legend is not None:
                self._legend = None
                self._mplAxes._legend = None
            if self._legendLoc == "bottom":
                self._legend = self._mplAxes.legend(loc="upper left",
                                                    bbox_to_anchor=(0.0, -0.05,
                                                                    1.0,
                                                                    -0.05),
                                                    borderaxespad=0.0,
                                                    mode="expand",
                                                    fancybox=True,
                                                    shadow=True,
                                                    ncol=3,
                                                    prop={
                                                        'size': fontSize,
                                                        'style': 'italic'
                                                    })
            elif self._legendLoc == "right":
                self._legend = self._mplAxes.legend(loc="upper left",
                                                    bbox_to_anchor=(1.02, 1.0),
                                                    borderaxespad=0.0,
                                                    ncol=1,
                                                    fancybox=True,
                                                    shadow=True,
                                                    prop={
                                                        'size': fontSize,
                                                        'style': 'italic'
                                                    })
            else:
                raise Exception(
                    "Invalid legend placement! Must be 'bottom' or 'right'")
            # Canvas must be drawn so we can adjust the figure placement:
            self._mplCanvas.draw()
            self.__adjustFigureMargins(withLegend=True)
        else:
            if self._legend is None:
                # Nothing to do
                return
            else:
                self._legend.set_visible(False)
                self._legend = None
                self._mplAxes._legend = None
                self._mplCanvas.draw()
                self.__adjustFigureMargins(withLegend=False)

        curr_crv = self._model._currentCurve
        if curr_crv is None: curr_title = None
        else: curr_title = curr_crv.getTitle()
        if self._legend is not None:
            for label in self._legend.get_texts():
                text = label.get_text()
                if (text == curr_title):
                    label.set_backgroundcolor('0.85')
                else:
                    label.set_backgroundcolor('white')

        if repaint:
            self.repaint()

    def onSettings(self, trigger=False, dlg_test=None):
        dlg = dlg_test or PlotSettings()
        dlg.titleEdit.setText(self._mplAxes.get_title())
        dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
        dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
        dlg.gridCheckBox.setChecked(
            self._mplAxes.xaxis._gridOnMajor
        )  # could not find a relevant API to check this
        dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
        dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
        xmin, xmax = self._mplAxes.get_xlim()
        ymin, ymax = self._mplAxes.get_ylim()
        xminText = "%g" % xmin
        xmaxText = "%g" % xmax
        yminText = "%g" % ymin
        ymaxText = "%g" % ymax
        dlg.axisXMinEdit.setText(xminText)
        dlg.axisXMaxEdit.setText(xmaxText)
        dlg.axisYMinEdit.setText(yminText)
        dlg.axisYMaxEdit.setText(ymaxText)
        # List of markers
        dlg.markerCurve.clear()
        for marker in self.CURVE_MARKERS:
            dlg.markerCurve.addItem(marker)
        curr_crv = self._model.getCurrentCurve()
        if not curr_crv is None:
            dlg.colorCurve.setEnabled(True)
            dlg.markerCurve.setEnabled(True)
            name = curr_crv.getTitle()
            dlg.nameCurve.setText(name)
            view = self._curveViews[curr_crv.getID()]
            marker = view.getMarker()
            color = view.getColor()
            index = dlg.markerCurve.findText(marker)
            dlg.markerCurve.setCurrentIndex(index)
            rgb = colors.colorConverter.to_rgb(color)
            dlg.setRGB(rgb[0], rgb[1], rgb[2])
        else:
            dlg.colorCurve.setEnabled(False)
            dlg.markerCurve.setEnabled(False)
            dlg.nameCurve.setText("")
            view = None
        if self._legend is None:
            dlg.showLegendCheckBox.setChecked(False)
            dlg.legendPositionComboBox.setEnabled(False)
        else:
            if self._legend.get_visible():
                dlg.showLegendCheckBox.setChecked(True)
                dlg.legendPositionComboBox.setEnabled(True)
                if self._legendLoc == "bottom":
                    dlg.legendPositionComboBox.setCurrentIndex(0)
                elif self._legendLoc == "right":
                    dlg.legendPositionComboBox.setCurrentIndex(1)
            else:
                dlg.showLegendCheckBox.setChecked(False)
                dlg.legendPositionComboBox.setEnabled(False)

        if dlg.exec_():
            # Title
            self._model.setTitle(dlg.titleEdit.text())
            # Axis
            self._model.setXLabel(dlg.axisXTitleEdit.text())
            self._model.setYLabel(dlg.axisYTitleEdit.text())
            # Grid
            if dlg.gridCheckBox.isChecked():
                self._mplAxes.grid(True)
            else:
                self._mplAxes.grid(False)
            # Legend
            if dlg.showLegendCheckBox.isChecked():
                self._actionLegend.setChecked(True)
                if dlg.legendPositionComboBox.currentIndex() == 0:
                    self._legendLoc = "bottom"
                elif dlg.legendPositionComboBox.currentIndex() == 1:
                    self._legendLoc = "right"
            else:
                self._actionLegend.setChecked(False)
            xminText = dlg.axisXMinEdit.text()
            xmaxText = dlg.axisXMaxEdit.text()
            yminText = dlg.axisYMinEdit.text()
            ymaxText = dlg.axisYMaxEdit.text()
            self._mplAxes.axis([
                float(xminText),
                float(xmaxText),
                float(yminText),
                float(ymaxText)
            ])
            self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
            self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
            self.changeFormatAxis()
            # Color and marker of the curve
            if view:
                view.setColor(dlg.getRGB())
                view.setMarker(
                    self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
            self.showHideLegend(repaint=True)
            self._mplCanvas.draw()
        pass

    def updateViewTitle(self):
        s = ""
        if self._model._title != "":
            s = " - %s" % self._model._title
        title = "CurvePlot (%d)%s" % (self._model.getID(), s)
        self._sgPyQt.setViewTitle(self._salomeViewID, title)

    def onCurrentPlotSetChange(self):
        """ Avoid a unnecessary call to update() when just switching current plot set! """
        pass

    def onCurrentCurveChange(self):
        curr_crv2 = self._model.getCurrentCurve()
        if curr_crv2 != self._currCrv:
            if self._currCrv is not None:
                view = self._curveViews[self._currCrv.getID()]
                view.toggleHighlight(False)
            if not curr_crv2 is None:
                view = self._curveViews[curr_crv2.getID()]
                view.toggleHighlight(True)
            self._currCrv = curr_crv2
            self.showHideLegend(repaint=False)  # redo legend
            self.repaint()

    def changeFormatAxis(self):
        if not self.__repaintOK():
            return

        # don't try to switch to sci notation if we are not using the
        # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
        if self._horLinearAction.isChecked():
            if self._axisXSciNotation:
                self._mplAxes.ticklabel_format(style='sci',
                                               scilimits=(0, 0),
                                               axis='x')
            else:
                self._mplAxes.ticklabel_format(style='plain', axis='x')
        if self._verLinearAction.isChecked():
            if self._axisYSciNotation:
                self._mplAxes.ticklabel_format(style='sci',
                                               scilimits=(0, 0),
                                               axis='y')
            else:
                self._mplAxes.ticklabel_format(style='plain', axis='y')

    def update(self):
        if self._salomeViewID is None:
            self.createPlotWidget()
            self._salomeViewID = self._sgPyQt.createView(
                "CurvePlot", self._plotWidget)
            Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
            self._sgPyQt.setViewVisible(self._salomeViewID, True)

        self.updateViewTitle()

        # Check list of curve views:
        set_mod = set(self._model._curves.keys())
        set_view = set(self._curveViews.keys())

        # Deleted/Added curves:
        dels = set_view - set_mod
        added = set_mod - set_view

        for d in dels:
            self.removeCurve(d)

        if not len(self._curveViews):
            # Reset color cycle
            self._mplAxes.set_color_cycle(None)

        for a in added:
            self.appendCurve(a)

        # Axes labels and title
        self._mplAxes.set_xlabel(self._model._xlabel)
        self._mplAxes.set_ylabel(self._model._ylabel)
        self._mplAxes.set_title(self._model._title)

        self.onViewHorizontalMode(repaint=False)
        self.onViewVerticalMode(repaint=False)
        self.changeModeCurve(repaint=False)
        self.showHideLegend(
            repaint=False
        )  # The canvas is repainted anyway (needed to get legend bounding box)
        self.changeFormatAxis()

        # Redo auto-fit
        self.autoFit(repaint=False)
        self.repaint()

    def onDataChange(self):
        # the rest is done in the CurveView:
        self.autoFit(repaint=True)

    def onMousePress(self, event):
        if event.button == 3:
            if self._panAction.isChecked():
                self._panAction.setChecked(False)
            if self._zoomAction.isChecked():
                self._zoomAction.setChecked(False)

    def onContextMenu(self, position):
        pos = self._mplCanvas.mapToGlobal(
            QtCore.QPoint(position.x(), position.y()))
        self._popupMenu.exec_(pos)

    def onScroll(self, event):
        # Event location (x and y)
        xdata = event.xdata
        ydata = event.ydata

        cur_xlim = self._mplAxes.get_xlim()
        cur_ylim = self._mplAxes.get_ylim()

        base_scale = 2.
        if event.button == 'down':
            # deal with zoom in
            scale_factor = 1 / base_scale
        elif event.button == 'up':
            # deal with zoom out
            scale_factor = base_scale
        else:
            # deal with something that should never happen
            scale_factor = 1

        new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
        new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

        relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
        rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])

        self._mplAxes.set_xlim(
            [xdata - new_width * (1 - relx), xdata + new_width * (relx)])
        self._mplAxes.set_ylim(
            [ydata - new_height * (1 - rely), ydata + new_height * (rely)])

        self.repaint()
        pass

    def onPressEvent(self, event):
        if event.button == 3:
            #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
            canvasSize = event.canvas.geometry()
            point = event.canvas.mapToGlobal(
                QtCore.QPoint(event.x,
                              canvasSize.height() - event.y))
            self._popupMenu.exec_(point)
        else:
            print "Press event on the other button"
        #if event.button == 3 :
        #  canvasSize = event.canvas.geometry()
        #  point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
        #  self._popupMenu.move(point)
        #  self._popupMenu.show()

    def onMotionEvent(self, event):
        print "OnMotionEvent ", event.button
        #if event.button == 3 :
        #  event.button = None
        #  return True

    def onReleaseEvent(self, event):
        print "OnReleaseEvent ", event.button