Source code for silx.gui.plot.tools.CurveLegendsWidget

# /*##########################################################################
#
# Copyright (c) 2018-2020 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 to display :class:`PlotWidget` curve legends.
"""

__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "20/07/2018"


import logging
import weakref


from ... import qt
from ...widgets.FlowLayout import FlowLayout as _FlowLayout
from ..LegendSelector import LegendIcon as _LegendIcon
from .. import items


_logger = logging.getLogger(__name__)


class _LegendWidget(qt.QWidget):
    """Widget displaying curve style and its legend

    :param QWidget parent: See :class:`QWidget`
    :param ~silx.gui.plot.items.Curve curve: Associated curve
    """

    def __init__(self, parent, curve):
        super(_LegendWidget, self).__init__(parent)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(10, 0, 10, 0)

        curve.sigItemChanged.connect(self._curveChanged)

        icon = _LegendIcon(curve=curve)
        layout.addWidget(icon)

        label = qt.QLabel(curve.getName())
        label.setAlignment(qt.Qt.AlignLeft | qt.Qt.AlignVCenter)
        layout.addWidget(label)

        self._update()

    def getCurve(self):
        """Returns curve associated to this widget

        :rtype: Union[~silx.gui.plot.items.Curve,None]
        """
        icon = self.findChild(_LegendIcon)
        return icon.getCurve()

    def _update(self):
        """Update widget according to current curve state."""
        curve = self.getCurve()
        if curve is None:
            _logger.error("Curve no more exists")
            self.setVisible(False)
            return

        self.setEnabled(curve.isVisible())

        label = self.findChild(qt.QLabel)
        if curve.isHighlighted():
            label.setStyleSheet("border: 1px solid black")
        else:
            label.setStyleSheet("")

    def _curveChanged(self, event):
        """Handle update of curve item

        :param event: Kind of change
        """
        if event in (
            items.ItemChangedType.VISIBLE,
            items.ItemChangedType.HIGHLIGHTED,
            items.ItemChangedType.HIGHLIGHTED_STYLE,
        ):
            self._update()


[docs] class CurveLegendsWidget(qt.QWidget): """Widget displaying curves legends in a plot :param QWidget parent: See :class:`QWidget` """ sigCurveClicked = qt.Signal(object) """Signal emitted when the legend of a curve is clicked It provides the corresponding curve. """ def __init__(self, parent=None): super(CurveLegendsWidget, self).__init__(parent) self._clicked = None self._legends = {} self._plotRef = None
[docs] def layout(self): layout = super(CurveLegendsWidget, self).layout() if layout is None: # Lazy layout initialization to allow overloading layout = _FlowLayout() layout.setHorizontalSpacing(0) self.setLayout(layout) return layout
[docs] def getPlotWidget(self): """Returns the associated :class:`PlotWidget` :rtype: Union[~silx.gui.plot.PlotWidget,None] """ return None if self._plotRef is None else self._plotRef()
[docs] def setPlotWidget(self, plot): """Set the associated :class:`PlotWidget` :param ~silx.gui.plot.PlotWidget plot: Plot widget to attach """ previousPlot = self.getPlotWidget() if previousPlot is not None: previousPlot.sigItemAdded.disconnect(self._itemAdded) previousPlot.sigItemAboutToBeRemoved.disconnect(self._itemRemoved) for legend in list(self._legends.keys()): self._removeLegend(legend) self._plotRef = None if plot is None else weakref.ref(plot) if plot is not None: plot.sigItemAdded.connect(self._itemAdded) plot.sigItemAboutToBeRemoved.connect(self._itemRemoved) for legend in plot.getAllCurves(just_legend=True): self._addLegend(legend)
[docs] def curveAt(self, *args): """Returns the curve object represented at the given position Either takes a QPoint or x and y as input in widget coordinates. :rtype: Union[~silx.gui.plot.items.Curve,None] """ if len(args) == 1: point = args[0] elif len(args) == 2: point = qt.QPoint(*args) else: raise ValueError("Unsupported arguments") assert isinstance(point, qt.QPoint) widget = self.childAt(point) while widget not in (self, None): if isinstance(widget, _LegendWidget): return widget.getCurve() widget = widget.parent() return None # No widget or not in _LegendWidget
def _itemAdded(self, item): """Handle item added to the plot content""" if isinstance(item, items.Curve): self._addLegend(item.getName()) def _itemRemoved(self, item): """Handle item removed from the plot content""" if isinstance(item, items.Curve): self._removeLegend(item.getName()) def _addLegend(self, legend): """Add a curve to the legends :param str legend: Curve's legend """ if legend in self._legends: return # Can happen when changing curve's y axis plot = self.getPlotWidget() if plot is None: return None curve = plot.getCurve(legend) if curve is None: _logger.error("Curve not found: %s" % legend) return widget = _LegendWidget(parent=self, curve=curve) self.layout().addWidget(widget) self._legends[legend] = widget def _removeLegend(self, legend): """Remove a curve from the legends if it exists :param str legend: The curve's legend """ widget = self._legends.pop(legend, None) if widget is None: _logger.warning("Unknown legend: %s" % legend) else: self.layout().removeWidget(widget) widget.setParent(None)
[docs] def mousePressEvent(self, event): if event.button() == qt.Qt.LeftButton: self._clicked = event.pos()
_CLICK_THRESHOLD = 5 """Threshold for clicks"""
[docs] def mouseMoveEvent(self, event): if self._clicked is not None: dx = abs(self._clicked.x() - event.pos().x()) dy = abs(self._clicked.y() - event.pos().y()) if dx > self._CLICK_THRESHOLD or dy > self._CLICK_THRESHOLD: self._clicked = None # Click is cancelled
[docs] def mouseReleaseEvent(self, event): if event.button() == qt.Qt.LeftButton and self._clicked is not None: curve = self.curveAt(event.pos()) if curve is not None: self.sigCurveClicked.emit(curve) self._clicked = None