diff options
Diffstat (limited to 'silx/gui/plot/ItemsSelectionDialog.py')
-rw-r--r-- | silx/gui/plot/ItemsSelectionDialog.py | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/silx/gui/plot/ItemsSelectionDialog.py b/silx/gui/plot/ItemsSelectionDialog.py new file mode 100644 index 0000000..acb287a --- /dev/null +++ b/silx/gui/plot/ItemsSelectionDialog.py @@ -0,0 +1,282 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2017 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 dialog widget to select plot items. + +.. autoclass:: ItemsSelectionDialog + +""" + +__authors__ = ["P. Knobel"] +__license__ = "MIT" +__date__ = "28/06/2017" + +import logging + +from silx.gui import qt +from silx.gui.plot.PlotWidget import PlotWidget + +_logger = logging.getLogger(__name__) + + +class KindsSelector(qt.QListWidget): + """List widget allowing to select plot item kinds + ("curve", "scatter", "image"...) + """ + sigSelectedKindsChanged = qt.Signal(list) + + def __init__(self, parent=None, kinds=None): + """ + + :param parent: Parent QWidget or None + :param tuple(str) kinds: Sequence of kinds. If None, the default + behavior is to provide a checkbox for all possible item kinds. + """ + qt.QListWidget.__init__(self, parent) + + self.plot_item_kinds = [] + + self.setAvailableKinds(kinds if kinds is not None else PlotWidget.ITEM_KINDS) + + self.setSelectionMode(qt.QAbstractItemView.ExtendedSelection) + self.selectAll() + + self.itemSelectionChanged.connect(self.emitSigKindsSelectionChanged) + + def emitSigKindsSelectionChanged(self): + self.sigSelectedKindsChanged.emit(self.selectedKinds) + + @property + def selectedKinds(self): + """Tuple of all selected kinds (as strings).""" + # check for updates when self.itemSelectionChanged + return [item.text() for item in self.selectedItems()] + + def setAvailableKinds(self, kinds): + """Set a list of kinds to be displayed. + + :param list[str] kinds: Sequence of kinds + """ + self.plot_item_kinds = kinds + + self.clear() + for kind in self.plot_item_kinds: + item = qt.QListWidgetItem(self) + item.setText(kind) + self.addItem(item) + + def selectAll(self): + """Select all available kinds.""" + if self.selectionMode() in [qt.QAbstractItemView.SingleSelection, + qt.QAbstractItemView.NoSelection]: + raise RuntimeError("selectAll requires a multiple selection mode") + for i in range(self.count()): + self.item(i).setSelected(True) + + +class PlotItemsSelector(qt.QTableWidget): + """Table widget displaying the legend and kind of all + plot items corresponding to a list of specified kinds. + + Selected plot items are provided as property :attr:`selectedPlotItems`. + You can be warned of selection changes by listening to signal + :attr:`itemSelectionChanged`. + """ + def __init__(self, parent=None, plot=None): + if plot is None or not isinstance(plot, PlotWidget): + raise AttributeError("parameter plot is required") + self.plot = plot + """:class:`PlotWidget` instance""" + + self.plot_item_kinds = None + """List of plot item kinds (strings)""" + + qt.QTableWidget.__init__(self, parent) + + self.setColumnCount(2) + + self.setSelectionBehavior(qt.QTableWidget.SelectRows) + + def _clear(self): + self.clear() + self.setHorizontalHeaderLabels(["legend", "type"]) + + def setAllKindsFilter(self): + """Display all kinds of plot items.""" + self.setKindsFilter(PlotWidget.ITEM_KINDS) + + def setKindsFilter(self, kinds): + """Set list of all kinds of plot items to be displayed. + + :param list[str] kinds: Sequence of kinds + """ + if not set(kinds) <= set(PlotWidget.ITEM_KINDS): + raise KeyError("Illegal plot item kinds: %s" % + set(kinds) - set(PlotWidget.ITEM_KINDS)) + self.plot_item_kinds = kinds + + self.updatePlotItems() + + def updatePlotItems(self): + self._clear() + + nrows = len(self.plot._getItems(kind=self.plot_item_kinds, + just_legend=True)) + self.setRowCount(nrows) + + # respect order of kinds as set in method setKindsFilter + i = 0 + for kind in self.plot_item_kinds: + for plot_item in self.plot._getItems(kind=kind): + legend_twitem = qt.QTableWidgetItem(plot_item.getLegend()) + self.setItem(i, 0, legend_twitem) + + kind_twitem = qt.QTableWidgetItem(kind) + self.setItem(i, 1, kind_twitem) + i += 1 + + @property + def selectedPlotItems(self): + """List of all selected items""" + selection_model = self.selectionModel() + selected_rows_idx = selection_model.selectedRows() + selected_rows = [idx.row() for idx in selected_rows_idx] + + items = [] + for row in selected_rows: + legend = self.item(row, 0).text() + kind = self.item(row, 1).text() + items.append(self.plot._getItem(kind, legend)) + + return items + + +class ItemsSelectionDialog(qt.QDialog): + """This widget is a modal dialog allowing to select one or more plot + items, in a table displaying their legend and kind. + + Public methods: + + - :meth:`getSelectedItems` + - :meth:`setAvailableKinds` + - :meth:`setItemsSelectionMode` + + This widget inherits QDialog and therefore implements the usual + dialog methods, e.g. :meth:`exec_`. + + A trivial usage example would be:: + + isd = ItemsSelectionDialog(plot=my_plot_widget) + isd.setItemsSelectionMode(qt.QTableWidget.SingleSelection) + result = isd.exec_() + if result: + for item in isd.getSelectedItems(): + print(item.getLegend(), type(item)) + else: + print("Selection cancelled") + """ + def __init__(self, parent=None, plot=None): + if plot is None or not isinstance(plot, PlotWidget): + raise AttributeError("parameter plot is required") + qt.QDialog.__init__(self, parent) + + self.setWindowTitle("Plot items selector") + + kind_selector_label = qt.QLabel("Filter item kinds:", self) + item_selector_label = qt.QLabel("Select items:", self) + + self.kind_selector = KindsSelector(self) + self.kind_selector.setToolTip( + "select one or more item kinds to show them in the item list") + + self.item_selector = PlotItemsSelector(self, plot) + self.item_selector.setToolTip("select items") + + self.item_selector.setKindsFilter(self.kind_selector.selectedKinds) + self.kind_selector.sigSelectedKindsChanged.connect( + self.item_selector.setKindsFilter + ) + + okb = qt.QPushButton("OK", self) + okb.clicked.connect(self.accept) + + cancelb = qt.QPushButton("Cancel", self) + cancelb.clicked.connect(self.reject) + + layout = qt.QGridLayout(self) + layout.addWidget(kind_selector_label, 0, 0) + layout.addWidget(item_selector_label, 0, 1) + layout.addWidget(self.kind_selector, 1, 0) + layout.addWidget(self.item_selector, 1, 1) + layout.addWidget(okb, 2, 0) + layout.addWidget(cancelb, 2, 1) + + self.setLayout(layout) + + def getSelectedItems(self): + """Return a list of selected plot items + + :return: List of selected plot items + :rtype: list[silx.gui.plot.items.Item]""" + return self.item_selector.selectedPlotItems + + def setAvailableKinds(self, kinds): + """Set a list of kinds to be displayed. + + :param list[str] kinds: Sequence of kinds + """ + self.kind_selector.setAvailableKinds(kinds) + + def selectAllKinds(self): + self.kind_selector.selectAll() + + def setItemsSelectionMode(self, mode): + """Set selection mode for plot item (single item selection, + multiple...). + + :param mode: One of :class:`QTableWidget` selection modes + """ + if mode == self.item_selector.SingleSelection: + self.item_selector.setToolTip( + "Select one item by clicking on it.") + elif mode == self.item_selector.MultiSelection: + self.item_selector.setToolTip( + "Select one or more items by clicking with the left mouse" + " button.\nYou can unselect items by clicking them again.\n" + "Multiple items can be toggled by dragging the mouse over them.") + elif mode == self.item_selector.ExtendedSelection: + self.item_selector.setToolTip( + "Select one or more items. You can select multiple items " + "by keeping the Ctrl key pushed when clicking.\nYou can " + "select a range of items by clicking on the first and " + "last while keeping the Shift key pushed.") + elif mode == self.item_selector.ContiguousSelection: + self.item_selector.setToolTip( + "Select one item by clicking on it. If you press the Shift" + " key while clicking on a second item,\nall items between " + "the two will be selected.") + elif mode == self.item_selector.NoSelection: + raise ValueError("The NoSelection mode is not allowed " + "in this context.") + self.item_selector.setSelectionMode(mode) |