Source code for exopy.utils.widgets.qt_list_str_widget

# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright 2015-2018 by Exopy Authors, see AUTHORS for more details.
#
# Distributed under the terms of the BSD license.
#
# The full license is in the file LICENCE, distributed with this software.
# -----------------------------------------------------------------------------
"""Basic list widget limited to selection.

"""
from atom.api import (Bool, List, Value, Int, Callable, Dict, set_default)
from enaml.widgets.api import RawWidget
from enaml.core.declarative import d_
from enaml.qt import QtCore, QtWidgets

# cyclic notification guard flags
INDEX_GUARD = 0x1


[docs]class QtListStrWidget(RawWidget): """A list widget for Enaml displaying objects as strings. Objects that are not string should be convertible to str and hashable. """ #: The list of str being viewed items = d_(List()) #: The list of the currently selected str selected_item = d_(Value()) selected_items = d_(List()) #: Whether or not the user can select multiple lines multiselect = d_(Bool(False)) #: Callable to use to build a unicode representation of the objects #: (one at a time). to_string = d_(Callable(str)) #: Whether or not to sort the items before inserting them. sort = d_(Bool(True)) hug_width = set_default(str('strong')) hug_height = set_default(str('ignore')) # PySide requires weakrefs for using bound methods as slots. # PyQt doesn't, but executes unsafe code if not using weakrefs. __slots__ = '__weakref__'
[docs] def initialize(self): """Ensures that the selected members always have meaningful values. """ self._build_mapping(self.items) if self.items: self._do_default_selection() super(QtListStrWidget, self).initialize()
[docs] def refresh_items(self): """Refresh the items displayed in the list. This is useful after an inplace operation on the list which is not notified. """ self._post_setattr_items([], self.items)
[docs] def clear_selection(self): """Make no item be selected. """ # HINT : this only gives a visual hint to the user the selected value # is not updated. widget = self.get_widget() if widget is not None: widget.clearSelection()
[docs] def create_widget(self, parent): """ Create the QListView widget. """ # Create the list widget. widget = QtWidgets.QListWidget(parent) # Populate the widget. self._set_widget_items(widget) # Set the selection mode. if self.multiselect: mode = QtWidgets.QAbstractItemView.ExtendedSelection selected = self.selected_items else: mode = QtWidgets.QAbstractItemView.SingleSelection selected = [self.selected_item] widget.setSelectionMode(mode) self.proxy.widget = widget # Anticipated so that selection works # Make sure the widget selection reflects the members. if self.items: self._select_on_widget(selected, widget) widget.itemSelectionChanged.connect(self.on_selection) return widget
[docs] def on_selection(self): """ The signal handler for the index changed signal. """ if not self._guard & INDEX_GUARD: self._guard ^= INDEX_GUARD widget = self.get_widget() selected = [self._rmap[index.row()] for index in widget.selectedIndexes()] if selected: if self.multiselect: self.selected_items = selected else: self.selected_item = selected[0] self._guard ^= INDEX_GUARD
# ========================================================================= # --- Private API --------------------------------------------------------- # ========================================================================= #: Guard bit field. _guard = Int(0) #: Mapping between user list objects and widget list indexes. _map = Dict() #: Mapping between the widget list indexes and the user list objects. _rmap = Dict() #: String representation of the objects in the widget order. _items = List() def _post_setattr_items(self, old, new): """Update the widget content when the items changes. """ self._build_mapping(new) self._set_widget_items(self.get_widget()) if new: self._do_default_selection() else: if self.multiselect: self.selected_items = [] else: self.selected_item = None def _post_setattr_multiselect(self, old, new): """Update the widget selection mode. """ widget = self.get_widget() if widget is None: return if new: mode = QtWidgets.QAbstractItemView.ExtendedSelection if self.items: self.selected_items = [self.selected_item] else: mode = QtWidgets.QAbstractItemView.SingleSelection if self.items: self.selected_item = self.selected_items[0] widget.setSelectionMode(mode) if self.items: self._select_on_widget(self.selected_items if new else [self.selected_item]) def _post_setattr_selected_item(self, old, new): """Update the widget when the selected item is changed externally. """ if not self._guard & INDEX_GUARD and self.items: self._guard ^= INDEX_GUARD self._select_on_widget([new]) self._guard ^= INDEX_GUARD def _post_setattr_selected_items(self, old, new): """Update the widget when the selected items are changed externally. """ if not self._guard & INDEX_GUARD and self.items: self._guard ^= INDEX_GUARD self._select_on_widget(new) self._guard ^= INDEX_GUARD def _build_mapping(self, items): """Build the mapping between user objects and widget indexes. """ items_map = {self.to_string(o): o for o in items} items = sorted(items_map) if self.sort else list(items_map) self._rmap = {i: items_map[item] for i, item in enumerate(items)} self._map = {v: k for k, v in self._rmap.items()} self._items = items def _set_widget_items(self, widget): """Set the list items sorting if necessary. """ if widget is not None: widget.clearSelection() widget.clear() for i in self._items: widget.addItem(i) def _do_default_selection(self): """Determine the items that should be selected. This method also ensures that the widget state reflects the member values. """ items = self.items if not self.multiselect: if self.selected_item not in items: self.selected_item = self._rmap[0] else: self._post_setattr_selected_item(None, self.selected_item) else: if not any(i in items for i in self.selected_items): self.selected_items = [self._rmap[0]] else: items_selected = [i for i in self.selected_items if i in items] if len(items_selected) == len(self.selected_item): self._post_setattr_selected_items(None, items) else: self.selected_items = items_selected def _select_on_widget(self, items, widget=None): """Select the specified items on the widget. """ if widget is None: widget = self.get_widget() if widget is not None: widget.setCurrentItem(widget.item(0), QtCore.QItemSelectionModel.Clear) item_map = self._map for n in items: widget.setCurrentItem(widget.item(item_map[n]), QtCore.QItemSelectionModel.Select)