diff options
Diffstat (limited to 'silx/app/view/CustomNxdataWidget.py')
-rw-r--r-- | silx/app/view/CustomNxdataWidget.py | 1008 |
1 files changed, 0 insertions, 1008 deletions
diff --git a/silx/app/view/CustomNxdataWidget.py b/silx/app/view/CustomNxdataWidget.py deleted file mode 100644 index 72c9940..0000000 --- a/silx/app/view/CustomNxdataWidget.py +++ /dev/null @@ -1,1008 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# Copyright (C) 2016-2018 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. -# -# ############################################################################*/ - -"""Widget to custom NXdata groups""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "15/06/2018" - -import logging -import numpy -import weakref - -from silx.gui import qt -from silx.io import commonh5 -import silx.io.nxdata -from silx.gui.hdf5._utils import Hdf5DatasetMimeData -from silx.gui.data.TextFormatter import TextFormatter -from silx.gui.hdf5.Hdf5Formatter import Hdf5Formatter -from silx.gui import icons - - -_logger = logging.getLogger(__name__) -_formatter = TextFormatter() -_hdf5Formatter = Hdf5Formatter(textFormatter=_formatter) - - -class _RowItems(qt.QStandardItem): - """Define the list of items used for a specific row.""" - - def type(self): - return qt.QStandardItem.UserType + 1 - - def getRowItems(self): - """Returns the list of items used for a specific row. - - The first item should be this class. - - :rtype: List[qt.QStandardItem] - """ - raise NotImplementedError() - - -class _DatasetItemRow(_RowItems): - """Define a row which can contain a dataset.""" - - def __init__(self, label="", dataset=None): - """Constructor""" - super(_DatasetItemRow, self).__init__(label) - self.setEditable(False) - self.setDropEnabled(False) - self.setDragEnabled(False) - - self.__name = qt.QStandardItem() - self.__name.setEditable(False) - self.__name.setDropEnabled(True) - - self.__type = qt.QStandardItem() - self.__type.setEditable(False) - self.__type.setDropEnabled(False) - self.__type.setDragEnabled(False) - - self.__shape = qt.QStandardItem() - self.__shape.setEditable(False) - self.__shape.setDropEnabled(False) - self.__shape.setDragEnabled(False) - - self.setDataset(dataset) - - def getDefaultFormatter(self): - """Get the formatter used to display dataset informations. - - :rtype: Hdf5Formatter - """ - return _hdf5Formatter - - def setDataset(self, dataset): - """Set the dataset stored in this item. - - :param Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset] dataset: - The dataset to store. - """ - self.__dataset = dataset - if self.__dataset is not None: - name = self.__dataset.name - - if silx.io.is_dataset(dataset): - type_ = self.getDefaultFormatter().humanReadableType(dataset) - shape = self.getDefaultFormatter().humanReadableShape(dataset) - - if dataset.shape is None: - icon_name = "item-none" - elif len(dataset.shape) < 4: - icon_name = "item-%ddim" % len(dataset.shape) - else: - icon_name = "item-ndim" - icon = icons.getQIcon(icon_name) - else: - type_ = "" - shape = "" - icon = qt.QIcon() - else: - name = "" - type_ = "" - shape = "" - icon = qt.QIcon() - - self.__icon = icon - self.__name.setText(name) - self.__name.setDragEnabled(self.__dataset is not None) - self.__name.setIcon(self.__icon) - self.__type.setText(type_) - self.__shape.setText(shape) - - parent = self.parent() - if parent is not None: - self.parent()._datasetUpdated() - - def getDataset(self): - """Returns the dataset stored within the item.""" - return self.__dataset - - def getRowItems(self): - """Returns the list of items used for a specific row. - - The first item should be this class. - - :rtype: List[qt.QStandardItem] - """ - return [self, self.__name, self.__type, self.__shape] - - -class _DatasetAxisItemRow(_DatasetItemRow): - """Define a row describing an axis.""" - - def __init__(self): - """Constructor""" - super(_DatasetAxisItemRow, self).__init__() - - def setAxisId(self, axisId): - """Set the id of the axis (the first axis is 0) - - :param int axisId: Identifier of this axis. - """ - self.__axisId = axisId - label = "Axis %d" % (axisId + 1) - self.setText(label) - - def getAxisId(self): - """Returns the identifier of this axis. - - :rtype: int - """ - return self.__axisId - - -class _NxDataItem(qt.QStandardItem): - """ - Define a custom NXdata. - """ - - def __init__(self): - """Constructor""" - qt.QStandardItem.__init__(self) - self.__error = None - self.__title = None - self.__axes = [] - self.__virtual = None - - item = _DatasetItemRow("Signal", None) - self.appendRow(item.getRowItems()) - self.__signal = item - - self.setEditable(False) - self.setDragEnabled(False) - self.setDropEnabled(False) - self.__setError(None) - - def getRowItems(self): - """Returns the list of items used for a specific row. - - The first item should be this class. - - :rtype: List[qt.QStandardItem] - """ - row = [self] - for _ in range(3): - item = qt.QStandardItem("") - item.setEditable(False) - item.setDragEnabled(False) - item.setDropEnabled(False) - row.append(item) - return row - - def _datasetUpdated(self): - """Called when the NXdata contained of the item have changed. - - It invalidates the NXdata stored and send an event `sigNxdataUpdated`. - """ - self.__virtual = None - self.__setError(None) - model = self.model() - if model is not None: - model.sigNxdataUpdated.emit(self.index()) - - def createVirtualGroup(self): - """Returns a new virtual Group using a NeXus NXdata structure to store - data - - :rtype: silx.io.commonh5.Group - """ - name = "" - if self.__title is not None: - name = self.__title - virtual = commonh5.Group(name) - virtual.attrs["NX_class"] = "NXdata" - - if self.__title is not None: - virtual.attrs["title"] = self.__title - - if self.__signal is not None: - signal = self.__signal.getDataset() - if signal is not None: - # Could be done using a link instead of a copy - node = commonh5.DatasetProxy("signal", target=signal) - virtual.attrs["signal"] = "signal" - virtual.add_node(node) - - axesAttr = [] - for i, axis in enumerate(self.__axes): - if axis is None: - name = "." - else: - axis = axis.getDataset() - if axis is None: - name = "." - else: - name = "axis%d" % i - node = commonh5.DatasetProxy(name, target=axis) - virtual.add_node(node) - axesAttr.append(name) - - if axesAttr != []: - virtual.attrs["axes"] = numpy.array(axesAttr) - - validator = silx.io.nxdata.NXdata(virtual) - if not validator.is_valid: - message = "<html>" - message += "This NXdata is not consistant" - message += "<ul>" - for issue in validator.issues: - message += "<li>%s</li>" % issue - message += "</ul>" - message += "</html>" - self.__setError(message) - else: - self.__setError(None) - return virtual - - def isValid(self): - """Returns true if the stored NXdata is valid - - :rtype: bool - """ - return self.__error is None - - def getVirtualGroup(self): - """Returns a cached virtual Group using a NeXus NXdata structure to - store data. - - If the stored NXdata was invalidated, :meth:`createVirtualGroup` is - internally called to update the cache. - - :rtype: silx.io.commonh5.Group - """ - if self.__virtual is None: - self.__virtual = self.createVirtualGroup() - return self.__virtual - - def getTitle(self): - """Returns the title of the NXdata - - :rtype: str - """ - return self.text() - - def setTitle(self, title): - """Set the title of the NXdata - - :param str title: The title of this NXdata - """ - self.setText(title) - - def __setError(self, error): - """Set the error message in case of the current state of the stored - NXdata is not valid. - - :param str error: Message to display - """ - self.__error = error - style = qt.QApplication.style() - if error is None: - message = "" - icon = style.standardIcon(qt.QStyle.SP_DirLinkIcon) - else: - message = error - icon = style.standardIcon(qt.QStyle.SP_MessageBoxCritical) - self.setIcon(icon) - self.setToolTip(message) - - def getError(self): - """Returns the error message in case the NXdata is not valid. - - :rtype: str""" - return self.__error - - def setSignalDataset(self, dataset): - """Set the dataset to use as signal with this NXdata. - - :param Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset] dataset: - The dataset to use as signal. - """ - - self.__signal.setDataset(dataset) - self._datasetUpdated() - - def getSignalDataset(self): - """Returns the dataset used as signal. - - :rtype: Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset] - """ - return self.__signal.getDataset() - - def setAxesDatasets(self, datasets): - """Set all the available dataset used as axes. - - Axes will be created or removed from the GUI in order to provide the - same amount of requested axes. - - A `None` element is an axes with no dataset. - - :param List[Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset,None]] datasets: - List of dataset to use as axes. - """ - for i, dataset in enumerate(datasets): - if i < len(self.__axes): - mustAppend = False - item = self.__axes[i] - else: - mustAppend = True - item = _DatasetAxisItemRow() - item.setAxisId(i) - item.setDataset(dataset) - if mustAppend: - self.__axes.append(item) - self.appendRow(item.getRowItems()) - - # Clean up extra axis - for i in range(len(datasets), len(self.__axes)): - item = self.__axes.pop(len(datasets)) - self.removeRow(item.row()) - - self._datasetUpdated() - - def getAxesDatasets(self): - """Returns available axes as dataset. - - A `None` element is an axes with no dataset. - - :rtype: List[Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset,None]] - """ - datasets = [] - for axis in self.__axes: - datasets.append(axis.getDataset()) - return datasets - - -class _Model(qt.QStandardItemModel): - """Model storing a list of custom NXdata items. - - Supports drag and drop of datasets. - """ - - sigNxdataUpdated = qt.Signal(qt.QModelIndex) - """Emitted when stored NXdata was edited""" - - def __init__(self, parent=None): - """Constructor""" - qt.QStandardItemModel.__init__(self, parent) - root = self.invisibleRootItem() - root.setDropEnabled(True) - root.setDragEnabled(False) - - def supportedDropActions(self): - """Inherited method to redefine supported drop actions.""" - return qt.Qt.CopyAction | qt.Qt.MoveAction - - def mimeTypes(self): - """Inherited method to redefine draggable mime types.""" - return [Hdf5DatasetMimeData.MIME_TYPE] - - def mimeData(self, indexes): - """ - Returns an object that contains serialized items of data corresponding - to the list of indexes specified. - - :param List[qt.QModelIndex] indexes: List of indexes - :rtype: qt.QMimeData - """ - if len(indexes) > 1: - return None - if len(indexes) == 0: - return None - - qindex = indexes[0] - qindex = self.index(qindex.row(), 0, parent=qindex.parent()) - item = self.itemFromIndex(qindex) - if isinstance(item, _DatasetItemRow): - dataset = item.getDataset() - if dataset is None: - return None - else: - mimeData = Hdf5DatasetMimeData(dataset=item.getDataset()) - else: - mimeData = None - return mimeData - - def dropMimeData(self, mimedata, action, row, column, parentIndex): - """Inherited method to handle a drop operation to this model.""" - if action == qt.Qt.IgnoreAction: - return True - - if mimedata.hasFormat(Hdf5DatasetMimeData.MIME_TYPE): - if row != -1 or column != -1: - # It is not a drop on a specific item - return False - item = self.itemFromIndex(parentIndex) - if item is None or item is self.invisibleRootItem(): - # Drop at the end - dataset = mimedata.dataset() - if silx.io.is_dataset(dataset): - self.createFromSignal(dataset) - elif silx.io.is_group(dataset): - nxdata = dataset - try: - self.createFromNxdata(nxdata) - except ValueError: - _logger.error("Error while dropping a group as an NXdata") - _logger.debug("Backtrace", exc_info=True) - return False - else: - _logger.error("Dropping a wrong object") - return False - else: - item = item.parent().child(item.row(), 0) - if not isinstance(item, _DatasetItemRow): - # Dropped at a bad place - return False - dataset = mimedata.dataset() - if silx.io.is_dataset(dataset): - item.setDataset(dataset) - else: - _logger.error("Dropping a wrong object") - return False - return True - - return False - - def __getNxdataByTitle(self, title): - """Returns an NXdata item by its title, else None. - - :rtype: Union[_NxDataItem,None] - """ - for row in range(self.rowCount()): - qindex = self.index(row, 0) - item = self.itemFromIndex(qindex) - if item.getTitle() == title: - return item - return None - - def findFreeNxdataTitle(self): - """Returns an NXdata title which is not yet used. - - :rtype: str - """ - for i in range(self.rowCount() + 1): - name = "NXData #%d" % (i + 1) - group = self.__getNxdataByTitle(name) - if group is None: - break - return name - - def createNewNxdata(self, name=None): - """Create a new NXdata item. - - :param Union[str,None] name: A title for the new NXdata - """ - item = _NxDataItem() - if name is None: - name = self.findFreeNxdataTitle() - item.setTitle(name) - self.appendRow(item.getRowItems()) - - def createFromSignal(self, dataset): - """Create a new NXdata item from a signal dataset. - - This signal will also define an amount of axes according to its number - of dimensions. - - :param Union[numpy.ndarray,h5py.Dataset,silx.io.commonh5.Dataset] dataset: - A dataset uses as signal. - """ - - item = _NxDataItem() - name = self.findFreeNxdataTitle() - item.setTitle(name) - item.setSignalDataset(dataset) - item.setAxesDatasets([None] * len(dataset.shape)) - self.appendRow(item.getRowItems()) - - def createFromNxdata(self, nxdata): - """Create a new custom NXdata item from an existing NXdata group. - - If the NXdata is not valid, nothing is created, and an exception is - returned. - - :param Union[h5py.Group,silx.io.commonh5.Group] nxdata: An h5py group - following the NXData specification. - :raise ValueError:If `nxdata` is not valid. - """ - validator = silx.io.nxdata.NXdata(nxdata) - if validator.is_valid: - item = _NxDataItem() - title = validator.title - if title in [None or ""]: - title = self.findFreeNxdataTitle() - item.setTitle(title) - item.setSignalDataset(validator.signal) - item.setAxesDatasets(validator.axes) - self.appendRow(item.getRowItems()) - else: - raise ValueError("Not a valid NXdata") - - def removeNxdataItem(self, item): - """Remove an NXdata item from this model. - - :param _NxDataItem item: An item - """ - if isinstance(item, _NxDataItem): - parent = item.parent() - assert(parent is None) - model = item.model() - model.removeRow(item.row()) - else: - _logger.error("Unexpected item") - - def appendAxisToNxdataItem(self, item): - """Append a new axes to this item (or the NXdata item own by this item). - - :param Union[_NxDataItem,qt.QStandardItem] item: An item - """ - if item is not None and not isinstance(item, _NxDataItem): - item = item.parent() - nxdataItem = item - if isinstance(item, _NxDataItem): - datasets = nxdataItem.getAxesDatasets() - datasets.append(None) - nxdataItem.setAxesDatasets(datasets) - else: - _logger.error("Unexpected item") - - def removeAxisItem(self, item): - """Remove an axis item from this model. - - :param _DatasetAxisItemRow item: An axis item - """ - if isinstance(item, _DatasetAxisItemRow): - axisId = item.getAxisId() - nxdataItem = item.parent() - datasets = nxdataItem.getAxesDatasets() - del datasets[axisId] - nxdataItem.setAxesDatasets(datasets) - else: - _logger.error("Unexpected item") - - -class CustomNxDataToolBar(qt.QToolBar): - """A specialised toolbar to manage custom NXdata model and items.""" - - def __init__(self, parent=None): - """Constructor""" - super(CustomNxDataToolBar, self).__init__(parent=parent) - self.__nxdataWidget = None - self.__initContent() - # Initialize action state - self.__currentSelectionChanged(qt.QModelIndex(), qt.QModelIndex()) - - def __initContent(self): - """Create all expected actions and set the content of this toolbar.""" - action = qt.QAction("Create a new custom NXdata", self) - action.setIcon(icons.getQIcon("nxdata-create")) - action.triggered.connect(self.__createNewNxdata) - self.addAction(action) - self.__addNxDataAction = action - - action = qt.QAction("Remove the selected NXdata", self) - action.setIcon(icons.getQIcon("nxdata-remove")) - action.triggered.connect(self.__removeSelectedNxdata) - self.addAction(action) - self.__removeNxDataAction = action - - self.addSeparator() - - action = qt.QAction("Create a new axis to the selected NXdata", self) - action.setIcon(icons.getQIcon("nxdata-axis-add")) - action.triggered.connect(self.__appendNewAxisToSelectedNxdata) - self.addAction(action) - self.__addNxDataAxisAction = action - - action = qt.QAction("Remove the selected NXdata axis", self) - action.setIcon(icons.getQIcon("nxdata-axis-remove")) - action.triggered.connect(self.__removeSelectedAxis) - self.addAction(action) - self.__removeNxDataAxisAction = action - - def __getSelectedItem(self): - """Get the selected item from the linked CustomNxdataWidget. - - :rtype: qt.QStandardItem - """ - selectionModel = self.__nxdataWidget.selectionModel() - index = selectionModel.currentIndex() - if not index.isValid(): - return - model = self.__nxdataWidget.model() - index = model.index(index.row(), 0, index.parent()) - item = model.itemFromIndex(index) - return item - - def __createNewNxdata(self): - """Create a new NXdata item to the linked CustomNxdataWidget.""" - if self.__nxdataWidget is None: - return - model = self.__nxdataWidget.model() - model.createNewNxdata() - - def __removeSelectedNxdata(self): - """Remove the NXdata item currently selected in the linked - CustomNxdataWidget.""" - if self.__nxdataWidget is None: - return - model = self.__nxdataWidget.model() - item = self.__getSelectedItem() - model.removeNxdataItem(item) - - def __appendNewAxisToSelectedNxdata(self): - """Append a new axis to the NXdata item currently selected in the - linked CustomNxdataWidget.""" - if self.__nxdataWidget is None: - return - model = self.__nxdataWidget.model() - item = self.__getSelectedItem() - model.appendAxisToNxdataItem(item) - - def __removeSelectedAxis(self): - """Remove the axis item currently selected in the linked - CustomNxdataWidget.""" - if self.__nxdataWidget is None: - return - model = self.__nxdataWidget.model() - item = self.__getSelectedItem() - model.removeAxisItem(item) - - def setCustomNxDataWidget(self, widget): - """Set the linked CustomNxdataWidget to this toolbar.""" - assert(isinstance(widget, CustomNxdataWidget)) - if self.__nxdataWidget is not None: - selectionModel = self.__nxdataWidget.selectionModel() - selectionModel.currentChanged.disconnect(self.__currentSelectionChanged) - self.__nxdataWidget = widget - if self.__nxdataWidget is not None: - selectionModel = self.__nxdataWidget.selectionModel() - selectionModel.currentChanged.connect(self.__currentSelectionChanged) - - def __currentSelectionChanged(self, current, previous): - """Update the actions according to the linked CustomNxdataWidget - item selection""" - if not current.isValid(): - item = None - else: - model = self.__nxdataWidget.model() - index = model.index(current.row(), 0, current.parent()) - item = model.itemFromIndex(index) - self.__removeNxDataAction.setEnabled(isinstance(item, _NxDataItem)) - self.__removeNxDataAxisAction.setEnabled(isinstance(item, _DatasetAxisItemRow)) - self.__addNxDataAxisAction.setEnabled(isinstance(item, _NxDataItem) or isinstance(item, _DatasetItemRow)) - - -class _HashDropZones(qt.QStyledItemDelegate): - """Delegate item displaying a drop zone when the item do not contains - dataset.""" - - def __init__(self, parent=None): - """Constructor""" - super(_HashDropZones, self).__init__(parent) - pen = qt.QPen() - pen.setColor(qt.QColor("#D0D0D0")) - pen.setStyle(qt.Qt.DotLine) - pen.setWidth(2) - self.__dropPen = pen - - def paint(self, painter, option, index): - """ - Paint the item - - :param qt.QPainter painter: A painter - :param qt.QStyleOptionViewItem option: Options of the item to paint - :param qt.QModelIndex index: Index of the item to paint - """ - displayDropZone = False - if index.isValid(): - model = index.model() - rowIndex = model.index(index.row(), 0, index.parent()) - rowItem = model.itemFromIndex(rowIndex) - if isinstance(rowItem, _DatasetItemRow): - displayDropZone = rowItem.getDataset() is None - - if displayDropZone: - painter.save() - - # Draw background if selected - if option.state & qt.QStyle.State_Selected: - colorGroup = qt.QPalette.Inactive - if option.state & qt.QStyle.State_Active: - colorGroup = qt.QPalette.Active - if not option.state & qt.QStyle.State_Enabled: - colorGroup = qt.QPalette.Disabled - brush = option.palette.brush(colorGroup, qt.QPalette.Highlight) - painter.fillRect(option.rect, brush) - - painter.setPen(self.__dropPen) - painter.drawRect(option.rect.adjusted(3, 3, -3, -3)) - painter.restore() - else: - qt.QStyledItemDelegate.paint(self, painter, option, index) - - -class CustomNxdataWidget(qt.QTreeView): - """Widget providing a table displaying and allowing to custom virtual - NXdata.""" - - sigNxdataItemUpdated = qt.Signal(qt.QStandardItem) - """Emitted when the NXdata from an NXdata item was edited""" - - sigNxdataItemRemoved = qt.Signal(qt.QStandardItem) - """Emitted when an NXdata item was removed""" - - def __init__(self, parent=None): - """Constructor""" - qt.QTreeView.__init__(self, parent=None) - self.__model = _Model(self) - self.__model.setColumnCount(4) - self.__model.setHorizontalHeaderLabels(["Name", "Dataset", "Type", "Shape"]) - self.setModel(self.__model) - - self.setItemDelegateForColumn(1, _HashDropZones(self)) - - self.__model.sigNxdataUpdated.connect(self.__nxdataUpdate) - self.__model.rowsAboutToBeRemoved.connect(self.__rowsAboutToBeRemoved) - self.__model.rowsAboutToBeInserted.connect(self.__rowsAboutToBeInserted) - - header = self.header() - if qt.qVersion() < "5.0": - setResizeMode = header.setResizeMode - else: - setResizeMode = header.setSectionResizeMode - setResizeMode(0, qt.QHeaderView.ResizeToContents) - setResizeMode(1, qt.QHeaderView.Stretch) - setResizeMode(2, qt.QHeaderView.ResizeToContents) - setResizeMode(3, qt.QHeaderView.ResizeToContents) - - self.setSelectionMode(qt.QAbstractItemView.SingleSelection) - self.setDropIndicatorShown(True) - self.setDragDropOverwriteMode(True) - self.setDragEnabled(True) - self.viewport().setAcceptDrops(True) - - self.setContextMenuPolicy(qt.Qt.CustomContextMenu) - self.customContextMenuRequested[qt.QPoint].connect(self.__executeContextMenu) - - def __rowsAboutToBeInserted(self, parentIndex, start, end): - if qt.qVersion()[0:2] == "5.": - # FIXME: workaround for https://github.com/silx-kit/silx/issues/1919 - # Uses of ResizeToContents looks to break nice update of cells with Qt5 - # This patch make the view blinking - self.repaint() - - def __rowsAboutToBeRemoved(self, parentIndex, start, end): - """Called when an item was removed from the model.""" - items = [] - model = self.model() - for index in range(start, end): - qindex = model.index(index, 0, parent=parentIndex) - item = self.__model.itemFromIndex(qindex) - if isinstance(item, _NxDataItem): - items.append(item) - for item in items: - self.sigNxdataItemRemoved.emit(item) - - if qt.qVersion()[0:2] == "5.": - # FIXME: workaround for https://github.com/silx-kit/silx/issues/1919 - # Uses of ResizeToContents looks to break nice update of cells with Qt5 - # This patch make the view blinking - self.repaint() - - def __nxdataUpdate(self, index): - """Called when a virtual NXdata was updated from the model.""" - model = self.model() - item = model.itemFromIndex(index) - self.sigNxdataItemUpdated.emit(item) - - def createDefaultContextMenu(self, index): - """Create a default context menu at this position. - - :param qt.QModelIndex index: Index of the item - """ - index = self.__model.index(index.row(), 0, parent=index.parent()) - item = self.__model.itemFromIndex(index) - - menu = qt.QMenu() - - weakself = weakref.proxy(self) - - if isinstance(item, _NxDataItem): - action = qt.QAction("Add a new axis", menu) - action.triggered.connect(lambda: weakself.model().appendAxisToNxdataItem(item)) - action.setIcon(icons.getQIcon("nxdata-axis-add")) - action.setIconVisibleInMenu(True) - menu.addAction(action) - menu.addSeparator() - action = qt.QAction("Remove this NXdata", menu) - action.triggered.connect(lambda: weakself.model().removeNxdataItem(item)) - action.setIcon(icons.getQIcon("remove")) - action.setIconVisibleInMenu(True) - menu.addAction(action) - else: - if isinstance(item, _DatasetItemRow): - if item.getDataset() is not None: - action = qt.QAction("Remove this dataset", menu) - action.triggered.connect(lambda: item.setDataset(None)) - menu.addAction(action) - - if isinstance(item, _DatasetAxisItemRow): - menu.addSeparator() - action = qt.QAction("Remove this axis", menu) - action.triggered.connect(lambda: weakself.model().removeAxisItem(item)) - action.setIcon(icons.getQIcon("remove")) - action.setIconVisibleInMenu(True) - menu.addAction(action) - - return menu - - def __executeContextMenu(self, point): - """Execute the context menu at this position.""" - index = self.indexAt(point) - menu = self.createDefaultContextMenu(index) - if menu is None or menu.isEmpty(): - return - menu.exec_(qt.QCursor.pos()) - - def removeDatasetsFrom(self, root): - """ - Remove all datasets provided by this root - - :param root: The root file of datasets to remove - """ - for row in range(self.__model.rowCount()): - qindex = self.__model.index(row, 0) - item = self.model().itemFromIndex(qindex) - - edited = False - datasets = item.getAxesDatasets() - for i, dataset in enumerate(datasets): - if dataset is not None: - # That's an approximation, IS can't be used as h5py generates - # To objects for each requests to a node - if dataset.file.filename == root.file.filename: - datasets[i] = None - edited = True - if edited: - item.setAxesDatasets(datasets) - - dataset = item.getSignalDataset() - if dataset is not None: - # That's an approximation, IS can't be used as h5py generates - # To objects for each requests to a node - if dataset.file.filename == root.file.filename: - item.setSignalDataset(None) - - def replaceDatasetsFrom(self, removedRoot, loadedRoot): - """ - Replace any dataset from any NXdata items using the same dataset name - from another root. - - Usually used when a file was synchronized. - - :param removedRoot: The h5py root file which is replaced - (which have to be removed) - :param loadedRoot: The new h5py root file which have to be used - instread. - """ - for row in range(self.__model.rowCount()): - qindex = self.__model.index(row, 0) - item = self.model().itemFromIndex(qindex) - - edited = False - datasets = item.getAxesDatasets() - for i, dataset in enumerate(datasets): - newDataset = self.__replaceDatasetRoot(dataset, removedRoot, loadedRoot) - if dataset is not newDataset: - datasets[i] = newDataset - edited = True - if edited: - item.setAxesDatasets(datasets) - - dataset = item.getSignalDataset() - newDataset = self.__replaceDatasetRoot(dataset, removedRoot, loadedRoot) - if dataset is not newDataset: - item.setSignalDataset(newDataset) - - def __replaceDatasetRoot(self, dataset, fromRoot, toRoot): - """ - Replace the dataset by the same dataset name from another root. - """ - if dataset is None: - return None - - if dataset.file is None: - # Not from the expected root - return dataset - - # That's an approximation, IS can't be used as h5py generates - # To objects for each requests to a node - if dataset.file.filename == fromRoot.file.filename: - # Try to find the same dataset name - try: - return toRoot[dataset.name] - except Exception: - _logger.debug("Backtrace", exc_info=True) - return None - else: - # Not from the expected root - return dataset - - def selectedItems(self): - """Returns the list of selected items containing NXdata - - :rtype: List[qt.QStandardItem] - """ - result = [] - for qindex in self.selectedIndexes(): - if qindex.column() != 0: - continue - if not qindex.isValid(): - continue - item = self.__model.itemFromIndex(qindex) - if not isinstance(item, _NxDataItem): - continue - result.append(item) - return result - - def selectedNxdata(self): - """Returns the list of selected NXdata - - :rtype: List[silx.io.commonh5.Group] - """ - result = [] - for qindex in self.selectedIndexes(): - if qindex.column() != 0: - continue - if not qindex.isValid(): - continue - item = self.__model.itemFromIndex(qindex) - if not isinstance(item, _NxDataItem): - continue - result.append(item.getVirtualGroup()) - return result |