diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2021-09-07 14:39:36 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2021-09-07 14:39:36 +0200 |
commit | d3194b1a9c4404ba93afac43d97172ab24c57098 (patch) | |
tree | a1604130e1401dc1cbd084518ed72869dc92b86f /silx/gui/data | |
parent | b3bea947efa55d2c0f198b6c6795b3177be27f45 (diff) |
New upstream version 0.15.2+dfsg
Diffstat (limited to 'silx/gui/data')
-rw-r--r-- | silx/gui/data/ArrayTableModel.py | 78 | ||||
-rw-r--r-- | silx/gui/data/DataViews.py | 6 | ||||
-rw-r--r-- | silx/gui/data/NXdataWidgets.py | 9 | ||||
-rw-r--r-- | silx/gui/data/RecordTableView.py | 42 | ||||
-rw-r--r-- | silx/gui/data/test/test_arraywidget.py | 15 |
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. |