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