summaryrefslogtreecommitdiff
path: root/silx/gui/data/RecordTableView.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/data/RecordTableView.py')
-rw-r--r--silx/gui/data/RecordTableView.py447
1 files changed, 0 insertions, 447 deletions
diff --git a/silx/gui/data/RecordTableView.py b/silx/gui/data/RecordTableView.py
deleted file mode 100644
index 2c0011a..0000000
--- a/silx/gui/data/RecordTableView.py
+++ /dev/null
@@ -1,447 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# 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
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""
-This module define model and widget to display 1D slices from numpy
-array using compound data types or hdf5 databases.
-"""
-from __future__ import division
-
-import itertools
-import numpy
-from silx.gui import qt
-import silx.io
-from .TextFormatter import TextFormatter
-from silx.gui.widgets.TableWidget import CopySelectedCellsAction
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "29/08/2018"
-
-
-class _MultiLineItem(qt.QItemDelegate):
- """Draw a multiline text without hiding anything.
-
- The paint method display a cell without any wrap. And an editor is
- available to scroll into the selected cell.
- """
-
- def __init__(self, parent=None):
- """
- Constructor
-
- :param qt.QWidget parent: Parent of the widget
- """
- qt.QItemDelegate.__init__(self, parent)
- self.__textOptions = qt.QTextOption()
- self.__textOptions.setFlags(qt.QTextOption.IncludeTrailingSpaces |
- qt.QTextOption.ShowTabsAndSpaces)
- self.__textOptions.setWrapMode(qt.QTextOption.NoWrap)
- self.__textOptions.setAlignment(qt.Qt.AlignTop | qt.Qt.AlignLeft)
-
- def paint(self, painter, option, index):
- """
- Write multiline text without using any wrap or any alignment according
- to the cell size.
-
- :param qt.QPainter painter: Painter context used to displayed the cell
- :param qt.QStyleOptionViewItem option: Control how the editor is shown
- :param qt.QIndex index: Index of the data to display
- """
- painter.save()
-
- # set colors
- painter.setPen(qt.QPen(qt.Qt.NoPen))
- if option.state & qt.QStyle.State_Selected:
- brush = option.palette.highlight()
- painter.setBrush(brush)
- else:
- brush = index.data(qt.Qt.BackgroundRole)
- if brush is None:
- # default background color for a cell
- brush = qt.Qt.white
- painter.setBrush(brush)
- painter.drawRect(option.rect)
-
- if index.isValid():
- if option.state & qt.QStyle.State_Selected:
- brush = option.palette.highlightedText()
- else:
- brush = index.data(qt.Qt.ForegroundRole)
- if brush is None:
- brush = option.palette.text()
- painter.setPen(qt.QPen(brush.color()))
- text = index.data(qt.Qt.DisplayRole)
- painter.drawText(qt.QRectF(option.rect), text, self.__textOptions)
-
- painter.restore()
-
- def createEditor(self, parent, option, index):
- """
- Returns the widget used to edit the item specified by index for editing.
-
- We use it not to edit the content but to show the content with a
- convenient scroll bar.
-
- :param qt.QWidget parent: Parent of the widget
- :param qt.QStyleOptionViewItem option: Control how the editor is shown
- :param qt.QIndex index: Index of the data to display
- """
- if not index.isValid():
- return super(_MultiLineItem, self).createEditor(parent, option, index)
-
- editor = qt.QTextEdit(parent)
- editor.setReadOnly(True)
- return editor
-
- def setEditorData(self, editor, index):
- """
- Read data from the model and feed the editor.
-
- :param qt.QWidget editor: Editor widget
- :param qt.QIndex index: Index of the data to display
- """
- text = index.model().data(index, qt.Qt.EditRole)
- editor.setText(text)
-
- def updateEditorGeometry(self, editor, option, index):
- """
- Update the geometry of the editor according to the changes of the view.
-
- :param qt.QWidget editor: Editor widget
- :param qt.QStyleOptionViewItem option: Control how the editor is shown
- :param qt.QIndex index: Index of the data to display
- """
- editor.setGeometry(option.rect)
-
-
-class RecordTableModel(qt.QAbstractTableModel):
- """This data model provides access to 1D slices from numpy array using
- compound data types or hdf5 databases.
-
- Each entries are displayed in a single row, and each columns contain a
- specific field of the compound type.
-
- It also allows to display 1D arrays of simple data types.
- array.
-
- :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)
-
- self.__data = None
- self.__is_array = False
- self.__fields = None
- self.__formatter = None
- self.__editFormatter = None
- self.setFormatter(TextFormatter(self))
-
- # set _data
- self.setArrayData(data)
-
- # Methods to be implemented to subclass QAbstractTableModel
- def rowCount(self, parent_idx=None):
- """Returns number of rows to be displayed in table"""
- if self.__data is None:
- return 0
- elif not self.__is_array:
- return 1
- else:
- 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"""
- if self.__fields is None:
- return 1
- 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"""
- if not index.isValid():
- return None
-
- 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:
- row = index.row()
- if row >= self.rowCount():
- return None
- 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
- data = self.__data
-
- if self.__fields is not None:
- if index.column() >= len(self.__fields):
- return None
- key = self.__fields[index.column()][1]
- data = data[key[0]]
- if len(key) > 1:
- data = data[key[1]]
-
- # no dtype in case of 1D array of unicode objects (#2093)
- dtype = getattr(data, "dtype", None)
-
- if role == qt.Qt.DisplayRole:
- return self.__formatter.toString(data, dtype=dtype)
- elif role == qt.Qt.EditRole:
- return self.__editFormatter.toString(data, dtype=dtype)
- return None
-
- def headerData(self, section, orientation, role=qt.Qt.DisplayRole):
- """Returns the 0-based row or column index, for display in the
- horizontal and vertical headers"""
- if section == -1:
- # 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:
- if self.__fields is None:
- if section == 0:
- return "Data"
- else:
- return None
- else:
- if section < len(self.__fields):
- return self.__fields[section][0]
- else:
- return None
- return None
-
- def flags(self, index):
- """QAbstractTableModel method to inform the view whether data
- is editable or not.
- """
- 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.
-
- You can set ``copy=False`` if you need more performances, when dealing
- with a large numpy array. In this case, a simple reference to the data
- is used to access the data, rather than a copy of the array.
-
- .. warning::
-
- Any change to the data model will affect your original data
- array, when using a reference rather than a copy..
-
- :param data: 1D numpy array, or any object that can be
- converted to a numpy array using ``numpy.array(data)`` (e.g.
- a nested sequence).
- """
- if qt.qVersion() > "4.6":
- self.beginResetModel()
-
- self.__data = data
- if isinstance(data, numpy.ndarray):
- self.__is_array = True
- elif silx.io.is_dataset(data) and data.shape != tuple():
- self.__is_array = True
- else:
- self.__is_array = False
-
- self.__fields = []
- if data is not None:
- if data.dtype.fields is not None:
- fields = sorted(data.dtype.fields.items(), key=lambda e: e[1][1])
- for name, (dtype, _index) in fields:
- if dtype.shape != tuple():
- keys = itertools.product(*[range(x) for x in dtype.shape])
- for key in keys:
- label = "%s%s" % (name, list(key))
- array_key = (name, key)
- self.__fields.append((label, array_key))
- else:
- self.__fields.append((name, (name,)))
- else:
- self.__fields = None
-
- if qt.qVersion() > "4.6":
- self.endResetModel()
- else:
- self.reset()
-
- def arrayData(self):
- """Returns the internal data.
-
- :rtype: numpy.ndarray of h5py.Dataset
- """
- return self.__data
-
- def setFormatter(self, formatter):
- """Set the formatter object to be used to display data from the model
-
- :param TextFormatter formatter: Formatter to use
- """
- if formatter is self.__formatter:
- return
-
- if qt.qVersion() > "4.6":
- self.beginResetModel()
-
- if self.__formatter is not None:
- self.__formatter.formatChanged.disconnect(self.__formatChanged)
-
- self.__formatter = formatter
- self.__editFormatter = TextFormatter(formatter)
- self.__editFormatter.setUseQuoteForText(False)
-
- if self.__formatter is not None:
- self.__formatter.formatChanged.connect(self.__formatChanged)
-
- if qt.qVersion() > "4.6":
- self.endResetModel()
- else:
- self.reset()
-
- def getFormatter(self):
- """Returns the text formatter used.
-
- :rtype: TextFormatter
- """
- return self.__formatter
-
- def __formatChanged(self):
- """Called when the format changed.
- """
- self.__editFormatter = TextFormatter(self, self.getFormatter())
- self.__editFormatter.setUseQuoteForText(False)
- self.reset()
-
-
-class _ShowEditorProxyModel(qt.QIdentityProxyModel):
- """
- Allow to custom the flag edit of the model
- """
-
- def __init__(self, parent=None):
- """
- Constructor
-
- :param qt.QObject arent: parent object
- """
- super(_ShowEditorProxyModel, self).__init__(parent)
- self.__forceEditable = False
-
- def flags(self, index):
- flag = qt.QIdentityProxyModel.flags(self, index)
- if self.__forceEditable:
- flag = flag | qt.Qt.ItemIsEditable
- return flag
-
- def forceCellEditor(self, show):
- """
- Enable the editable flag to allow to display cell editor.
- """
- if self.__forceEditable == show:
- return
- self.beginResetModel()
- self.__forceEditable = show
- self.endResetModel()
-
-
-class RecordTableView(qt.QTableView):
- """TableView using DatabaseTableModel as default model.
- """
- def __init__(self, parent=None):
- """
- Constructor
-
- :param qt.QWidget parent: parent QWidget
- """
- qt.QTableView.__init__(self, parent)
-
- model = _ShowEditorProxyModel(self)
- self._model = RecordTableModel()
- model.setSourceModel(self._model)
- self.setModel(model)
-
- self.__multilineView = _MultiLineItem(self)
- self.setEditTriggers(qt.QAbstractItemView.AllEditTriggers)
- self._copyAction = CopySelectedCellsAction(self)
- self.addAction(self._copyAction)
-
- def copy(self):
- self._copyAction.trigger()
-
- def setArrayData(self, data):
- model = self.model()
- sourceModel = model.sourceModel()
- sourceModel.setArrayData(data)
-
- if data is not None:
- if issubclass(data.dtype.type, (numpy.string_, numpy.unicode_)):
- # TODO it would be nice to also fix fields
- # but using it only for string array is already very useful
- self.setItemDelegateForColumn(0, self.__multilineView)
- model.forceCellEditor(True)
- else:
- self.setItemDelegateForColumn(0, None)
- model.forceCellEditor(False)