Repository: silx-kit/silx
Issue: Issue with histogram item and stats widget

There is apparently an issue with histogram items and the Stats widget.

To reproduce:
```
from silx import sx                                                     
w = sx.plot()                                                           
w.addHistogram((1, 2, 3), (1, 2, 3))  
```
Then click on "Options"

```
Traceback (most recent call last):
  File "silx/gui/plot/PlotWindow.py", line 466, in _customControlButtonMenu
    controlMenu.addAction(self.getStatsAction())
  File "silx/gui/plot/PlotWindow.py", line 655, in getStatsAction
    self._statsAction.setChecked(self.getStatsWidget().parent().isVisible())
  File "silx/gui/plot/PlotWindow.py", line 585, in getStatsWidget
    statsWidget = BasicStatsWidget(parent=self, plot=self)
  File "silx/gui/plot/StatsWidget.py", line 1285, in __init__
    stats=DEFAULT_STATS)
  File "silx/gui/plot/StatsWidget.py", line 1168, in __init__
    self.setStats(stats)
  File "silx/gui/plot/StatsWidget.py", line 1211, in setStats
    return self._getStatsTable().setStats(statsHandler=statsHandler)
  File "silx/gui/plot/StatsWidget.py", line 635, in setStats
    self._updateItemObserve()
  File "silx/gui/plot/StatsWidget.py", line 666, in _updateItemObserve
    self._addItem(item)
  File "silx/gui/plot/StatsWidget.py", line 817, in _addItem
    self._updateStats(item, data_changed=True)
  File "silx/gui/plot/StatsWidget.py", line 873, in _updateStats
    data_changed=data_changed, roi_changed=roi_changed)
  File "silx/gui/plot/stats/statshandler.py", line 199, in calculate
    data_changed=data_changed, roi_changed=roi_changed)
  File "silx/gui/plot/stats/stats.py", line 104, in calculate
    roi=roi)
  File "silx/gui/plot/stats/stats.py", line 423, in clipData
    self.values = numpy.ma.array(yData, mask=mask)
  File "numpy/ma/core.py", line 6562, in array
    ndmin=ndmin, shrink=shrink, order=order)
  File "numpy/ma/core.py", line 2909, in __new__
    raise MaskError(msg % (nd, nm))
numpy.ma.core.MaskError: Mask and data not compatible: data size is 3, mask size is 6.
```

Changed files:
silx/gui/plot/stats/stats.py, silx/gui/plot/StatsWidget.py

diff --git a/silx/gui/plot/StatsWidget.py b/silx/gui/plot/StatsWidget.py
index 26b48dbe2..6d8739ed2 100644
--- a/silx/gui/plot/StatsWidget.py
+++ b/silx/gui/plot/StatsWidget.py
@@ -868,6 +868,12 @@ class StatsTable(_StatsWidgetBase, TableWidget):
 
         statsHandler = self.getStatsHandler()
         if statsHandler is not None:
+            # _updateStats is call when the plot visible area change.
+            # to force stats update we consider roi changed
+            if self._statsOnVisibleData:
+                roi_changed = True
+            else:
+                roi_changed = False
             stats = statsHandler.calculate(
                 item, plot, self._statsOnVisibleData,
                 data_changed=data_changed, roi_changed=roi_changed)
diff --git a/silx/gui/plot/stats/stats.py b/silx/gui/plot/stats/stats.py
index f91c25ede..a81f7bb56 100644
--- a/silx/gui/plot/stats/stats.py
+++ b/silx/gui/plot/stats/stats.py
@@ -328,9 +328,8 @@ class _CurveContext(_ScatterCurveHistoMixInContext):
                 mask = self.mask
             else:
                 mask = (minX <= xData) & (xData <= maxX)
-            yData = yData[mask]
-            xData = xData[mask]
-            mask = numpy.zeros_like(yData)
+                mask = mask == 0
+                self._set_mask_validity(onlimits=onlimits, from_=minX, to_=maxX)
         elif roi:
             minX, maxX = roi.getFrom(), roi.getTo()
             if self.is_mask_valid(onlimits=onlimits, from_=minX, to_=maxX):
@@ -338,10 +337,11 @@ class _CurveContext(_ScatterCurveHistoMixInContext):
             else:
                 mask = (minX <= xData) & (xData <= maxX)
                 mask = mask == 0
-                mask = mask.astype(numpy.int32)
+                self._set_mask_validity(onlimits=onlimits, from_=minX, to_=maxX)
         else:
             mask = numpy.zeros_like(yData)
 
+        mask = mask.astype(numpy.uint32)
         self.xData = xData
         self.yData = yData
         self.values = numpy.ma.array(yData, mask=mask)
@@ -351,7 +351,6 @@ class _CurveContext(_ScatterCurveHistoMixInContext):
         else:
             self.min, self.max = None, None
         self.data = (xData, yData)
-
         self.axes = (xData,)
 
     def _checkContextInputs(self, item, plot, onlimits, roi):
@@ -391,32 +390,29 @@ class _HistogramContext(_ScatterCurveHistoMixInContext):
                 mask = self.mask
             else:
                 mask = (minX <= xData) & (xData <= maxX)
-            yData = yData[mask]
-            xData = xData[mask]
-            mask = numpy.zeros_like(yData)
+                mask = mask == 0
+                self._set_mask_validity(onlimits=onlimits, from_=minX, to_=maxX)
         elif roi:
-            if self.is_mask_valid(onlimits, from_=roi._fromdata, to_=roi._todata):
+            if self.is_mask_valid(onlimits=onlimits, from_=roi._fromdata, to_=roi._todata):
                 mask = self.mask
             else:
                 mask = (roi._fromdata <= xData) & (xData <= roi._todata)
                 mask = mask == 0
-                self._set_mask_validity(onlimits=True, from_=roi._fromdata,
+                self._set_mask_validity(onlimits=onlimits, from_=roi._fromdata,
                                         to_=roi._todata)
         else:
             mask = numpy.zeros_like(yData)
-
-        self.data = (xData, yData)
-        self.values = numpy.ma.array(yData, mask=mask)
-        self.axes = (xData,)
-
+        mask = mask.astype(numpy.uint32)
         self.xData = xData
         self.yData = yData
-
+        self.values = numpy.ma.array(yData, mask=(mask))
         unmasked_data = self.values.compressed()
         if len(unmasked_data) > 0:
             self.min, self.max = min_max(unmasked_data)
         else:
             self.min, self.max = None, None
+        self.data = (self.xData, self.yData)
+        self.axes = (self.xData,)
 
     def _checkContextInputs(self, item, plot, onlimits, roi):
         _StatsContext._checkContextInputs(self, item=item, plot=plot,