Source code for silx.gui.plot3d.tools.PositionInfoWidget

# /*##########################################################################
#
# Copyright (c) 2018-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 provides a widget that displays data values of a SceneWidget.
"""

__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "01/10/2018"


import logging
import weakref

from ... import qt
from .. import actions
from .. import items
from ..items import volume
from ..SceneWidget import SceneWidget


_logger = logging.getLogger(__name__)


[docs] class PositionInfoWidget(qt.QWidget): """Widget displaying information about picked position :param QWidget parent: See :class:`QWidget` """ def __init__(self, parent=None): super(PositionInfoWidget, self).__init__(parent) self._sceneWidgetRef = None self.setToolTip("Double-click on a data point to show its value") layout = qt.QBoxLayout(qt.QBoxLayout.LeftToRight, self) self._xLabel = self._addInfoField("X") self._yLabel = self._addInfoField("Y") self._zLabel = self._addInfoField("Z") self._dataLabel = self._addInfoField("Data") self._itemLabel = self._addInfoField("Item") layout.addStretch(1) self._action = actions.mode.PickingModeAction(parent=self) self._action.setText("Selection") self._action.setToolTip( "Toggle selection information update with left button click" ) self._action.sigSceneClicked.connect(self.pick) self._action.changed.connect(self.__actionChanged) self._action.setChecked(False) # Disabled by default self.__actionChanged() # Sync action/widget def __actionChanged(self): """Handle toggle action change signal""" if self.toggleAction().isChecked() != self.isEnabled(): self.setEnabled(self.toggleAction().isChecked())
[docs] def toggleAction(self): """The action to toggle the picking mode. :rtype: QAction """ return self._action
def _addInfoField(self, label): """Add a description: info widget to this widget :param str label: Description label :return: The QLabel used to display the info :rtype: QLabel """ subLayout = qt.QHBoxLayout() subLayout.setContentsMargins(0, 0, 0, 0) subLayout.addWidget(qt.QLabel(label + ":")) widget = qt.QLabel("-") widget.setAlignment(qt.Qt.AlignLeft | qt.Qt.AlignVCenter) widget.setTextInteractionFlags(qt.Qt.TextSelectableByMouse) metrics = widget.fontMetrics() if qt.BINDING == "PyQt5": width = metrics.width("#######") else: # Qt6 width = metrics.horizontalAdvance("#######") widget.setMinimumWidth(width) subLayout.addWidget(widget) subLayout.addStretch(1) layout = self.layout() layout.addLayout(subLayout) return widget
[docs] def getSceneWidget(self): """Returns the associated :class:`SceneWidget` or None. :rtype: Union[None,~silx.gui.plot3d.SceneWidget.SceneWidget] """ if self._sceneWidgetRef is None: return None else: return self._sceneWidgetRef()
[docs] def setSceneWidget(self, widget): """Set the associated :class:`SceneWidget` :param ~silx.gui.plot3d.SceneWidget.SceneWidget widget: 3D scene for which to display information """ if widget is not None and not isinstance(widget, SceneWidget): raise ValueError("widget must be a SceneWidget or None") self._sceneWidgetRef = None if widget is None else weakref.ref(widget) self.toggleAction().setPlot3DWidget(widget)
[docs] def clear(self): """Clean-up displayed values""" for widget in ( self._xLabel, self._yLabel, self._zLabel, self._dataLabel, self._itemLabel, ): widget.setText("-")
_SUPPORTED_ITEMS = ( items.Scatter3D, items.Scatter2D, items.ImageData, items.ImageRgba, items.HeightMapData, items.HeightMapRGBA, items.Mesh, items.Box, items.Cylinder, items.Hexagon, volume.CutPlane, volume.Isosurface, ) """Type of items that are picked""" def _isSupportedItem(self, item): """Returns True if item is of supported type :param Item3D item: The Item3D to check :rtype: bool """ return isinstance(item, self._SUPPORTED_ITEMS)
[docs] def pick(self, x, y): """Pick items in the associated SceneWidget and display result Only the closest point is displayed. :param int x: X coordinate in pixel in the SceneWidget :param int y: Y coordinate in pixel in the SceneWidget """ self.clear() sceneWidget = self.getSceneWidget() if sceneWidget is None: # No associated widget _logger.info("Picking without associated SceneWidget") return # Find closest (and latest in the tree) supported item closestNdcZ = float("inf") picking = None for result in sceneWidget.pickItems(x, y, condition=self._isSupportedItem): ndcZ = result.getPositions("ndc", copy=False)[0, 2] if ndcZ <= closestNdcZ: closestNdcZ = ndcZ picking = result if picking is None: return # No picked item item = picking.getItem() self._itemLabel.setText(item.getLabel()) positions = picking.getPositions("scene", copy=False) x, y, z = positions[0] self._xLabel.setText("%g" % x) self._yLabel.setText("%g" % y) self._zLabel.setText("%g" % z) data = picking.getData(copy=False) if data is not None: data = data[0] if hasattr(data, "__len__"): text = " ".join(["%.3g"] * len(data)) % tuple(data) else: text = "%g" % data self._dataLabel.setText(text)
[docs] def updateInfo(self): """Update information according to cursor position""" widget = self.getSceneWidget() if widget is None: _logger.info("Update without associated SceneWidget") self.clear() return position = widget.mapFromGlobal(qt.QCursor.pos()) self.pick(position.x(), position.y())