diff options
Diffstat (limited to 'silx/gui/plot3d/ParamTreeView.py')
-rw-r--r-- | silx/gui/plot3d/ParamTreeView.py | 546 |
1 files changed, 0 insertions, 546 deletions
diff --git a/silx/gui/plot3d/ParamTreeView.py b/silx/gui/plot3d/ParamTreeView.py deleted file mode 100644 index 8cf2b90..0000000 --- a/silx/gui/plot3d/ParamTreeView.py +++ /dev/null @@ -1,546 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2017-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. -# -# ###########################################################################*/ -""" -This module provides a :class:`QTreeView` dedicated to display plot3d models. - -This module contains: -- :class:`ParamTreeView`: A QTreeView specific for plot3d parameters and scene. -- :class:`ParameterTreeDelegate`: The delegate for :class:`ParamTreeView`. -- A set of specific editors used by :class:`ParameterTreeDelegate`: - :class:`FloatEditor`, :class:`Vector3DEditor`, - :class:`Vector4DEditor`, :class:`IntSliderEditor`, :class:`BooleanEditor` -""" - -from __future__ import absolute_import - -__authors__ = ["T. Vincent"] -__license__ = "MIT" -__date__ = "05/12/2017" - - -import numbers -import sys - -import six - -from .. import qt -from ..widgets.FloatEdit import FloatEdit as _FloatEdit -from ._model import visitQAbstractItemModel - - -class FloatEditor(_FloatEdit): - """Editor widget for float. - - :param parent: The widget's parent - :param float value: The initial editor value - """ - - valueChanged = qt.Signal(float) - """Signal emitted when the float value has changed""" - - def __init__(self, parent=None, value=None): - super(FloatEditor, self).__init__(parent, value) - self.setAlignment(qt.Qt.AlignLeft) - self.editingFinished.connect(self._emit) - - def _emit(self): - self.valueChanged.emit(self.value) - - value = qt.Property(float, - fget=_FloatEdit.value, - fset=_FloatEdit.setValue, - user=True, - notify=valueChanged) - """Qt user property of the float value this widget edits""" - - -class Vector3DEditor(qt.QWidget): - """Editor widget for QVector3D. - - :param parent: The widget's parent - :param flags: The widgets's flags - """ - - valueChanged = qt.Signal(qt.QVector3D) - """Signal emitted when the QVector3D value has changed""" - - def __init__(self, parent=None, flags=qt.Qt.Widget): - super(Vector3DEditor, self).__init__(parent, flags) - layout = qt.QHBoxLayout(self) - # layout.setSpacing(0) - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - self._xEdit = _FloatEdit(parent=self, value=0.) - self._xEdit.setAlignment(qt.Qt.AlignLeft) - # self._xEdit.editingFinished.connect(self._emit) - self._yEdit = _FloatEdit(parent=self, value=0.) - self._yEdit.setAlignment(qt.Qt.AlignLeft) - # self._yEdit.editingFinished.connect(self._emit) - self._zEdit = _FloatEdit(parent=self, value=0.) - self._zEdit.setAlignment(qt.Qt.AlignLeft) - # self._zEdit.editingFinished.connect(self._emit) - layout.addWidget(qt.QLabel('x:')) - layout.addWidget(self._xEdit) - layout.addWidget(qt.QLabel('y:')) - layout.addWidget(self._yEdit) - layout.addWidget(qt.QLabel('z:')) - layout.addWidget(self._zEdit) - layout.addStretch(1) - - def _emit(self): - vector = self.value - self.valueChanged.emit(vector) - - def getValue(self): - """Returns the QVector3D value of this widget - - :rtype: QVector3D - """ - return qt.QVector3D( - self._xEdit.value(), self._yEdit.value(), self._zEdit.value()) - - def setValue(self, value): - """Set the QVector3D value - - :param QVector3D value: The new value - """ - self._xEdit.setValue(value.x()) - self._yEdit.setValue(value.y()) - self._zEdit.setValue(value.z()) - self.valueChanged.emit(value) - - value = qt.Property(qt.QVector3D, - fget=getValue, - fset=setValue, - user=True, - notify=valueChanged) - """Qt user property of the QVector3D value this widget edits""" - - -class Vector4DEditor(qt.QWidget): - """Editor widget for QVector4D. - - :param parent: The widget's parent - :param flags: The widgets's flags - """ - - valueChanged = qt.Signal(qt.QVector4D) - """Signal emitted when the QVector4D value has changed""" - - def __init__(self, parent=None, flags=qt.Qt.Widget): - super(Vector4DEditor, self).__init__(parent, flags) - layout = qt.QHBoxLayout(self) - # layout.setSpacing(0) - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - self._xEdit = _FloatEdit(parent=self, value=0.) - self._xEdit.setAlignment(qt.Qt.AlignLeft) - # self._xEdit.editingFinished.connect(self._emit) - self._yEdit = _FloatEdit(parent=self, value=0.) - self._yEdit.setAlignment(qt.Qt.AlignLeft) - # self._yEdit.editingFinished.connect(self._emit) - self._zEdit = _FloatEdit(parent=self, value=0.) - self._zEdit.setAlignment(qt.Qt.AlignLeft) - # self._zEdit.editingFinished.connect(self._emit) - self._wEdit = _FloatEdit(parent=self, value=0.) - self._wEdit.setAlignment(qt.Qt.AlignLeft) - # self._wEdit.editingFinished.connect(self._emit) - layout.addWidget(qt.QLabel('x:')) - layout.addWidget(self._xEdit) - layout.addWidget(qt.QLabel('y:')) - layout.addWidget(self._yEdit) - layout.addWidget(qt.QLabel('z:')) - layout.addWidget(self._zEdit) - layout.addWidget(qt.QLabel('w:')) - layout.addWidget(self._wEdit) - layout.addStretch(1) - - def _emit(self): - vector = self.value - self.valueChanged.emit(vector) - - def getValue(self): - """Returns the QVector4D value of this widget - - :rtype: QVector4D - """ - return qt.QVector4D(self._xEdit.value(), self._yEdit.value(), - self._zEdit.value(), self._wEdit.value()) - - def setValue(self, value): - """Set the QVector4D value - - :param QVector4D value: The new value - """ - self._xEdit.setValue(value.x()) - self._yEdit.setValue(value.y()) - self._zEdit.setValue(value.z()) - self._wEdit.setValue(value.w()) - self.valueChanged.emit(value) - - value = qt.Property(qt.QVector4D, - fget=getValue, - fset=setValue, - user=True, - notify=valueChanged) - """Qt user property of the QVector4D value this widget edits""" - - -class IntSliderEditor(qt.QSlider): - """Slider editor widget for integer. - - Note: Tracking is disabled. - - :param parent: The widget's parent - """ - - def __init__(self, parent=None): - super(IntSliderEditor, self).__init__(parent) - self.setOrientation(qt.Qt.Horizontal) - self.setSingleStep(1) - self.setRange(0, 255) - self.setValue(0) - - -class BooleanEditor(qt.QCheckBox): - """Checkbox editor for bool. - - This is a QCheckBox with white background. - - :param parent: The widget's parent - """ - - def __init__(self, parent=None): - super(BooleanEditor, self).__init__(parent) - self.setStyleSheet("background: white;") - - -class ParameterTreeDelegate(qt.QStyledItemDelegate): - """TreeView delegate specific to plot3d scene and object parameter tree. - - It provides additional editors. - - :param parent: Delegate's parent - """ - - EDITORS = { - bool: BooleanEditor, - float: FloatEditor, - qt.QVector3D: Vector3DEditor, - qt.QVector4D: Vector4DEditor, - } - """Specific editors for different type of data""" - - def __init__(self, parent=None): - super(ParameterTreeDelegate, self).__init__(parent) - - def _fixVariant(self, data): - """Fix PyQt4 zero vectors being stored as QPyNullVariant. - - :param data: Data retrieved from the model - :return: Corresponding object - """ - if qt.BINDING == 'PyQt4' and isinstance(data, qt.QPyNullVariant): - typeName = data.typeName() - if typeName == 'QVector3D': - data = qt.QVector3D() - elif typeName == 'QVector4D': - data = qt.QVector4D() - return data - - def paint(self, painter, option, index): - """See :meth:`QStyledItemDelegate.paint`""" - data = index.data(qt.Qt.DisplayRole) - data = self._fixVariant(data) - - if isinstance(data, (qt.QVector3D, qt.QVector4D)): - if isinstance(data, qt.QVector3D): - text = '(x: %g; y: %g; z: %g)' % (data.x(), data.y(), data.z()) - elif isinstance(data, qt.QVector4D): - text = '(%g; %g; %g; %g)' % (data.x(), data.y(), data.z(), data.w()) - else: - text = '' - - painter.save() - painter.setRenderHint(qt.QPainter.Antialiasing, True) - - # Select palette color group - 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 - - # Draw background if selected - if option.state & qt.QStyle.State_Selected: - brush = option.palette.brush(colorGroup, - qt.QPalette.Highlight) - painter.fillRect(option.rect, brush) - - # Draw text - if option.state & qt.QStyle.State_Selected: - colorRole = qt.QPalette.HighlightedText - else: - colorRole = qt.QPalette.WindowText - color = option.palette.color(colorGroup, colorRole) - painter.setPen(qt.QPen(color)) - painter.drawText(option.rect, qt.Qt.AlignLeft, text) - - painter.restore() - - # The following commented code does the same as QPainter based code - # but it does not work with PySide - # self.initStyleOption(option, index) - # option.text = text - # widget = option.widget - # style = qt.QApplication.style() if not widget else widget.style() - # style.drawControl(qt.QStyle.CE_ItemViewItem, option, painter, widget) - - else: - super(ParameterTreeDelegate, self).paint(painter, option, index) - - def _commit(self, *args): - """Commit data to the model from editors""" - sender = self.sender() - self.commitData.emit(sender) - - def editorEvent(self, event, model, option, index): - """See :meth:`QStyledItemDelegate.editorEvent`""" - if (event.type() == qt.QEvent.MouseButtonPress and - isinstance(index.data(qt.Qt.EditRole), qt.QColor)): - initialColor = index.data(qt.Qt.EditRole) - - def callback(color): - theModel = index.model() - theModel.setData(index, color, qt.Qt.EditRole) - - dialog = qt.QColorDialog(self.parent()) - # dialog.setOption(qt.QColorDialog.ShowAlphaChannel, True) - if sys.platform == 'darwin': - # Use of native color dialog on macos might cause problems - dialog.setOption(qt.QColorDialog.DontUseNativeDialog, True) - dialog.setCurrentColor(initialColor) - dialog.currentColorChanged.connect(callback) - if dialog.exec_() == qt.QDialog.Rejected: - # Reset color - dialog.setCurrentColor(initialColor) - - return True - else: - return super(ParameterTreeDelegate, self).editorEvent( - event, model, option, index) - - def createEditor(self, parent, option, index): - """See :meth:`QStyledItemDelegate.createEditor`""" - data = index.data(qt.Qt.EditRole) - data = self._fixVariant(data) - editorHint = index.data(qt.Qt.UserRole) - - if callable(editorHint): - editor = editorHint() - assert isinstance(editor, qt.QWidget) - editor.setParent(parent) - - elif isinstance(data, numbers.Number) and editorHint is not None: - # Use a slider - editor = IntSliderEditor(parent) - range_ = editorHint - editor.setRange(*range_) - editor.sliderReleased.connect(self._commit) - - elif isinstance(data, six.string_types) and editorHint is not None: - # Use a combo box - editor = qt.QComboBox(parent) - if data not in editorHint: - editor.addItem(data) - editor.addItems(editorHint) - - index = editor.findText(data) - editor.setCurrentIndex(index) - - editor.currentIndexChanged.connect(self._commit) - - else: - # Handle overridden editors from Python - # Mimic Qt C++ implementation - for type_, editorClass in self.EDITORS.items(): - if isinstance(data, type_): - editor = editorClass(parent) - metaObject = editor.metaObject() - userProperty = metaObject.userProperty() - if userProperty.isValid() and userProperty.hasNotifySignal(): - notifySignal = userProperty.notifySignal() - if hasattr(notifySignal, 'signature'): # Qt4 - signature = notifySignal.signature() - else: - signature = notifySignal.methodSignature() - if qt.BINDING == 'PySide2': - signature = signature.data() - else: - signature = bytes(signature) - - if hasattr(signature, 'decode'): # For PySide with python3 - signature = signature.decode('ascii') - signalName = signature.split('(')[0] - - signal = getattr(editor, signalName) - signal.connect(self._commit) - break - - else: # Default handling for default types - return super(ParameterTreeDelegate, self).createEditor( - parent, option, index) - - editor.setAutoFillBackground(True) - return editor - - def setModelData(self, editor, model, index): - """See :meth:`QStyledItemDelegate.setModelData`""" - if isinstance(editor, tuple(self.EDITORS.values())): - # Special handling of Python classes - # Translation of QStyledItemDelegate::setModelData to Python - # To make it work with Python QVariant wrapping/unwrapping - name = editor.metaObject().userProperty().name() - if not name: - pass # TODO handle the case of missing user property - if name: - if hasattr(editor, name): - value = getattr(editor, name) - else: - value = editor.property(name) - model.setData(index, value, qt.Qt.EditRole) - - else: - super(ParameterTreeDelegate, self).setModelData(editor, model, index) - - -class ParamTreeView(qt.QTreeView): - """QTreeView specific to handle plot3d scene and object parameters. - - It provides additional editors and specific creation of persistent editors. - - :param parent: The widget's parent. - """ - - def __init__(self, parent=None): - super(ParamTreeView, self).__init__(parent) - - header = self.header() - header.setMinimumSectionSize(128) # For colormap pixmaps - if hasattr(header, 'setSectionResizeMode'): # Qt5 - header.setSectionResizeMode(qt.QHeaderView.ResizeToContents) - else: # Qt4 - header.setResizeMode(qt.QHeaderView.ResizeToContents) - - delegate = ParameterTreeDelegate() - self.setItemDelegate(delegate) - - self.setSelectionBehavior(qt.QAbstractItemView.SelectRows) - self.setSelectionMode(qt.QAbstractItemView.SingleSelection) - - self.expanded.connect(self._expanded) - - self.setEditTriggers(qt.QAbstractItemView.CurrentChanged | - qt.QAbstractItemView.DoubleClicked) - - self.__persistentEditors = set() - - def _openEditorForIndex(self, index): - """Check if it has to open a persistent editor for a specific cell. - - :param QModelIndex index: The cell index - """ - if index.flags() & qt.Qt.ItemIsEditable: - data = index.data(qt.Qt.EditRole) - editorHint = index.data(qt.Qt.UserRole) - if (isinstance(data, bool) or - callable(editorHint) or - (isinstance(data, numbers.Number) and editorHint)): - self.openPersistentEditor(index) - self.__persistentEditors.add(index) - - def _openEditors(self, parent=qt.QModelIndex()): - """Open persistent editors in a subtree starting at parent. - - :param QModelIndex parent: The root of the subtree to process. - """ - model = self.model() - if model is not None: - for index in visitQAbstractItemModel(model, parent): - self._openEditorForIndex(index) - - def setModel(self, model): - """Set the model this TreeView is displaying - - :param QAbstractItemModel model: - """ - super(ParamTreeView, self).setModel(model) - self._openEditors() - - def rowsInserted(self, parent, start, end): - """See :meth:`QTreeView.rowsInserted`""" - super(ParamTreeView, self).rowsInserted(parent, start, end) - model = self.model() - if model is not None: - for row in range(start, end+1): - self._openEditorForIndex(model.index(row, 1, parent)) - self._openEditors(model.index(row, 0, parent)) - - def _expanded(self, index): - """Handle QTreeView expanded signal""" - name = index.data(qt.Qt.DisplayRole) - if name == 'Transform': - rotateIndex = self.model().index(1, 0, index) - self.setExpanded(rotateIndex, True) - - def dataChanged(self, topLeft, bottomRight, roles=()): - """Handle model dataChanged signal eventually closing editors""" - if roles: # Qt 5 - super(ParamTreeView, self).dataChanged(topLeft, bottomRight, roles) - else: # Qt4 compatibility - super(ParamTreeView, self).dataChanged(topLeft, bottomRight) - if not roles or qt.Qt.UserRole in roles: # Check editorHint update - for row in range(topLeft.row(), bottomRight.row() + 1): - for column in range(topLeft.column(), bottomRight.column() + 1): - index = topLeft.sibling(row, column) - if index.isValid(): - if self._isPersistentEditorOpen(index): - self.closePersistentEditor(index) - self._openEditorForIndex(index) - - def _isPersistentEditorOpen(self, index): - """Returns True if a persistent editor is opened for index - - :param QModelIndex index: - :rtype: bool - """ - return index in self.__persistentEditors - - def selectionCommand(self, index, event=None): - """Filter out selection of not selectable items""" - if index.flags() & qt.Qt.ItemIsSelectable: - return super(ParamTreeView, self).selectionCommand(index, event) - else: - return qt.QItemSelectionModel.NoUpdate |