summaryrefslogtreecommitdiff
path: root/silx/gui/plot3d/ParamTreeView.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot3d/ParamTreeView.py')
-rw-r--r--silx/gui/plot3d/ParamTreeView.py546
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