Repository: silx-kit/silx
Issue: [plot] Plot2D widget's ColorBar disappear


To reproduce:
```
from silx import sx
w = sx.imshow()
w.hide()
w.show()
```
and the colorbar is not visible.

Changed files:
silx/gui/plot/PlotWindow.py, silx/gui/plot/actions/control.py, silx/gui/plot/StackView.py, silx/gui/plot/ColorBar.py

diff --git a/silx/gui/plot/ColorBar.py b/silx/gui/plot/ColorBar.py
index e092f7a32..2db7b7995 100644
--- a/silx/gui/plot/ColorBar.py
+++ b/silx/gui/plot/ColorBar.py
@@ -27,7 +27,7 @@
 
 __authors__ = ["H. Payno", "T. Vincent"]
 __license__ = "MIT"
-__date__ = "11/04/2017"
+__date__ = "15/02/2018"
 
 
 import logging
@@ -65,11 +65,12 @@ class ColorBarWidget(qt.QWidget):
     :param plot: PlotWidget the colorbar is attached to (optional)
     :param str legend: the label to set to the colorbar
     """
+    sigVisibleChanged = qt.Signal(bool)
+    """Emitted when the property `visible` have changed."""
 
     def __init__(self, parent=None, plot=None, legend=None):
         self._isConnected = False
         self._plot = None
-        self._viewAction = None
         self._colormap = None
         self._data = None
 
@@ -127,15 +128,18 @@ class ColorBarWidget(qt.QWidget):
             self._plot.sigPlotSignal.connect(self._defaultColormapChanged)
             self._isConnected = True
 
+    def setVisible(self, isVisible):
+        # isHidden looks to be always synchronized, while isVisible is not
+        wasHidden = self.isHidden()
+        qt.QWidget.setVisible(self, isVisible)
+        if wasHidden != self.isHidden():
+            self.sigVisibleChanged.emit(not self.isHidden())
+
     def showEvent(self, event):
         self._connectPlot()
-        if self._viewAction is not None:
-            self._viewAction.setChecked(True)
 
     def hideEvent(self, event):
         self._disconnectPlot()
-        if self._viewAction is not None:
-            self._viewAction.setChecked(False)
 
     def getColormap(self):
         """
@@ -230,21 +234,6 @@ class ColorBarWidget(qt.QWidget):
             and ticks"""
         return self._colorScale
 
-    def getToggleViewAction(self):
-        """Returns a checkable action controlling this widget's visibility.
-
-        :rtype: QAction
-        """
-        if self._viewAction is None:
-            self._viewAction = qt.QAction(self)
-            self._viewAction.setText('Colorbar')
-            self._viewAction.setIcon(icons.getQIcon('colorbar'))
-            self._viewAction.setToolTip('Show/Hide the colorbar')
-            self._viewAction.setCheckable(True)
-            self._viewAction.setChecked(self.isVisible())
-            self._viewAction.toggled[bool].connect(self.setVisible)
-        return self._viewAction
-
 
 class _VerticalLegend(qt.QLabel):
     """Display vertically the given text
@@ -405,8 +394,8 @@ class ColorScaleBar(qt.QWidget):
 
         :param val: if True, set the labels visible, otherwise set it not visible
         """
-        self._maxLabel.show() if val is True else self._maxLabel.hide()
-        self._minLabel.show() if val is True else self._minLabel.hide()
+        self._minLabel.setVisible(val)
+        self._maxLabel.setVisible(val)
 
     def _updateMinMax(self):
         """Update the min and max label if we are in the case of the
@@ -779,7 +768,7 @@ class _TickBar(qt.QWidget):
         if self._norm == Colormap.Colormap.LINEAR:
             return 1 - (val - self._vmin) / (self._vmax - self._vmin)
         elif self._norm == Colormap.Colormap.LOGARITHM:
-            return 1 - (numpy.log10(val) - numpy.log10(self._vmin))/(numpy.log10(self._vmax) - numpy.log(self._vmin))
+            return 1 - (numpy.log10(val) - numpy.log10(self._vmin)) / (numpy.log10(self._vmax) - numpy.log(self._vmin))
         else:
             raise ValueError('Norm is not recognized')
 
diff --git a/silx/gui/plot/PlotWindow.py b/silx/gui/plot/PlotWindow.py
index eebf9a15d..5c7e66191 100644
--- a/silx/gui/plot/PlotWindow.py
+++ b/silx/gui/plot/PlotWindow.py
@@ -29,7 +29,7 @@ The :class:`PlotWindow` is a subclass of :class:`.PlotWidget`.
 
 __authors__ = ["V.A. Sole", "T. Vincent"]
 __license__ = "MIT"
-__date__ = "22/11/2017"
+__date__ = "15/02/2018"
 
 import collections
 import logging
@@ -41,6 +41,7 @@ from . import actions
 from . import items
 from .actions import medfilt as actions_medfilt
 from .actions import fit as actions_fit
+from .actions import control as actions_control
 from .actions import histogram as actions_histogram
 from . import PlotToolButtons
 from .PlotTools import PositionInfo
@@ -114,6 +115,9 @@ class PlotWindow(PlotWidget):
         self._maskToolsDockWidget = None
         self._consoleDockWidget = None
 
+        # Create color bar, hidden by default for backward compatibility
+        self._colorbar = ColorBarWidget(parent=self, plot=self)
+
         # Init actions
         self.group = qt.QActionGroup(self)
         self.group.setExclusive(False)
@@ -169,6 +173,12 @@ class PlotWindow(PlotWidget):
         self.colormapAction.setVisible(colormap)
         self.addAction(self.colormapAction)
 
+        self.colorbarAction = self.group.addAction(
+            actions_control.ColorBarAction(self, self))
+        self.colorbarAction.setVisible(False)
+        self.addAction(self.colorbarAction)
+        self._colorbar.setVisible(False)
+
         self.keepDataAspectRatioButton = PlotToolButtons.AspectToolButton(
             parent=self, plot=self)
         self.keepDataAspectRatioButton.setVisible(aspectRatio)
@@ -220,10 +230,6 @@ class PlotWindow(PlotWidget):
         self._panWithArrowKeysAction = None
         self._crosshairAction = None
 
-        # Create color bar, hidden by default for backward compatibility
-        self._colorbar = ColorBarWidget(parent=self, plot=self)
-        self._colorbar.setVisible(False)
-
         # Make colorbar background white
         self._colorbar.setAutoFillBackground(True)
         palette = self._colorbar.palette()
@@ -315,7 +321,7 @@ class PlotWindow(PlotWidget):
                 custom_banner=banner,
                 parent=self)
             self.addTabbedDockWidget(self._consoleDockWidget)
-            #self._consoleDockWidget.setVisible(True)
+            # self._consoleDockWidget.setVisible(True)
             self._consoleDockWidget.toggleViewAction().toggled.connect(
                 self.getConsoleAction().setChecked)
 
@@ -698,6 +704,16 @@ class PlotWindow(PlotWidget):
         """
         return self._medianFilter2DAction
 
+    def getColorBarAction(self):
+        """Action toggling the colorbar show/hide action
+
+        .. warning:: to show/hide the plot colorbar call directly the ColorBar
+            widget using getColorBarWidget()
+
+        :rtype: actions.PlotAction
+        """
+        return self.colorbarAction
+
 
 class Plot1D(PlotWindow):
     """PlotWindow with tools specific for curves.
@@ -759,6 +775,7 @@ class Plot2D(PlotWindow):
         self.profile = ProfileToolBar(plot=self)
         self.addToolBar(self.profile)
 
+        self.colorbarAction.setVisible(True)
         self.getColorBarWidget().setVisible(True)
 
         # Put colorbar action after colormap action
@@ -766,9 +783,6 @@ class Plot2D(PlotWindow):
         for index, action in enumerate(actions):
             if action is self.getColormapAction():
                 break
-        self.toolBar().insertAction(
-            actions[index + 1],
-            self.getColorBarWidget().getToggleViewAction())
 
     def _getImageValue(self, x, y):
         """Get status bar value of top most image at position (x, y)
diff --git a/silx/gui/plot/StackView.py b/silx/gui/plot/StackView.py
index 4e7b85ccd..1fb188c4c 100644
--- a/silx/gui/plot/StackView.py
+++ b/silx/gui/plot/StackView.py
@@ -69,7 +69,7 @@ Example::
 
 __authors__ = ["P. Knobel", "H. Payno"]
 __license__ = "MIT"
-__date__ = "11/09/2017"
+__date__ = "15/02/2018"
 
 import numpy
 
@@ -82,6 +82,7 @@ from .PlotTools import LimitsToolBar
 from .Profile import Profile3DToolBar
 from ..widgets.FrameBrowser import HorizontalSliderWithBrowser
 
+from silx.gui.plot.actions import control as actions_control
 from silx.utils.array_like import DatasetView, ListOfImages
 from silx.math import calibration
 from silx.utils.deprecation import deprecated_warning
@@ -245,9 +246,8 @@ class StackView(qt.QMainWindow):
         for index, action in enumerate(actions):
             if action is self._plot.getColormapAction():
                 break
-        self._plot.toolBar().insertAction(
-            actions[index + 1],
-            self._plot.getColorBarWidget().getToggleViewAction())
+        self._colorbarAction = actions_control.ColorBarAction(self._plot, self._plot)
+        self._plot.toolBar().insertAction(actions[index + 1], self._colorbarAction)
 
     def _plotCallback(self, eventDict):
         """Callback for plot events.
@@ -972,6 +972,16 @@ class StackView(qt.QMainWindow):
         """
         return self._plot.getActiveImage(just_legend=just_legend)
 
+    def getColorBarAction(self):
+        """Returns the action managing the visibility of the colorbar.
+
+        .. warning:: to show/hide the plot colorbar call directly the ColorBar
+            widget using getColorBarWidget()
+
+        :rtype: QAction
+        """
+        return self._colorbarAction
+
     def remove(self, legend=None,
                kind=('curve', 'image', 'item', 'marker')):
         """See :meth:`Plot.Plot.remove`"""
@@ -1102,7 +1112,7 @@ class StackViewMainWindow(StackView):
         menu.addSeparator()
         menu.addAction(self._plot.resetZoomAction)
         menu.addAction(self._plot.colormapAction)
-        menu.addAction(self._plot.getColorBarWidget().getToggleViewAction())
+        menu.addAction(self.getColorBarAction())
 
         menu.addAction(actions.control.KeepAspectRatioAction(self._plot, self))
         menu.addAction(actions.control.YAxisInvertedAction(self._plot, self))
diff --git a/silx/gui/plot/actions/control.py b/silx/gui/plot/actions/control.py
index 07d8d04ae..ac6dc2fbf 100644
--- a/silx/gui/plot/actions/control.py
+++ b/silx/gui/plot/actions/control.py
@@ -50,7 +50,7 @@ from __future__ import division
 
 __authors__ = ["V.A. Sole", "T. Vincent", "P. Knobel"]
 __license__ = "MIT"
-__date__ = "08/01/2018"
+__date__ = "15/02/2018"
 
 from . import PlotAction
 import logging
@@ -401,6 +401,39 @@ class ColormapAction(PlotAction):
         self._dialog.setColormap(colormap)
 
 
+class ColorBarAction(PlotAction):
+    """QAction opening the ColorBarWidget of the specified plot.
+
+    :param plot: :class:`.PlotWidget` instance on which to operate
+    :param parent: See :class:`QAction`
+    """
+    def __init__(self, plot, parent=None):
+        self._dialog = None  # To store an instance of ColormapDialog
+        super(ColorBarAction, self).__init__(
+            plot, icon='colorbar', text='Colorbar',
+            tooltip="Show/Hide the colorbar",
+            triggered=self._actionTriggered,
+            checkable=True, parent=parent)
+        colorBarWidget = self.plot.getColorBarWidget()
+        old = self.blockSignals(True)
+        self.setChecked(colorBarWidget.isVisibleTo(self.plot))
+        self.blockSignals(old)
+        colorBarWidget.sigVisibleChanged.connect(self._widgetVisibleChanged)
+
+    def _widgetVisibleChanged(self, isVisible):
+        """Callback when the colorbar `visible` property change."""
+        if self.isChecked() == isVisible:
+            return
+        self.setChecked(isVisible)
+
+    def _actionTriggered(self, checked=False):
+        """Create a cmap dialog and update active image and default cmap."""
+        colorBarWidget = self.plot.getColorBarWidget()
+        if not colorBarWidget.isHidden() == checked:
+            return
+        self.plot.getColorBarWidget().setVisible(checked)
+
+
 class KeepAspectRatioAction(PlotAction):
     """QAction controlling aspect ratio on a :class:`.PlotWidget`.