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