]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
Adding the StemModel (for diracs); improving the onPick() method in XYView; reducing...
authorjh777916 <juba.hamma@cea.fr>
Wed, 13 Sep 2023 13:36:37 +0000 (15:36 +0200)
committerjh777916 <juba.hamma@cea.fr>
Wed, 13 Sep 2023 13:36:37 +0000 (15:36 +0200)
19 files changed:
tools/CurvePlot/src/python/controller/PlotController.py
tools/CurvePlot/src/python/controller/__init__.py
tools/CurvePlot/src/python/controller/test [new file with mode: 0755]
tools/CurvePlot/src/python/controller/utils.py.in
tools/CurvePlot/src/python/model/BarModel.py
tools/CurvePlot/src/python/model/CMakeLists.txt
tools/CurvePlot/src/python/model/CurveModel.py
tools/CurvePlot/src/python/model/Model.py
tools/CurvePlot/src/python/model/PlotManager.py
tools/CurvePlot/src/python/model/StemModel.py [new file with mode: 0644]
tools/CurvePlot/src/python/model/XYPlotSetModel.py
tools/CurvePlot/src/python/test/TestDesktop_test.py
tools/CurvePlot/src/python/ui/PlotSettings.py
tools/CurvePlot/src/python/ui/PlotSettings.ui
tools/CurvePlot/src/python/views/BarView.py
tools/CurvePlot/src/python/views/CMakeLists.txt
tools/CurvePlot/src/python/views/CurveView.py
tools/CurvePlot/src/python/views/StemView.py [new file with mode: 0644]
tools/CurvePlot/src/python/views/XYView.py

index af4fe052cd1d6dd7ddc0862556a78a32199b226d..2812bc8055e41595674d74848f023ebaeb397bc7 100644 (file)
@@ -22,6 +22,7 @@ from .PlotManager import PlotManager
 from .CurveTabsView import CurveTabsView
 from .CurveModel import CurveModel
 from .BarModel import BarModel
+from .StemModel import StemModel
 from .TableModel import TableModel
 from .utils import Logger
 import numpy as np
@@ -217,6 +218,31 @@ class PlotController(object):
       pm.unlockRepaint()
     return curveID, plotSetID
 
+  @classmethod
+  def AddStemPlot(cls, x, y, curve_label="", x_label="", y_label="", append=True):
+    """ Testing some stuff with StemPlot"""
+
+    from .XYView import XYView
+    control = cls.GetInstance()
+    pm = control._plotManager
+    t = TableModel(control)
+    data = np.transpose(np.vstack([x, y]))
+    t.setData(data)
+    # ensure a single Matplotlib repaint for all operations to come in AddCurve
+    prevLock = pm.isRepaintLocked()
+    if not prevLock:
+      pm.lockRepaint()
+    curveID, plotSetID = control.plotStemPlotFromTable(t, x_col_index=0, y_col_index=1,
+                                                    curve_label=curve_label, append=append)
+    ps = pm._plotSets[plotSetID]
+    if x_label != "":
+      ps.setXLabel(x_label)
+    if y_label != "":
+      ps.setYLabel(y_label)
+    if not prevLock:
+      pm.unlockRepaint()
+    return curveID, plotSetID
+
   @classmethod
   def ExtendCurve(cls, crv_id, x, y):
     """ Add new points to an already created curve
@@ -799,7 +825,7 @@ class PlotController(object):
 
     cps.addCurve(bm)
     Logger.Debug(" --- model ID when adding the barplot : {}".format(bm.getID()))
-    
+
     mp = self._curveTabsView.mapModId2ViewId()
     xyview_id = mp[cps.getID()]
     xyview = self._curveTabsView._XYViews[xyview_id]
@@ -814,3 +840,56 @@ class PlotController(object):
     self.setModelListener(bm, xyview)
 
     return bm.getID(),cps.getID()
+
+  def plotStemPlotFromTable(self, table, x_col_index=0, y_col_index=1, curve_label="", append=True):
+    """
+    :returns: a tuple containing the unique curve ID and the plot set ID 
+    """
+    # Regardless of 'append', we must create a view if none there:
+    if self._plotManager.getCurrentPlotSet() is None or not append:
+      ps = self._plotManager.createXYPlotSet()
+      self.setModelListener(ps, self._curveBrowserView)
+      # For curve picking, controller must listen:
+      self.setModelListener(ps, self)
+      cps_title = table.getTitle()
+    else:
+      cps_title = None
+
+    cps = self._plotManager.getCurrentPlotSet()
+
+    sm = StemModel(self, table, y_col_index)
+    sm.setXAxisIndex(x_col_index)
+
+    # X axis label
+    tix = table.getColumnTitle(x_col_index)
+    if tix != "":
+      cps.setXLabel(tix)
+
+    # Curve label
+    if curve_label != "":
+      sm.setTitle(curve_label)
+    else:
+      ti = table.getColumnTitle(y_col_index)
+      if ti != "":
+        sm.setTitle(ti)
+
+    # Plot set title
+    if cps_title != "" and cps_title is not None:
+      Logger.Debug("about to set title to: " + cps_title)
+      cps.setTitle(cps_title)
+
+    cps.addCurve(sm)
+    mp = self._curveTabsView.mapModId2ViewId()
+    xyview_id = mp[cps.getID()]
+    xyview = self._curveTabsView._XYViews[xyview_id]
+
+    if cps_title is None:  # no plot set was created above
+      self._plotManager.setCurrentPlotSet(cps.getID())
+
+    # Make CurveBrowser and CurveView depend on changes in the curve itself:
+    self.setModelListener(sm, self._curveBrowserView)
+    self.setModelListener(sm, xyview._curveViews[sm.getID()])
+    # Upon change on the curve also update the full plot, notably for the auto-fit and the legend:
+    self.setModelListener(sm, xyview)
+
+    return sm.getID(),cps.getID()
\ No newline at end of file
index ba67712aeaf751c773df26945111fbbf6aa11ff3..8269a5e03b4af3a7e09d2fbe7bcdd9bbaa2328f0 100644 (file)
@@ -37,12 +37,14 @@ from .PlotController import PlotController
 from .TableModel import TableModel
 from .CurveModel import CurveModel
 from .BarModel import BarModel
+from .StemModel import StemModel
 from .PlotManager import PlotManager
 from .XYPlotSetModel import XYPlotSetModel
 
 ## The static API of PlotController is the main interface of the package and is hence exposed at package level:
 AddCurve = PlotController.AddCurve
 AddBarPlot = PlotController.AddBarPlot
+AddStemPlot = PlotController.AddStemPlot
 AddPlotSet = PlotController.AddPlotSet
 ExtendCurve = PlotController.ExtendCurve
 ResetCurve = PlotController.ResetCurve
diff --git a/tools/CurvePlot/src/python/controller/test b/tools/CurvePlot/src/python/controller/test
new file mode 100755 (executable)
index 0000000..2dd8715
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright (C) 2016-2022  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+class Logger(object):
+    """
+    Debug Info.
+    """
+    LOG_LEVEL = 0 # 0 means all, 1 means all but DEBUG, 2 means all but INFO and DEBUG, 3 only FATAL
+
+    @classmethod
+    def Debug(cls, msg):
+        """
+        Prints an information message to the standard output.
+        
+        :param msg: str -- The message to be printed.
+        
+        """
+        if cls.LOG_LEVEL <= 0:
+            cls.__log("[DEBUG]", msg)
+
+
+    @classmethod
+    def Info(cls, msg):
+        """
+        Prints an information message to the standard output.
+        
+        :param msg: str -- The message to be printed.
+        
+        """
+        if cls.LOG_LEVEL <= 1:
+            cls.__log("[INFO]", msg)
+
+    
+    @classmethod
+    def Warning(cls, msg):
+        """
+        Prints a warning message to the standard output.
+        
+        :param msg: str -- The message to be printed.
+        
+        """
+        if cls.LOG_LEVEL <= 2:
+            cls.__log("[WARNING]", msg)
+        
+    
+    @classmethod
+    def FatalError(cls, msg):
+        """
+        Prints an error message to the standard output.
+        
+        :param msg: str -- The message to be printed.
+        :raises: Exception.
+        
+        """
+        if cls.LOG_LEVEL <= 3:
+            cls.__log("[FATAL]", msg)
+            raise Exception(msg)
+
+    @classmethod
+    def __log(cls, typ, msg):
+        print("%s: %s" % (typ, msg))
+      
+def trQ(tag, context="CURVEPLOT"):
+  """ @return a QString read from the translation file """
+  from pyqtside.QtWidgets import QApplication 
+  return QApplication.translate(context, tag) 
+
+def trU(tag, context="CURVEPLOT"):
+  """ @return same as above, but returns a Python unicode string.  """
+  qs = trQ(tag, context)
+  return str(qs, 'utf-8')
+
+def flatten(xs):
+  if hasattr(xs, '__iter__'):
+    for x in xs:
+        if hasattr(x, '__iter__'):
+            yield from flatten(x)
+        else:
+            yield x
+  else:
+    yield xs
+
+def toUnicodeWithWarning(s, method_name):
+  try: 
+    s = str(s)
+  except:
+    Logger.Warning("%s - warning, passing non-unicode, non-ASCII string '%s'! Trying to convert myself to UTF-8 ..." % (method_name, s))
+    s = str(s, 'utf-8')
+  return s
+
+def completeResPath(fileName):
+  import os
+  subPath = "lib/python3.6/site-packages/salome/curveplot"
+  rd = os.environ.get("CURVEPLOT_ROOT_DIR", None)
+  if rd is None:
+    raise Exception("CURVEPLOT_ROOT_DIR is not defined!")
+  if 0:   # do not remove automatically modified in CMake config
+    subPath = "/volatile/catB/jh777916/Devel/WORK/OPERA-9.9.0-CO7/BUILD/GUI/local/curveplot"
+  filePath = os.path.join(rd, subPath, fileName)
+  return filePath
index 4af81f726bfb0ff34162474342b180f3fa5d8243..ef70e904f332bddaa4eb78bd03b3264bfb6c02a0 100644 (file)
@@ -94,6 +94,21 @@ def toUnicodeWithWarning(s, method_name):
     s = str(s, 'utf-8')
   return s
 
+def flatten(xs):
+  """ Takes a container (list or tuple) that can contain other list/tuples 
+  and returns a list of all the element as flattened. Useful to obtain artists from 
+  matplotlib plotting methods.
+  @return Flattened list containing all the elements (OR returns xs if xs is not iterable)
+  """
+  if hasattr(xs, '__iter__'):
+    for x in xs:
+        if hasattr(x, '__iter__'):
+            yield from flatten(x)
+        else:
+            yield x
+  else:
+    yield xs
+
 def completeResPath(fileName):
   import os
   subPath = "@SALOME_CURVEPLOT_INSTALL_PYTHON@"
index bc9debadba46065b9579ed96ecbf958de58f253d..b62f603d54958ef65fdc10bde87fcdf7f7f9500f 100644 (file)
@@ -52,8 +52,6 @@ class BarModel(Model):
 
   def setIsDirac(self, isD, silent=False):
     self._isDirac = isD
-    if not silent:
-      self.notifyChange("BarTypeChange")
 
   def setTable(self, t, silent=False):
     self._table = t
@@ -76,7 +74,7 @@ class BarModel(Model):
     ti = toUnicodeWithWarning(ti, "BarModel::setTitle()")
     self._title = ti
     if not silent:
-      self.notifyChange("BarTitleChange") 
+      self.notifyChange("CurveTitleChange") 
     
   def getTitle(self):
     return self._title
index 400a8785360458dd9001316cbebcdbe3eedd6709..2cc819175724f7b85e71248c6ff2210c8b6fd2b7 100644 (file)
@@ -21,6 +21,7 @@ SET(_all_lib_SCRIPTS
     Model.py
     CurveModel.py
     BarModel.py
+    StemModel.py
     TableModel.py
     PlotManager.py
     XYPlotSetModel.py
index 56367d7ffbaa6bd2ccb15c4676e865fd6ae34302..7db4e703f0e2a97fbcd18d9d29f33bd0ebfdf51a 100644 (file)
@@ -86,4 +86,4 @@ class CurveModel(Model):
     
   def getID(self):
     return self._idPlot
-    
+    
\ No newline at end of file
index 4fc9a633022fe21a6af194105a77bd036865e95a..d0012648cc19fd3ca2a9e86c00e4c271ab26dd09 100644 (file)
@@ -39,9 +39,10 @@ class Model(object):
         self._controller = controller
         self._id = self.__GenerateID()  # A unique ID for this class of object
 
-        # A unique ID for the traces (Curves and Bars), starting from 0
+        # A unique ID for the traces (Curves, Bars and Stems), starting from 0
         self._idPlot = self.COUNT_MODELS["CurveModel"] \
-                     + self.COUNT_MODELS["BarModel"] - 1         
+                     + self.COUNT_MODELS["BarModel"]   \
+                     + self.COUNT_MODELS["StemModel"]- 1
 
     def getID(self):
         return self._id
index d577f574c3897e8f9fdc0cfa235da4c5628051d3..f2b474c90fe8968de3ec2b2df41074b3f0b8676c 100644 (file)
@@ -50,18 +50,23 @@ class PlotManager(Model):
         return ps
     return None
 
-  def setCurrentCurve(self, curveId):
+  def setCurrentCurve(self, curveId, silent=False): # TAG CHECK : SILENT ajouté
+    Logger.Debug("PlotManager::setCurrentCurve() --> CALL")
     ps = self.getPlotSetContainingCurve(curveId)
     if ps is None and curveId != -1:
       raise ValueError("Invalid curve ID (%d)!" % curveId)
-    self.clearAllCurrentCurve()
+    Logger.Debug("PlotManager::setCurrentCurve() --> CALL clearAllCurrentCurve()")
+    self.clearAllCurrentCurve(silent=True)  # silencing the CurrentCurveChange : 1 draw less
+    Logger.Debug("PlotManager::setCurrentCurve() --> DONE clearAllCurrentCurve()")
     if curveId == -1:
       return -1
     ps_id = ps.getID()
     currPs = self.getCurrentPlotSet()
     if currPs is None or currPs.getID() != ps_id:
       self.setCurrentPlotSet(ps_id)
-    ps.setCurrentCurve(curveId)
+    Logger.Debug("PlotManager::setCurrentCurve() --> CALL ps.setCurrentCurve()")
+    ps.setCurrentCurve(curveId, silent=silent) # will actually notify CurrentCurveChange
+    Logger.Debug("PlotManager::setCurrentCurve() --> DONE")
     return ps_id
 
   def getCurrentCurve(self):
@@ -72,7 +77,7 @@ class PlotManager(Model):
 
   def clearAllCurrentCurve(self, silent=False):
     for psID in self._plotSets:
-      self._plotSets[psID].setCurrentCurve(-1)
+      self._plotSets[psID].setCurrentCurve(-1, silent=silent) # ajout du silent : 1 draw less
     if not silent:
       self.notifyChange("CurrentCurveChange")
 
@@ -87,7 +92,6 @@ class PlotManager(Model):
   def removeXYPlotSet(self, plotSetID):
     Logger.Debug("====> PlotManager::removeXYPlotSet() %d" % plotSetID)
     if plotSetID not in self._plotSets:
-      print(self._plotSets)
       raise ValueError("Plot set ID (%d) not found for deletion!" % plotSetID)
     ps = self._plotSets.pop(plotSetID)
     if self._currentPlotSet is ps:
@@ -115,4 +119,3 @@ class PlotManager(Model):
     for obj in self._plotSetsRepaint:
       obj.notifyChange()
     self._plotSetsRepaint = set()
-
diff --git a/tools/CurvePlot/src/python/model/StemModel.py b/tools/CurvePlot/src/python/model/StemModel.py
new file mode 100644 (file)
index 0000000..4f9339e
--- /dev/null
@@ -0,0 +1,89 @@
+# Copyright (C) 2016-2022  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from .Model import Model
+from .utils import toUnicodeWithWarning
+
+class StemModel(Model):
+  
+  def __init__(self, controller, table=None, index=-1):
+    Model.__init__(self, controller)
+
+    self._name = "StemModel"
+    self._title = "Stem %d" % self.getID()
+    self._table = table
+    self._yaxisIndex = index   # column index in the table
+    self._xaxisIndex = 0  # By default the first column of the table is used for the X-s
+    
+  def clone(self):
+    ret = StemModel(self._controller)
+    ret._title = self._title
+    ret._table = self._table  # TO CHECK: deep copy here? (I think not needed in Python ...)
+    ret._yaxisIndex = self._yaxisIndex
+    ret._xaxisIndex = self._xaxisIndex
+    return ret
+    
+  def setTable(self, t, silent=False):
+    self._table = t
+    if not silent:
+      self.notifyChange("DataChange") 
+    
+  def getTable(self):
+    return self._table
+    
+  def extendData(self, t, silent=False):
+    self._table.extend(t)
+    if not silent:
+      self.notifyChange("DataChange")
+    
+  def resetData(self):
+    self._table.clear()
+    self.notifyChange("DataChange")
+    
+  def setTitle(self, ti, silent=False):
+    ti = toUnicodeWithWarning(ti, "StemModel::setTitle()")
+    self._title = ti
+    if not silent:
+      self.notifyChange("CurveTitleChange") 
+    
+  def getTitle(self):
+    return self._title
+    
+  def getYAxisIndex(self):
+    return self._yaxisIndex
+  
+  def setYAxisIndex(self, idx, silent=False):
+    self._yaxisIndex = idx
+    if not silent:
+      self.notifyChange("YAxisIndexChange")
+  
+  def getXAxisIndex(self):
+    return self._xaxisIndex
+  
+  def setXAxisIndex(self, idx, silent=False):
+    sh = self._table.getShape()
+    if idx >= sh[1]:
+      raise ValueError("Index out of bound (is %d, but max is %d)" % (idx, sh[1]))
+    self._xaxisIndex = idx
+    if not silent:
+      self.notifyChange("XAxisIndexChange")
+    
+  def getID(self):
+    return self._idPlot
+    
\ No newline at end of file
index c960e258b300e8498cea28c0d1bc71943544e576..919733824bed72fc6211ec13ba456382292e4f13 100644 (file)
@@ -50,26 +50,14 @@ class XYPlotSetModel(Model):
       raise ValueError("Invalid curve ID (%d)!" % curveID)
     self._currentCurve = self._curves.get(curveID, None)
     if not silent:
-      self.notifyChange("CurrentCurveChange") 
+      self.notifyChange("CurrentCurveChange")
 
   def getCurrentCurve(self):
     return self._currentCurve
       
   def addCurve(self, curve, silent=False):
-    # if self._curves:  
-    #   lastID = max(list(self._curves.keys()))
-    # else :
-    #   lastID = -1
-    
-    # # if (curve.getID() >= lastID):
-    # #   newLastID = curve.getID()
-    # # else :
-    # self._lastID += 1
-
     self._curves[curve.getID()] = curve
-
     Logger.Debug(" ---- XYPlotSetModel::addCurve( curve ) -> curve.getID() = {}".format(curve.getID()))
-    # Logger.Debug(" ---- XYPlotSetModel::addCurve( curve ) -> _curves new last ID = {}".format(self._lastID))
     if not silent:
       self.notifyChange("AddCurve")
   
index 0a3ef732a65a243c126482d1f8133d18466caf13..7d68b1214f962eedaafba07dfc395fa75e405878 100755 (executable)
@@ -76,8 +76,9 @@ class TestDesktop_test(QMainWindow):
         self.addTabAction.triggered.connect(self.addTab)
         self.memAction.triggered.connect(self.memPrint)
         self.perfTestAction.triggered.connect(self.perfTest)
-        self.addBarPlotAction.triggered.connect(self.addBarplot)
+        self.addStemPlotAction.triggered.connect(self.addStemplot)
         self.addBarPlotNonDiracAction.triggered.connect(self.addBarplotNonDirac)
+        self.addBarPlotDiracAction.triggered.connect(self.addBarplotDirac)
 
 
     def generateID(self):
@@ -95,8 +96,9 @@ class TestDesktop_test(QMainWindow):
         self.addTabActionID = self.generateID()
         self.memActionID = self.generateID()
         self.perfActionID = self.generateID()
-        self.addBarPlotActionID = self.generateID()
+        self.addStemPlotActionID = self.generateID()
         self.addBarPlotNonDiracActionID = self.generateID()
+        self.addBarPlotDiracActionID = self.generateID()
         
         # Menus
         self.etudeMenuID = self.generateID()
@@ -113,8 +115,9 @@ class TestDesktop_test(QMainWindow):
         self.addTabAction = ca(self.addTabActionID, "Add tab", "Add tab", "", "")
         self.memAction = ca(self.memActionID, "Display used mem", "Display used mem", "", "")
         self.perfTestAction = ca(self.perfActionID, "Perf test", "Perf test", "", "")
-        self.addBarPlotAction = ca(self.addBarPlotActionID, "Add BarPlot - Dirac", "Add BarPlot - Dirac", "", "")
-        self.addBarPlotNonDiracAction = ca(self.addBarPlotActionID, "Add BarPlot - Non Dirac", "Add BarPlot- Non Dirac", "", "")
+        self.addStemPlotAction = ca(self.addStemPlotActionID, "Add StemPlot - Dirac", "Add StemPlot - Dirac", "", "")
+        self.addBarPlotNonDiracAction = ca(self.addBarPlotNonDiracActionID, "Add BarPlot - Non Dirac", "Add BarPlot- Non Dirac", "", "")
+        self.addBarPlotDiracAction = ca(self.addBarPlotDiracActionID, "Add BarPlot - Dirac", "Add BarPlot - Dirac", "", "")
 
     def createToolbars(self):
         pass
@@ -136,8 +139,9 @@ class TestDesktop_test(QMainWindow):
         self._sgPyQt.createMenu(self.addPSAction, curveMenu)
         self._sgPyQt.createMenu(self.memAction, curveMenu)
         self._sgPyQt.createMenu(self.perfTestAction, curveMenu)
-        self._sgPyQt.createMenu(self.addBarPlotAction, curveMenu)
+        self._sgPyQt.createMenu(self.addStemPlotAction, curveMenu)
         self._sgPyQt.createMenu(self.addBarPlotNonDiracAction, curveMenu)
+        self._sgPyQt.createMenu(self.addBarPlotDiracAction, curveMenu)
         
         dummyMenu = self._sgPyQt.createMenu( "Dummy", -1, self.dummyMenuID, self._sgPyQt.defaultMenuGroup() )
         self._sgPyQt.createMenu(self.addTabAction, dummyMenu)
@@ -193,13 +197,13 @@ class TestDesktop_test(QMainWindow):
       print("Elapsed: %.2f" % ( time() - t0))
 
     @pyqtSlot()
-    def addBarplot(self):
-      energy_val = np.array([8.01, 8.33, 8.67, 8.99, 9.01, 9.33,  9.99])
+    def addBarplotDirac(self):
+      energy_val = np.array([0., 1.15, 5.2, 8.01, 8.33, 8.67, 8.99, 9.01, 9.33, 9.99, 15.1, 17.8, 19.7])
       # energy_val = np.array([8.15, 8.42, 8.75, 8.88, 9.1, 9.55,  9.79])
 
-      intensity =  np.random.rand(energy_val.size) * 5
+      intensity =  np.random.rand(energy_val.size) * 10
       isDirac = True
-      width_dirac = np.full(energy_val.size, 0.01)
+      width_dirac = np.full(energy_val.size, 0.05)
       
       _, ps_id = curveplot.AddBarPlot(energy_val, intensity, width_dirac, isDirac, 
                                         x_label="Energy", y_label="Intensity", append=True)
@@ -208,16 +212,32 @@ class TestDesktop_test(QMainWindow):
       # if self.cnt >= 0:
       #   QTimer.singleShot(self.timeLap, self, SLOT("itemDel()"))
 
+
+    @pyqtSlot()
+    def addStemplot(self):
+      energy_val = np.array([0., 1.15, 5.2, 8.01, 8.33, 8.67, 8.99, 9.01, 9.33, 9.99, 15.1, 17.8, 19.7])
+      # energy_val = np.array([8.15, 8.42, 8.75, 8.88, 9.1, 9.55,  9.79])
+
+      intensity =  np.random.rand(energy_val.size) * 10
+      
+      _, ps_id = curveplot.AddStemPlot(energy_val, intensity, 
+                                        x_label="Energy", y_label="Intensity", append=True)
+
+      curveplot.SetLegendVisible(ps_id, True)
+      # if self.cnt >= 0:
+      #   QTimer.singleShot(self.timeLap, self, SLOT("itemDel()"))
+
+
     @pyqtSlot()
     def addBarplotNonDirac(self):
       # Testing with TableModel
       from curveplot import TableModel
       from random import random
      
-      energy_inf = np.array([8.01, 8.33, 8.67, 8.99, 9.01, 9.33,  9.99])
-      energy_sup = np.array([8.33, 8.67, 8.99, 9.01, 9.33, 9.99, 10.01])
+      energy_inf = np.array([0., 1.15, 5.2, 8.01, 8.33, 8.67, 8.99, 9.01, 9.33, 9.99, 15.1, 17.8, 19.7])
+      energy_sup = np.array([1.15, 5.2, 8.01, 8.33, 8.67, 8.99, 9.01, 9.33,  9.99, 15.1, 17.8, 19.7, 20.])
       deltaE = energy_sup - energy_inf
-      intensity =  np.random.rand(energy_inf.size) * 5
+      intensity =  np.random.rand(energy_inf.size) * 10
       isDirac = False      
 
       t = TableModel(None)
index 876a3847c91a48fc6208b4d61125d6a15984fb16..6020d41a58cd40c6396f355faad5cf153b040b54 100644 (file)
@@ -51,6 +51,11 @@ class PlotSettings(QDialog):
       r, g, b = [c/255.0 for c in col.getRgb()[:3]]
       self.setRGB(r, g, b)
 
+  @pyqtSlot()
+  def onOpacityChanged(self):
+    opacity = self.sliderOpacity.value() / 100.
+    self.valueOpacity.setText(f"{opacity:.2f}")
+
   def setSelectedCurveName(self, name):
     self.nameCurve.setText(name)
     if name :
index 0672d6f55118f98940f4560cad53c4fd4d9805dd..12a206fe1a28ea418e576ea3b08c6b91c7ac7b75 100644 (file)
        <property name="minimumSize">
         <size>
          <width>0</width>
-         <height>100</height>
+         <height>120</height>
         </size>
        </property>
        <property name="title">
              </property>
             </widget>
            </item>
+           <item>
+            <widget class="QLabel" name="valueOpacityLabel">
+             <property name="text">
+              <string>Opacity :</string>
+             </property>
+            </widget>
+           </item>
+            <item>
+            <widget class="QSlider" name="sliderOpacity">
+             <property name="enabled">
+              <bool>true</bool>
+             </property>
+             <property name="orientation">
+              <enum>Qt::Horizontal</enum>
+             </property>             
+             <property name="minimum">
+              <number>0</number>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>             
+            </widget>
+           </item>   
+           <item>
+            <widget class="QLabel" name="valueOpacity">
+             <property name="text">
+              <string></string>
+             </property>
+            </widget>
+           </item>                   
           </layout>
          </item>
          <item>
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>sliderOpacity</sender>
+   <signal>valueChanged()</signal>
+   <receiver>PlotSettings</receiver>
+   <slot>onOpacityChanged()</slot>
+  </connection>  
  </connections>
  <slots>
   <slot>onShowLegend(int)</slot>
index 47125c92523aac51f27aafccc545fd428faf5344..1c4eb4e4ad2a97a350e4e9e13874beb62968dc0d 100644 (file)
@@ -26,12 +26,12 @@ class BarView(View):
   def __init__(self, controller, parentXYView):
     View.__init__(self, controller)
     self._mplAxes = None
-    self._mplBars = None
+    self._mplArtist = None
 
     self._isHighlighted = False
     self._initialLineWidth = None
     self._initialEdgeColorBar = None
-    self._initialAlpha = None
+    self._initialOpacity = None
     self._initialZOrder = None
     self._parentXYView = parentXYView
     
@@ -42,8 +42,8 @@ class BarView(View):
     self._mplAxes = axes
         
   def erase(self):
-      self._mplBars.remove()
-      self._mplBars = None
+      self._mplArtist.remove()
+      self._mplArtist = None
     
   def draw(self):
     m = self._model
@@ -51,24 +51,28 @@ class BarView(View):
     x_align = "center" if m.getIsDirac() else "edge"
     d = self._model.getTable().getData()
 
-    self._mplBars = self._mplAxes.bar(d[:, x_idx], d[:, height_idx], width=d[:, width_idx],
+    # Bar returns a BarContainer (list of Artists)
+    self._mplArtist = self._mplAxes.bar(d[:, x_idx], d[:, height_idx], width=d[:, width_idx],
                      align=x_align, label=m._title, alpha= 0.7,
                       picker=self._PICKER_PRECISION_BAR)
 
-    self._initialLineWidth = 0. #self._mplBars[0].get_linewidth()
-    self._initialEdgeColorBar = self._mplBars[0].get_edgecolor()
-    self._initialAlpha = self._mplBars[0].get_alpha()
-    self._initialZOrder = self._mplBars[0].get_zorder()
+    self._initialLineWidth = 0. #self._mplArtist[0].get_linewidth()
+    self._initialEdgeColorBar = self._mplArtist[0].get_edgecolor()
+    self._initialOpacity = self._mplArtist[0].get_alpha()
+    self._initialZOrder = self._mplArtist[0].get_zorder()
+
+    # print(f"TYPE ARTISTS BARS: {type(self._mplArtist)}")
+    # print(f"SIZE ARTISTS BARS: {len(self._mplArtist)}")
 
   def onCurveTitleChange(self):
-    if self._mplBars is None:
+    if self._mplArtist is None:
       return
-    self._mplBars.set_label(self._model._title)
+    self._mplArtist.set_label(self._model._title)
   
   def update(self):
     Logger.Debug("BarView::udpate")
 
-    if self._mplBars is None:
+    if self._mplArtist is None:
       return
     color = self.getColor()
     opacity = self.getAlpha()
@@ -79,7 +83,7 @@ class BarView(View):
     self.toggleHighlight(self._isHighlighted, force=True)
  
   def toggleHighlight(self, highlight, force=False):
-    bar = self._mplBars.patches # TAG CHECK
+    bar = self._mplArtist.patches # TAG CHECK
     if highlight and (force or not self._isHighlighted):
       for rect in bar : 
         rect.set_linewidth(1.)
@@ -90,7 +94,7 @@ class BarView(View):
     elif not highlight and (force or self._isHighlighted):
       for rect in bar : 
         rect.set_linewidth(self._initialLineWidth)
-        rect.set_alpha(self._initialAlpha)
+        rect.set_alpha(self._initialOpacity)
         rect.set_edgecolor(self._initialEdgeColorBar)
         rect.set_zorder(self._initialZOrder)
       self._isHighlighted = False
@@ -102,19 +106,30 @@ class BarView(View):
     return self._isHighlighted
     
   def setColor(self, rgb_color):
-    for rect in self._mplBars :
+    for rect in self._mplArtist :
       rect.set_facecolor(rgb_color)
     
   def getColor(self):
-    if self._mplBars is None:
+    if self._mplArtist is None:
       return None
-    return self._mplBars[0].get_facecolor()
+    return self._mplArtist[0].get_facecolor()
     
   def setAlpha(self, opacity):
-    for rect in self._mplBars:
+    for rect in self._mplArtist:
       rect.set_alpha(opacity)
     
   def getAlpha(self):
-    if self._mplBars is None:
+    if self._mplArtist is None:
       return None
-    return self._mplBars[0].get_alpha()
+    return self._mplArtist[0].get_alpha()
+
+  def setInitialAlpha(self, opacity):
+    self._initialOpacity = opacity
+
+  def getInitialAlpha(self):
+    if self._mplArtist is None:
+      return None
+    return self._initialOpacity
+
+  def setLineStyle(self, linestyle=""):
+    return
\ No newline at end of file
index 6ec77d1f168976c64815f64b5dbd6d73d1433a3a..67f359c3168ca23ada4f22bb6fabc7f41c5cfcd8 100644 (file)
@@ -23,6 +23,7 @@ SET(_all_lib_SCRIPTS
     CurveTabsView.py
     CurveView.py
     BarView.py
+    StemView.py
     XYView.py
 )
 
index 064172c46d5c641ae04125d4cc051c2f06f158a8..a7ead94ada8a9233466897d05eab7ecbb1c8f397 100644 (file)
@@ -26,39 +26,49 @@ class CurveView(View):
   def __init__(self, controller, parentXYView):
     View.__init__(self, controller)
     self._mplAxes = None
-    self._mplLines = None
+    self._mplArtist = None
 
     self._isHighlighted = False
     self._initialLineWidth = None
+    self._initialOpacity = None
+    self._initialZOrder = None    
     self._parentXYView = parentXYView
     
     self._marker = None
     self._color = None
     self._lineStyle = None
-    
+    self._opacity = None
+
   def setMPLAxes(self, axes):
     self._mplAxes = axes
         
   def erase(self):
-    self._mplAxes.lines.remove(self._mplLines[0])
-    self._mplLines = None
+    self._mplAxes.lines.remove(self._mplArtist)
+    self._mplArtist = None
     
   def draw(self):
+    Logger.Debug("CurveView::draw")
     m = self._model
     x_idx, y_idx = m.getXAxisIndex(), m.getYAxisIndex()
     d = self._model.getTable().getData()
-    self._mplLines = self._mplAxes.plot(d[:, x_idx], d[:, y_idx], label=m._title, 
+    plots = self._mplAxes.plot(d[:, x_idx], d[:, y_idx], label=m._title, alpha = 1.,
                                         picker=self._PICKER_PRECISION)
-    self._initialLineWidth = self._mplLines[0].get_linewidth()
-  
+    self._mplArtist = plots[0]
+    self._initialLineWidth = self._mplArtist.get_linewidth()
+    self._initialOpacity = self._mplArtist.get_alpha()
+    self._initialZOrder = self._mplArtist.get_zorder()
+
+    # print(f"TYPE ARTISTS LINES: {type(self._mplArtist)}")
+    # print(f"SIZE ARTISTS LINES: {len(self._mplArtist)}")
+
   def onCurveTitleChange(self):
-    if self._mplLines is None:
+    if self._mplArtist is None:
       return
-    self._mplLines[0].set_label(self._model._title)
+    self._mplArtist.set_label(self._model._title)
   
   def update(self):
     Logger.Debug("CurveView::udpate")
-    if self._mplLines is None:
+    if self._mplArtist is None:
       return
     lineStyle, marker, color = self.getLineStyle(), self.getMarker(), self.getColor()
     self.erase()
@@ -70,30 +80,34 @@ class CurveView(View):
     self.toggleHighlight(self._isHighlighted, force=True)
     
   def setLineStyle(self, lin_style):
-    lin = self._mplLines[0] 
+    lin = self._mplArtist 
     lin.set_linestyle(lin_style)
     
   def getLineStyle(self):
-    if self._mplLines is None:
+    if self._mplArtist is None:
       return None
-    return self._mplLines[0].get_linestyle()
+    return self._mplArtist.get_linestyle()
     
   def setMarker(self, marker):
-    lin = self._mplLines[0]
+    lin = self._mplArtist
     lin.set_marker(marker)
 
   def getMarker(self):
-    if self._mplLines is None:
+    if self._mplArtist is None:
       return None
-    return self._mplLines[0].get_marker()
+    return self._mplArtist.get_marker()
 
   def toggleHighlight(self, highlight, force=False):
-    lin = self._mplLines[0]
+    lin = self._mplArtist
     if highlight and (force or not self._isHighlighted):
+      lin.set_alpha(1.)
       lin.set_linewidth(2*self._initialLineWidth)
+      lin.set_zorder(1000)
       self._isHighlighted = True
     elif not highlight and (force or self._isHighlighted):
+      lin.set_alpha(self._initialOpacity)
       lin.set_linewidth(self._initialLineWidth)
+      lin.set_zorder(self._initialZOrder)
       self._isHighlighted = False
     else:
       # Nothing to do, already the correct state
@@ -103,19 +117,27 @@ class CurveView(View):
     return self._isHighlighted
     
   def setColor(self, rgb_color):
-    lin = self._mplLines[0]
+    lin = self._mplArtist
     lin.set_color(rgb_color)
     
   def getColor(self):
-    if self._mplLines is None:
+    if self._mplArtist is None:
       return None
-    return self._mplLines[0].get_color()
+    return self._mplArtist.get_color()
     
   def setAlpha(self, opacity):
-    lin = self._mplLines[0]
+    lin = self._mplArtist
     lin.set_alpha(opacity)
-    
+
   def getAlpha(self):
-    if self._mplLines is None:
+    if self._mplArtist is None:
+      return None
+    return self._mplArtist.get_alpha()
+
+  def setInitialAlpha(self, opacity):
+    self._initialOpacity = opacity
+
+  def getInitialAlpha(self):
+    if self._mplArtist is None:
       return None
-    return self._mplLines[0].get_alpha()
+    return self._initialOpacity
diff --git a/tools/CurvePlot/src/python/views/StemView.py b/tools/CurvePlot/src/python/views/StemView.py
new file mode 100644 (file)
index 0000000..3007a64
--- /dev/null
@@ -0,0 +1,180 @@
+# Copyright (C) 2016-2022  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from .View import View
+from .utils import Logger
+
+class StemView(View):
+  _PICKER_PRECISION = 10  #pts
+    
+  def __init__(self, controller, parentXYView):
+    View.__init__(self, controller)
+    self._mplAxes = None
+    self._mplArtist = None
+
+    self._isHighlighted = False
+    self._initialLineWidth = None
+    self._initialOpacity = None
+    self._initialZOrder = None    
+    self._parentXYView = parentXYView
+    
+    self._marker = None
+    self._color = None
+    self._lineStyle = None
+    self._opacity = None
+
+  def setMPLAxes(self, axes):
+    self._mplAxes = axes
+        
+  def erase(self):
+    self._mplArtist.remove()
+    self._mplArtist = None
+    
+  def draw(self):
+    m = self._model
+    x_idx, y_idx = m.getXAxisIndex(), m.getYAxisIndex()
+    d = self._model.getTable().getData()
+
+    # stem returns a StemContainer : (markerline <Line2D>, stemlines<list(Line2D)>, baseline<Line2D> )
+    self._mplArtist = self._mplAxes.stem(d[:, x_idx], d[:, y_idx], label=m._title,\
+                                           linefmt='b-', markerfmt='_', basefmt=' ')
+
+    self._initialLineWidth = self._mplArtist[0].get_linewidth()
+    self._initialZOrder = self._mplArtist[0].get_zorder()
+
+    self.setAlpha(1.)
+    self.setInitialAlpha(1.)
+    self.setColor(next(self._mplAxes._get_lines.prop_cycler)['color'])
+    self.setMarkerEdgeWidth(self._initialLineWidth)
+    self.setMarkerSize(8)
+
+    # picker for the stemlines
+    for lin in self._mplArtist[1]:
+      lin.set_picker(self._PICKER_PRECISION)
+
+    Logger.Debug(f"** Init lw: {self._initialLineWidth}")
+    Logger.Debug(f"** Init opacity : {self._initialOpacity}")
+    Logger.Debug(f"** Init zorder : {self._initialZOrder}")
+
+  def onCurveTitleChange(self):
+    if self._mplArtist is None:
+      return
+    self._mplArtist.set_label(self._model._title)
+  
+  def update(self):
+    Logger.Debug("StemView::udpate")
+    if self._mplArtist is None:
+      return
+    lineStyle, marker, color = self.getLineStyle(), self.getMarker(), self.getColor()
+    self.erase()
+    self.draw()
+    # Reset correctly color, marker and highlight state
+    self.setLineStyle(lineStyle)
+    self.setMarker(marker)
+    self.setColor(color)
+    self.toggleHighlight(self._isHighlighted, force=True)
+    
+  def setLineStyle(self, lin_style):
+    for lin in self._mplArtist[1]:
+      lin.set_linestyle(lin_style)
+    
+  def getLineStyle(self):
+    if self._mplArtist is None:
+      return None
+    return self._mplArtist[1][0].get_linestyle()
+    
+  def setMarker(self, marker):
+    markerline = self._mplArtist[0]
+    markerline.set_marker(marker)
+
+  def setMarkerSize(self, ms):
+    markerline = self._mplArtist[0]
+    markerline.set_markersize(ms)
+
+  def setMarkerEdgeWidth(self, mew):
+    markerline = self._mplArtist[0]
+    markerline.set_markeredgewidth(mew)
+
+  def getMarker(self):
+    if self._mplArtist is None:
+      return None
+    return self._mplArtist[0].get_marker()
+
+  def toggleHighlight(self, highlight, force=False):
+    markerline, stemlines, _ = self._mplArtist
+    if highlight and (force or not self._isHighlighted):
+      self.setAlpha(1.)
+      self.setZOrder(1000)
+      self.setLineWidth(2*self._initialLineWidth)
+      self.setMarkerEdgeWidth(2*self._initialLineWidth)
+      self._isHighlighted = True
+    elif not highlight and (force or self._isHighlighted):
+      self.setAlpha(self._initialOpacity)
+      self.setZOrder(self._initialZOrder)
+      self.setLineWidth(self._initialLineWidth)
+      self.setMarkerEdgeWidth(self._initialLineWidth)
+      self._isHighlighted = False
+    else:
+      # Nothing to do, already the correct state
+      return
+    
+  def isHighlighted(self):
+    return self._isHighlighted
+
+  def setZOrder(self, zorder):
+    markerline, stemlines, _ = self._mplArtist
+    markerline.set_zorder(zorder)
+    for lin in stemlines:
+      lin.set_zorder(zorder)
+
+  def setLineWidth(self, lw):
+    markerline, stemlines, _ = self._mplArtist
+    markerline.set_linewidth(lw)
+    for lin in stemlines:
+      lin.set_linewidth(lw)
+
+  def setColor(self, rgb_color):
+    markerline, stemlines, _ = self._mplArtist
+    markerline.set_color(rgb_color)
+    for lin in stemlines:
+      lin.set_color(rgb_color)
+    
+  def getColor(self):
+    if self._mplArtist is None:
+      return None
+    return self._mplArtist[0].get_color()
+    
+  def setAlpha(self, opacity):
+    markerline, stemlines, _ = self._mplArtist
+    markerline.set_alpha(opacity)
+    for lin in stemlines:
+      lin.set_alpha(opacity)
+
+  def getAlpha(self):
+    if self._mplArtist is None:
+      return None
+    return self._mplArtist[0].get_alpha()
+
+  def setInitialAlpha(self, opacity):
+    self._initialOpacity = opacity
+
+  def getInitialAlpha(self):
+    if self._mplArtist is None:
+      return None
+    return self._initialOpacity
index 9c039c9d0c2df2976e59da8f3f599ab3cb69aa93..dcf016fef151fb6f080c926a2d32483e6bb9df83 100644 (file)
@@ -28,9 +28,10 @@ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationTool
 from .View import View
 from .CurveView import CurveView
 from .BarView import BarView
+from .StemView import StemView
 from .PlotWidget import PlotWidget
 from .PlotSettings import PlotSettings
-from .utils import Logger, trQ
+from .utils import Logger, trQ, flatten
 
 class EventHandler(QObject):
   """ Handle the right-click properly so that it only triggers the contextual menu """
@@ -67,6 +68,7 @@ class XYView(View):
                     "v",  # triangle_down
                     "H",  # hexagon2
                     "d",  # thin diamond
+                    "_",  # horizontal line
                     "",   # NO MARKER
                    ]
 
@@ -137,6 +139,15 @@ class XYView(View):
     Logger.Debug(" ** > appendBarplot() crvID = {}".format(curveID))
     self._curveViews[curveID] = newB
 
+  def appendStemplot(self, curveID):
+    Logger.Debug(" > Appending a stem model")
+    newS = StemView(self._controller, self)
+    newS.setModel(self._model._curves[curveID])
+    newS.setMPLAxes(self._mplAxes)
+    newS.draw()
+    Logger.Debug(" ** > appendStemplot() crvID = {}".format(curveID))
+    self._curveViews[curveID] = newS
+
   def removeCurve(self, curveID):
     v = self._curveViews.pop(curveID)
     v.erase()
@@ -186,9 +197,12 @@ class XYView(View):
   def onPick(self, event):
     """ MPL callback when picking
     """
-    Logger.Debug(" > Event onPick : {}".format(event))
+    Logger.Debug(" > Event onPick : {} ---> START EVENT".format(event))
+
+    if event.mouseevent.dblclick:
+      Logger.Debug(" > > > Double click !")
 
-    if event.mouseevent.button == 1:
+    if (event.mouseevent.button == 1 and not event.mouseevent.dblclick):
       selected_id = -1
       a = event.artist
       Logger.Debug(" > Artist picked : {}".format(a))
@@ -197,48 +211,65 @@ class XYView(View):
       if self._legend is not None:
         h, l = self._mplAxes.get_legend_handles_labels()
 
-        # For text
+        # Checks if the text legend has been clicked
         for leg_idx,legText in enumerate(self._legend.get_texts()):
           if legText is a :
             # Gets the associated plot from the axes.legend handles
             selected_plot = h[leg_idx]
             # Gets the id from the curveViews
             for crv_id, cv in list(self._curveViews.items()):
-              if (cv._model._name == "BarModel") :
-                if selected_plot is cv._mplBars:
-                  selected_id = crv_id
-              if (cv._model._name == "CurveModel") :
-                if selected_plot in cv._mplLines:
+                if selected_plot is cv._mplArtist:
                   selected_id = crv_id
+                  break
 
+        # Checks if the plot symbol legend has been clicked
         for leg_idx,legHandler in enumerate(self._legend.legendHandles):
           if legHandler is a :
             # Gets the associated plot from the axes.legend handles
             selected_plot = h[leg_idx]
             # Gets the id from the curveViews
             for crv_id, cv in list(self._curveViews.items()):
-              if (cv._model._name == "BarModel") :
-                if selected_plot is cv._mplBars:
-                  selected_id = crv_id
-              if (cv._model._name == "CurveModel") :
-                if selected_plot in cv._mplLines:
+                if selected_plot is cv._mplArtist:
                   selected_id = crv_id
+                  break
 
             Logger.Debug("Legend {} selected".format(selected_id))
 
+      # Checks if an artist has been picked inside the plotting area
       for crv_id, cv in list(self._curveViews.items()):
-        if (cv._model._name == "BarModel") :
-          # Checks if the picked artist is in the list of Rectangles
-          if a in cv._mplBars:
-            selected_id = crv_id
-            Logger.Debug("Barplot {} selected".format(selected_id))
-        else:
-          if cv._mplLines[0] is a:
-            selected_id = crv_id
-            Logger.Debug("Curve {} selected".format(selected_id))
-
-      # Use the plotmanager so that other plot sets get their current reset:
-      self._controller._plotManager.setCurrentCurve(selected_id)
+        # Permits to get back to the intiial opacity of each Artists ... let's keep it in case we want that
+        # self._curveViews[crv_id].setAlpha(self._curveViews[crv_id].getInitialAlpha())
+
+          # Checks if the picked artist is in the list of artists
+        if a in flatten(cv._mplArtist):
+          selected_id = crv_id
+          Logger.Debug("Curve/Barplot/Stemplot {} selected".format(selected_id))
+          break         
+
+      # Use the plotmanager to set the current curve so that other plot sets get their current reset:
+      if self._currCrv is not None:     
+        if selected_id != self._currCrv.getID() :
+          # Setting the current curve only if different 
+          if event.mouseevent.dblclick:
+            # if double click (2nd onPick event on same artist) : set the _currCrv variable without notifying
+            Logger.Debug(" onPick::setCurrentCurve() SILENT TRUE ...")
+            self._controller._plotManager.setCurrentCurve(selected_id, silent=True)
+            Logger.Debug(" onPick::setCurrentCurve() SILENT TRUE : DONE")
+          else :
+            # if NOT double click : set the _currCrv variable and notifying (repaint)
+            Logger.Debug(" onPick::setCurrentCurve() SILENT FALSE ...")            
+            self._controller._plotManager.setCurrentCurve(selected_id, silent=False)
+            Logger.Debug(" onPick::setCurrentCurve() SILENT FALSE : DONE")            
+      else : 
+        Logger.Debug(" onPick::setCurrentCurve() SILENT FALSE ...")            
+        self._controller._plotManager.setCurrentCurve(selected_id, silent=False)
+        Logger.Debug(" onPick::setCurrentCurve() SILENT FALSE : DONE")        
+
+    else : #if event.mouseevent.dblclick:
+      self.showOnlySelected()
+
+    Logger.Debug(" > Event onPick : {} ---> END EVENT".format(event))
+
 
   def createAndAddLocalAction(self, icon_file, short_name):
     return self._toolbar.addAction(self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)
@@ -248,6 +279,7 @@ class XYView(View):
     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)
@@ -260,6 +292,12 @@ class XYView(View):
 
     self._popupMenu = QtWidgets.QMenu()
     self._popupMenu.addAction(self._actionLegend)
+    self._actionShowAll = self._sgPyQt.createAction(2, "Show all", "Show all", "", "")
+    self._actionShowOnlySelected = self._sgPyQt.createAction(3, "Show only selected", "Show only selected", "", "")
+    self._popupMenu.addAction(self._actionShowAll)
+    self._popupMenu.addAction(self._actionShowOnlySelected)
+    self._actionShowAll.triggered.connect(self.showAll)
+    self._actionShowOnlySelected.triggered.connect(self.showOnlySelected)
 
     # Connect evenement for the graphic scene
     self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
@@ -340,7 +378,8 @@ class XYView(View):
                                         filters,
                                         trQ("DUMP_VIEW_FILE"),
                                         False )
-    name = str(fileName)
+    name = str(fileName[0]) # Modif portage
+    Logger.Debug(f" >> Save fig filename : {name}")
     if name != "":
       self._mplAxes.figure.savefig(name)
     pass
@@ -380,12 +419,10 @@ class XYView(View):
     action = self._curveActionGroup.checkedAction()
     if action is self._pointsAction :
       for crv_view in list(self._curveViews.values()):
-        if (crv_view._model._name != "BarModel") :
-          crv_view.setLineStyle("None")
+        crv_view.setLineStyle("None")
     elif action is self._linesAction :
       for crv_view in list(self._curveViews.values()):
-        if (crv_view._model._name != "BarModel") :
-          crv_view.setLineStyle("-")
+        crv_view.setLineStyle("-")
     else :
       raise NotImplementedError
     if repaint:
@@ -480,6 +517,23 @@ class XYView(View):
       self._actionLegend.setChecked(False)
       self.showHideLegend(repaint=repaint)
 
+  def showAll(self):
+    for crv_id, cv in list(self._curveViews.items()):
+      cv.toggleHighlight(False, force=True)
+    # self._controller._plotManager.clearAllCurrentCurve()
+    current_ps = self._controller._plotManager.getCurrentPlotSet()
+    current_ps.setCurrentCurve(-1)
+    return
+
+  def showOnlySelected(self):
+    """ Show only selected curve/barplot """
+    if self._currCrv is not None:
+      for crv_id, cv in list(self._curveViews.items()):
+        if (crv_id != self._currCrv.getID()) :
+          cv.setAlpha(0.05)
+      self.showHideLegend() # redo legend
+    return
+
   def showHideLegend(self, actionChecked=None, repaint=True):
     if not self.__repaintOK():  # Show/hide legend is extremely costly
       return
@@ -536,15 +590,8 @@ class XYView(View):
       curr_crv = self._model._currentCurve
       if curr_crv is not None :
         h, l = self._mplAxes.get_legend_handles_labels()
-        print(h)
         view = self._curveViews[curr_crv.getID()]
-        if curr_crv._name == "CurveModel":
-          whatToGet = view._mplLines[0]
-        elif curr_crv._name == "BarModel":
-          whatToGet = view._mplBars
-        else:
-          raise Exception("Invalid model for the current curve")
-        legCurr_idx = h.index(whatToGet)
+        legCurr_idx = h.index(view._mplArtist)
         legLabelCurr_crv = self._legend.get_texts()[legCurr_idx]
         legLabelCurr_crv.set_backgroundcolor('0.85')
       else :
@@ -578,18 +625,26 @@ class XYView(View):
     if curr_crv is None:
       dlg.colorCurve.setEnabled(False)
       dlg.markerCurve.setEnabled(False)
+      dlg.sliderOpacity.setEnabled(False)
       dlg.setSelectedCurveName("")
       view = None
     else:
       dlg.colorCurve.setEnabled(True)
+      dlg.sliderOpacity.setEnabled(True)
+
+      # dlg.sliderOpacity.valueChanged.connect(dlg.onOpacityChanged())
       name = curr_crv.getTitle()
       dlg.setSelectedCurveName(name)
       view = self._curveViews[curr_crv.getID()]
       color = view.getColor()
+
+      opacity = view.getInitialAlpha()
+      dlg.sliderOpacity.setValue(opacity*100)
+
       rgb = colors.colorConverter.to_rgb(color)
       dlg.setRGB(rgb[0],rgb[1],rgb[2])
 
-      if curr_crv._name == "CurveModel":
+      if (curr_crv._name == "CurveModel" or  curr_crv._name == "StemModel"):
         # List of markers
         dlg.markerCurve.show()
         dlg.markerCurveLabel.show()
@@ -652,7 +707,9 @@ class XYView(View):
       if view:
         view.setColor(dlg.getRGB())
         crvModel = view._model
-        if curr_crv._name == "CurveModel":
+        newOpacity = dlg.sliderOpacity.value() / 100.
+        view.setInitialAlpha(newOpacity)
+        if (curr_crv._name == "CurveModel" or curr_crv._name == "StemModel"):
           view.setMarker(self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
         if dlg.nameCurve.text() != crvModel.getTitle():
           Logger.Debug("XYView : about to cahnge crv title after settings")
@@ -673,18 +730,37 @@ class XYView(View):
     pass
 
   def onCurrentCurveChange(self):
-    Logger.Debug("XYView::onCurrentCurveChange()")
+    Logger.Debug("XYView::onCurrentCurveChange() : CALL")
     curr_crv2 = self._model.getCurrentCurve()
+
+    if self._currCrv is None :
+      id_debug1 = "None"
+    else :
+      id_debug1 = self._currCrv.getID()
+
+    if curr_crv2 is None :
+      id_debug2 = "None"
+    else :
+      id_debug2 = curr_crv2.getID()
+    
+    Logger.Debug(f"XYView::onCurrentCurveChange() : Curr crv 1 ID : {id_debug1}")
+    Logger.Debug(f"XYView::onCurrentCurveChange() : Curr crv 2 ID : {id_debug2}")
+
     if curr_crv2 != self._currCrv:
+      Logger.Debug("XYView::onCurrentCurveChange() : NEW CURVE SELECTED")
       if self._currCrv is not None:
+        Logger.Debug("XYView::onCurrentCurveChange() :      Toggle -> False")
         view = self._curveViews[self._currCrv.getID()]
         view.toggleHighlight(False)
       if not curr_crv2 is None:
+        Logger.Debug("XYView::onCurrentCurveChange() :      Toggle -> True")
         view = self._curveViews[curr_crv2.getID()]
         view.toggleHighlight(True)
       self._currCrv = curr_crv2
       self.showHideLegend(repaint=False) # redo legend
       self.repaint()
+      Logger.Debug("XYView::onCurrentCurveChange() : REPAINT")
+    Logger.Debug("XYView::onCurrentCurveChange() : END")
 
   def changeFormatAxis(self) :
     if not self.__repaintOK():
@@ -716,15 +792,15 @@ class XYView(View):
     set_mod = set(self._model._curves.keys())
     set_view = set(self._curveViews.keys())
 
-    Logger.Debug(" --- List of models ID: {}".format(set_mod))    
-    Logger.Debug(" --- List of views  ID: {}".format(set_view))    
+    Logger.Debug(" --- List of models ID: {}".format(set_mod))    
+    Logger.Debug(" --- List of views  ID: {}".format(set_view))    
 
     # Deleted/Added curves:
     dels = set_view - set_mod
     added = set_mod - set_view
 
-    Logger.Debug(" --- List of dels  ID: {}".format(dels))
-    Logger.Debug(" --- List of added ID: {}".format(added))
+    Logger.Debug(" --- List of dels  ID: {}".format(dels))
+    Logger.Debug(" --- List of added ID: {}".format(added))
 
     for d in dels:
       self.removeCurve(d)
@@ -739,6 +815,8 @@ class XYView(View):
         self.appendCurve(a)
       elif ( self._model._curves[a]._name == "BarModel" )  :
         self.appendBarplot(a)
+      elif ( self._model._curves[a]._name == "StemModel" )  :
+        self.appendStemplot(a)
       else:
         # Should not happen
         raise Exception("Invalid specified curve type")