""" allows multiple selection in a large list Contact : marco@opengis.ch .. note:: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. """ from builtins import range __author__ = 'marco@opengis.ch' __revision__ = '$Format:%H$' __date__ = '9/07/2013' from qgis.PyQt.QtWidgets import (QGroupBox, QPushButton, QSizePolicy, QLabel, QHBoxLayout, QVBoxLayout, QListWidget, QAbstractItemView) from qgis.PyQt.QtGui import QFont from qgis.PyQt.QtCore import Qt, QSize, pyqtSignal class ListMultiSelectWidget(QGroupBox): """Widget to show two parallel lists and move elements between the two usage from code: self.myWidget = ListMultiSelectWidget(title='myTitle') self.myLayout.insertWidget(1, self.myWidget) usage from designer: insert a QGroupBox in your UI file optionally give a title to the QGroupBox promote it to ListMultiSelectWidget """ selection_changed = pyqtSignal() def __init__(self, parent=None, title=None): QGroupBox.__init__(self) self.setTitle(title) self.selected_widget = None self.deselected_widget = None self._setupUI() # connect actions self.select_all_btn.clicked.connect(self._select_all) self.deselect_all_btn.clicked.connect(self._deselect_all) self.select_btn.clicked.connect(self._select) self.deselect_btn.clicked.connect(self._deselect) self.deselected_widget.itemDoubleClicked.connect(self._select) self.selected_widget.itemDoubleClicked.connect(self._deselect) def get_selected_items(self): """ :return list with all the selected items text """ return self._get_items(self.selected_widget) def get_deselected_items(self): """ :return list with all the deselected items text """ return self._get_items(self.deselected_widget) def add_selected_items(self, items): """ :param items list of strings to be added in the selected list """ self._add_items(self.selected_widget, items) def add_deselected_items(self, items): """ :param items list of strings to be added in the deselected list """ self._add_items(self.deselected_widget, items) def set_selected_items(self, items): """ :param items list of strings to be set as the selected list """ self._set_items(self.selected_widget, items) def set_deselected_items(self, items): """ :param items list of strings to be set as the deselected list """ self._set_items(self.deselected_widget, items) def clear(self): """ removes all items from selected and deselected """ self.set_selected_items([]) self.set_deselected_items([]) def addItem(self, item): """ This is for Processing :param item: string to be added in the deselected list """ self.add_deselected_items([item]) def addItems(self, items): """ This is for Processing :param items: list of strings to be added in the deselected list """ self.add_deselected_items(items) def _get_items(self, widget): for i in range(widget.count()): yield widget.item(i).text() def _set_items(self, widget, items): widget.clear() self._add_items(widget, items) def _add_items(self, widget, items): widget.addItems(items) def _select_all(self): self.deselected_widget.selectAll() self._do_move(self.deselected_widget, self.selected_widget) def _deselect_all(self): self.selected_widget.selectAll() self._do_move(self.selected_widget, self.deselected_widget) def _select(self): self._do_move(self.deselected_widget, self.selected_widget) def _deselect(self): self._do_move(self.selected_widget, self.deselected_widget) def _do_move(self, fromList, toList): for item in fromList.selectedItems(): prev_from_item = fromList.item(fromList.row(item) - 1) toList.addItem(fromList.takeItem(fromList.row(item))) fromList.scrollToItem(prev_from_item) self.selection_changed.emit() def _setupUI(self): self.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Preferred) self.setMinimumHeight(180) self.main_horizontal_layout = QHBoxLayout(self) italic_font = QFont() italic_font.setItalic(True) # deselected widget self.deselected_widget = QListWidget(self) self._set_list_widget_defaults(self.deselected_widget) deselected_label = QLabel() deselected_label.setText('Deselected') deselected_label.setAlignment(Qt.AlignCenter) deselected_label.setFont(italic_font) deselected_v_layout = QVBoxLayout() deselected_v_layout.addWidget(deselected_label) deselected_v_layout.addWidget(self.deselected_widget) # selected widget self.selected_widget = QListWidget(self) self._set_list_widget_defaults(self.selected_widget) selected_label = QLabel() selected_label.setText('Selected') selected_label.setAlignment(Qt.AlignCenter) selected_label.setFont(italic_font) selected_v_layout = QVBoxLayout() selected_v_layout.addWidget(selected_label) selected_v_layout.addWidget(self.selected_widget) # buttons self.buttons_vertical_layout = QVBoxLayout() self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1) self.select_all_btn = SmallQPushButton('>>') self.deselect_all_btn = SmallQPushButton('<<') self.select_btn = SmallQPushButton('>') self.deselect_btn = SmallQPushButton('<') self.select_btn.setToolTip('Add the selected items') self.deselect_btn.setToolTip('Remove the selected items') self.select_all_btn.setToolTip('Add all') self.deselect_all_btn.setToolTip('Remove all') # add buttons spacer_label = QLabel() # pragmatic way to create a spacer with # the same height of the labels on top # of the lists, in order to align the # buttons with the lists. self.buttons_vertical_layout.addWidget(spacer_label) self.buttons_vertical_layout.addWidget(self.select_btn) self.buttons_vertical_layout.addWidget(self.deselect_btn) self.buttons_vertical_layout.addWidget(self.select_all_btn) self.buttons_vertical_layout.addWidget(self.deselect_all_btn) # add sub widgets self.main_horizontal_layout.addLayout(deselected_v_layout) self.main_horizontal_layout.addLayout(self.buttons_vertical_layout) self.main_horizontal_layout.addLayout(selected_v_layout) def _set_list_widget_defaults(self, widget): widget.setAlternatingRowColors(True) widget.setSortingEnabled(True) widget.setDragEnabled(True) widget.setDragDropMode(QAbstractItemView.DragDrop) widget.setDragDropOverwriteMode(False) widget.setDefaultDropAction(Qt.MoveAction) widget.setSelectionMode(QAbstractItemView.MultiSelection) class SmallQPushButton(QPushButton): def __init__(self, text): QPushButton.__init__(self) self.setText(text) buttons_size_policy = QSizePolicy( QSizePolicy.Fixed, QSizePolicy.Fixed) self.setSizePolicy(buttons_size_policy) self.setMaximumSize(QSize(30, 30))