summaryrefslogtreecommitdiff
path: root/silx/gui/data
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@debian.org>2021-09-07 14:39:36 +0200
committerPicca Frédéric-Emmanuel <picca@debian.org>2021-09-07 14:39:36 +0200
commitd3194b1a9c4404ba93afac43d97172ab24c57098 (patch)
treea1604130e1401dc1cbd084518ed72869dc92b86f /silx/gui/data
parentb3bea947efa55d2c0f198b6c6795b3177be27f45 (diff)
New upstream version 0.15.2+dfsg
Diffstat (limited to 'silx/gui/data')
-rw-r--r--silx/gui/data/ArrayTableModel.py78
-rw-r--r--silx/gui/data/DataViews.py6
-rw-r--r--silx/gui/data/NXdataWidgets.py9
-rw-r--r--silx/gui/data/RecordTableView.py42
-rw-r--r--silx/gui/data/test/test_arraywidget.py15
5 files changed, 134 insertions, 16 deletions
diff --git a/silx/gui/data/ArrayTableModel.py b/silx/gui/data/ArrayTableModel.py
index 8805241..b7bd9c4 100644
--- a/silx/gui/data/ArrayTableModel.py
+++ b/silx/gui/data/ArrayTableModel.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -74,6 +74,10 @@ class ArrayTableModel(qt.QAbstractTableModel):
:param sequence[int] perspective: See documentation
of :meth:`setPerspective`.
"""
+
+ MAX_NUMBER_OF_SECTIONS = 10e6
+ """Maximum number of displayed rows and columns"""
+
def __init__(self, parent=None, data=None, perspective=None):
qt.QAbstractTableModel.__init__(self, parent)
@@ -173,7 +177,7 @@ class ArrayTableModel(qt.QAbstractTableModel):
if row_dim is None:
# 0-D and 1-D arrays
return 1
- return self._array.shape[row_dim]
+ return min(self._array.shape[row_dim], self.MAX_NUMBER_OF_SECTIONS)
def columnCount(self, parent_idx=None):
"""QAbstractTableModel method
@@ -182,14 +186,55 @@ class ArrayTableModel(qt.QAbstractTableModel):
if col_dim is None:
# 0-D array
return 1
- return self._array.shape[col_dim]
+ return min(self._array.shape[col_dim], self.MAX_NUMBER_OF_SECTIONS)
+
+ def __isClipped(self, orientation=qt.Qt.Vertical) -> bool:
+ """Returns whether or not array is clipped in a given orientation"""
+ if orientation == qt.Qt.Vertical:
+ dim = self._getRowDim()
+ else:
+ dim = self._getColumnDim()
+ return (dim is not None and
+ self._array.shape[dim] > self.MAX_NUMBER_OF_SECTIONS)
+
+ def __isClippedIndex(self, index) -> bool:
+ """Returns whether or not index's cell represents clipped data."""
+ if not index.isValid():
+ return False
+ if index.row() == self.MAX_NUMBER_OF_SECTIONS - 2:
+ return self.__isClipped(qt.Qt.Vertical)
+ if index.column() == self.MAX_NUMBER_OF_SECTIONS - 2:
+ return self.__isClipped(qt.Qt.Horizontal)
+ return False
+
+ def __clippedData(self, role=qt.Qt.DisplayRole):
+ """Return data for cells representing clipped data"""
+ if role == qt.Qt.DisplayRole:
+ return "..."
+ elif role == qt.Qt.ToolTipRole:
+ return "Dataset is too large: display is clipped"
+ else:
+ return None
def data(self, index, role=qt.Qt.DisplayRole):
"""QAbstractTableModel method to access data values
in the format ready to be displayed"""
if index.isValid():
- selection = self._getIndexTuple(index.row(),
- index.column())
+ if self.__isClippedIndex(index): # Special displayed for clipped data
+ return self.__clippedData(role)
+
+ row, column = index.row(), index.column()
+
+ # When clipped, display last data of the array in last column of the table
+ if (self.__isClipped(qt.Qt.Vertical) and
+ row == self.MAX_NUMBER_OF_SECTIONS - 1):
+ row = self._array.shape[self._getRowDim()] - 1
+ if (self.__isClipped(qt.Qt.Horizontal) and
+ column == self.MAX_NUMBER_OF_SECTIONS - 1):
+ column = self._array.shape[self._getColumnDim()] - 1
+
+ selection = self._getIndexTuple(row, column)
+
if role == qt.Qt.DisplayRole:
return self._formatter.toString(self._array[selection], self._array.dtype)
@@ -224,17 +269,30 @@ class ArrayTableModel(qt.QAbstractTableModel):
"""QAbstractTableModel method
Return the 0-based row or column index, for display in the
horizontal and vertical headers"""
+ if self.__isClipped(orientation): # Header is clipped
+ if section == self.MAX_NUMBER_OF_SECTIONS - 2:
+ # Represent clipped data
+ return self.__clippedData(role)
+
+ elif section == self.MAX_NUMBER_OF_SECTIONS - 1:
+ # Display last index from data not table
+ if role == qt.Qt.DisplayRole:
+ if orientation == qt.Qt.Vertical:
+ dim = self._getRowDim()
+ else:
+ dim = self._getColumnDim()
+ return str(self._array.shape[dim] - 1)
+ else:
+ return None
+
if role == qt.Qt.DisplayRole:
- if orientation == qt.Qt.Vertical:
- return "%d" % section
- if orientation == qt.Qt.Horizontal:
- return "%d" % section
+ return "%d" % section
return None
def flags(self, index):
"""QAbstractTableModel method to inform the view whether data
is editable or not."""
- if not self._editable:
+ if not self._editable or self.__isClippedIndex(index):
return qt.QAbstractTableModel.flags(self, index)
return qt.QAbstractTableModel.flags(self, index) | qt.Qt.ItemIsEditable
diff --git a/silx/gui/data/DataViews.py b/silx/gui/data/DataViews.py
index d9958de..b18a813 100644
--- a/silx/gui/data/DataViews.py
+++ b/silx/gui/data/DataViews.py
@@ -1042,6 +1042,8 @@ class _Plot2dView(DataView):
widget.setKeepDataAspectRatio(True)
widget.getXAxis().setLabel('X')
widget.getYAxis().setLabel('Y')
+ maskToolsWidget = widget.getMaskToolsDockWidget().widget()
+ maskToolsWidget.setItemMaskUpdated(True)
return widget
def clear(self):
@@ -1156,6 +1158,8 @@ class _ComplexImageView(DataView):
widget.getPlot().setKeepDataAspectRatio(True)
widget.getXAxis().setLabel('X')
widget.getYAxis().setLabel('Y')
+ maskToolsWidget = widget.getPlot().getMaskToolsDockWidget().widget()
+ maskToolsWidget.setItemMaskUpdated(True)
return widget
def clear(self):
@@ -1254,6 +1258,8 @@ class _StackView(DataView):
widget.setLabels(self.axesNames(None, None))
# hide default option panel
widget.setOptionVisible(False)
+ maskToolWidget = widget.getPlotWidget().getMaskToolsDockWidget().widget()
+ maskToolWidget.setItemMaskUpdated(True)
return widget
def clear(self):
diff --git a/silx/gui/data/NXdataWidgets.py b/silx/gui/data/NXdataWidgets.py
index 271b267..be7d0e3 100644
--- a/silx/gui/data/NXdataWidgets.py
+++ b/silx/gui/data/NXdataWidgets.py
@@ -371,6 +371,8 @@ class ArrayImagePlot(qt.QWidget):
normalization=Colormap.LINEAR))
self._plot.getIntensityHistogramAction().setVisible(True)
self._plot.setKeepDataAspectRatio(True)
+ maskToolWidget = self._plot.getMaskToolsDockWidget().widget()
+ maskToolWidget.setItemMaskUpdated(True)
# not closable
self._selector = NumpyAxesSelector(self)
@@ -511,7 +513,7 @@ class ArrayImagePlot(qt.QWidget):
self._plot.getYAxis().setScale('linear')
self._plot.addImage(image, legend=legend,
origin=origin, scale=scale,
- replace=True)
+ replace=True, resetzoom=False)
else:
xaxisscale, yaxisscale = self._axis_scales
@@ -587,6 +589,8 @@ class ArrayComplexImagePlot(qt.QWidget):
self._plot.getPlot().getIntensityHistogramAction().setVisible(True)
self._plot.setKeepDataAspectRatio(True)
+ maskToolWidget = self._plot.getPlot().getMaskToolsDockWidget().widget()
+ maskToolWidget.setItemMaskUpdated(True)
# not closable
self._selector = NumpyAxesSelector(self)
@@ -769,6 +773,9 @@ class ArrayStackPlot(qt.QWidget):
self.__x_axis_name = None
self._stack_view = StackView(self)
+ maskToolWidget = self._stack_view.getPlotWidget().getMaskToolsDockWidget().widget()
+ maskToolWidget.setItemMaskUpdated(True)
+
self._hline = qt.QFrame(self)
self._hline.setFrameStyle(qt.QFrame.HLine)
self._hline.setFrameShadow(qt.QFrame.Sunken)
diff --git a/silx/gui/data/RecordTableView.py b/silx/gui/data/RecordTableView.py
index b1b7dcd..2c0011a 100644
--- a/silx/gui/data/RecordTableView.py
+++ b/silx/gui/data/RecordTableView.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2021 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -149,6 +149,10 @@ class RecordTableModel(qt.QAbstractTableModel):
:param qt.QObject parent: Parent object
:param numpy.ndarray data: A numpy array or a h5py dataset
"""
+
+ MAX_NUMBER_OF_ROWS = 10e6
+ """Maximum number of display values of the dataset"""
+
def __init__(self, parent=None, data=None):
qt.QAbstractTableModel.__init__(self, parent)
@@ -170,7 +174,7 @@ class RecordTableModel(qt.QAbstractTableModel):
elif not self.__is_array:
return 1
else:
- return len(self.__data)
+ return min(len(self.__data), self.MAX_NUMBER_OF_ROWS)
def columnCount(self, parent_idx=None):
"""Returns number of columns to be displayed in table"""
@@ -179,6 +183,15 @@ class RecordTableModel(qt.QAbstractTableModel):
else:
return len(self.__fields)
+ def __clippedData(self, role=qt.Qt.DisplayRole):
+ """Return data for cells representing clipped data"""
+ if role == qt.Qt.DisplayRole:
+ return "..."
+ elif role == qt.Qt.ToolTipRole:
+ return "Dataset is too large: display is clipped"
+ else:
+ return None
+
def data(self, index, role=qt.Qt.DisplayRole):
"""QAbstractTableModel method to access data values
in the format ready to be displayed"""
@@ -188,10 +201,19 @@ class RecordTableModel(qt.QAbstractTableModel):
if self.__data is None:
return None
+ # Special display of one before last data for clipped table
+ if self.__isClipped() and index.row() == self.rowCount() - 2:
+ return self.__clippedData(role)
+
if self.__is_array:
- if index.row() >= len(self.__data):
+ row = index.row()
+ if row >= self.rowCount():
return None
- data = self.__data[index.row()]
+ elif self.__isClipped() and row == self.rowCount() - 1:
+ # Clipped array, display last value at the end
+ data = self.__data[-1]
+ else:
+ data = self.__data[row]
else:
if index.row() > 0:
return None
@@ -221,10 +243,18 @@ class RecordTableModel(qt.QAbstractTableModel):
# PyQt4 send -1 when there is columns but no rows
return None
+ # Handle clipping of huge tables
+ if (self.__isClipped() and
+ orientation == qt.Qt.Vertical and
+ section == self.rowCount() - 2):
+ return self.__clippedData(role)
+
if role == qt.Qt.DisplayRole:
if orientation == qt.Qt.Vertical:
if not self.__is_array:
return "Scalar"
+ elif section == self.MAX_NUMBER_OF_ROWS - 1:
+ return str(len(self.__data) - 1)
else:
return str(section)
if orientation == qt.Qt.Horizontal:
@@ -246,6 +276,10 @@ class RecordTableModel(qt.QAbstractTableModel):
"""
return qt.QAbstractTableModel.flags(self, index)
+ def __isClipped(self) -> bool:
+ """Returns whether the displayed array is clipped or not"""
+ return self.__data is not None and self.__is_array and len(self.__data) > self.MAX_NUMBER_OF_ROWS
+
def setArrayData(self, data):
"""Set the data array and the viewing perspective.
diff --git a/silx/gui/data/test/test_arraywidget.py b/silx/gui/data/test/test_arraywidget.py
index 7785ac5..87081ed 100644
--- a/silx/gui/data/test/test_arraywidget.py
+++ b/silx/gui/data/test/test_arraywidget.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2016-2020 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -34,6 +34,7 @@ import numpy
from silx.gui import qt
from silx.gui.data import ArrayTableWidget
+from silx.gui.data.ArrayTableModel import ArrayTableModel
from silx.gui.utils.testutils import TestCaseQt
import h5py
@@ -186,6 +187,18 @@ class TestArrayWidget(TestCaseQt):
b1 = self.aw.getData(copy=False)
self.assertIs(b0, b1)
+ def testClipping(self):
+ """Test clipping of large arrays"""
+ self.aw.show()
+ self.qWaitForWindowExposed(self.aw)
+
+ data = numpy.arange(ArrayTableModel.MAX_NUMBER_OF_SECTIONS + 10)
+
+ for shape in [(1, -1), (-1, 1)]:
+ with self.subTest(shape=shape):
+ self.aw.setArrayData(data.reshape(shape), editable=True)
+ self.qapp.processEvents()
+
class TestH5pyArrayWidget(TestCaseQt):
"""Basic test for ArrayTableWidget with a dataset.