ListMultiselectWidget.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. """
  2. ***************************************************************************
  3. ListMultiSelectWidget.py
  4. ---------------------
  5. Date : June 2016
  6. Copyright : (C) 2016 by Marco Bernasocchi
  7. Email : marco at opengis.ch
  8. ***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************
  16. """
  17. __author__ = 'Marco Bernasocchi'
  18. __date__ = 'June 2016'
  19. __copyright__ = '(C) 2016, Marco Bernasocchi'
  20. from qgis.PyQt.QtWidgets import (QGroupBox,
  21. QPushButton,
  22. QSizePolicy,
  23. QLabel,
  24. QHBoxLayout,
  25. QVBoxLayout,
  26. QListWidget,
  27. QAbstractItemView)
  28. from qgis.PyQt.QtGui import QFont
  29. from qgis.PyQt.QtCore import Qt, QSize, pyqtSignal
  30. class ListMultiSelectWidget(QGroupBox):
  31. """Widget to show two parallel lists and move elements between the two
  32. usage from code:
  33. self.myWidget = ListMultiSelectWidget(title='myTitle')
  34. self.myLayout.insertWidget(1, self.myWidget)
  35. usage from designer:
  36. insert a QGroupBox in your UI file
  37. optionally give a title to the QGroupBox
  38. promote it to ListMultiSelectWidget
  39. """
  40. selection_changed = pyqtSignal()
  41. def __init__(self, parent=None, title=None):
  42. QGroupBox.__init__(self)
  43. self.setTitle(title)
  44. self.selected_widget = None
  45. self.deselected_widget = None
  46. self._setupUI()
  47. # connect actions
  48. self.select_all_btn.clicked.connect(self._select_all)
  49. self.deselect_all_btn.clicked.connect(self._deselect_all)
  50. self.select_btn.clicked.connect(self._select)
  51. self.deselect_btn.clicked.connect(self._deselect)
  52. self.deselected_widget.itemDoubleClicked.connect(self._select)
  53. self.selected_widget.itemDoubleClicked.connect(self._deselect)
  54. def get_selected_items(self):
  55. """
  56. :return list with all the selected items text
  57. """
  58. return self._get_items(self.selected_widget)
  59. def get_deselected_items(self):
  60. """
  61. :return list with all the deselected items text
  62. """
  63. return self._get_items(self.deselected_widget)
  64. def add_selected_items(self, items):
  65. """
  66. :param items list of strings to be added in the selected list
  67. """
  68. self._add_items(self.selected_widget, items)
  69. def add_deselected_items(self, items):
  70. """
  71. :param items list of strings to be added in the deselected list
  72. """
  73. self._add_items(self.deselected_widget, items)
  74. def set_selected_items(self, items):
  75. """
  76. :param items list of strings to be set as the selected list
  77. """
  78. self._set_items(self.selected_widget, items)
  79. def set_deselected_items(self, items):
  80. """
  81. :param items list of strings to be set as the deselected list
  82. """
  83. self._set_items(self.deselected_widget, items)
  84. def clear(self):
  85. """
  86. removes all items from selected and deselected
  87. """
  88. self.set_selected_items([])
  89. self.set_deselected_items([])
  90. def addItem(self, item):
  91. """
  92. This is for Processing
  93. :param item: string to be added in the deselected list
  94. """
  95. self.add_deselected_items([item])
  96. def addItems(self, items):
  97. """
  98. This is for Processing
  99. :param items: list of strings to be added in the deselected list
  100. """
  101. self.add_deselected_items(items)
  102. def _get_items(self, widget):
  103. for i in range(widget.count()):
  104. yield widget.item(i).text()
  105. def _set_items(self, widget, items):
  106. widget.clear()
  107. self._add_items(widget, items)
  108. def _add_items(self, widget, items):
  109. widget.addItems(items)
  110. def _select_all(self):
  111. self.deselected_widget.selectAll()
  112. self._do_move(self.deselected_widget, self.selected_widget)
  113. def _deselect_all(self):
  114. self.selected_widget.selectAll()
  115. self._do_move(self.selected_widget, self.deselected_widget)
  116. def _select(self):
  117. self._do_move(self.deselected_widget, self.selected_widget)
  118. def _deselect(self):
  119. self._do_move(self.selected_widget, self.deselected_widget)
  120. def _do_move(self, fromList, toList):
  121. for item in fromList.selectedItems():
  122. prev_from_item = fromList.item(fromList.row(item) - 1)
  123. toList.addItem(fromList.takeItem(fromList.row(item)))
  124. fromList.scrollToItem(prev_from_item)
  125. self.selection_changed.emit()
  126. def _setupUI(self):
  127. self.setSizePolicy(
  128. QSizePolicy.Preferred, QSizePolicy.Preferred)
  129. self.setMinimumHeight(180)
  130. self.main_horizontal_layout = QHBoxLayout(self)
  131. italic_font = QFont()
  132. italic_font.setItalic(True)
  133. # deselected widget
  134. self.deselected_widget = QListWidget(self)
  135. self._set_list_widget_defaults(self.deselected_widget)
  136. deselected_label = QLabel()
  137. deselected_label.setText('Deselected')
  138. deselected_label.setAlignment(Qt.AlignCenter)
  139. deselected_label.setFont(italic_font)
  140. deselected_v_layout = QVBoxLayout()
  141. deselected_v_layout.addWidget(deselected_label)
  142. deselected_v_layout.addWidget(self.deselected_widget)
  143. # selected widget
  144. self.selected_widget = QListWidget(self)
  145. self._set_list_widget_defaults(self.selected_widget)
  146. selected_label = QLabel()
  147. selected_label.setText('Selected')
  148. selected_label.setAlignment(Qt.AlignCenter)
  149. selected_label.setFont(italic_font)
  150. selected_v_layout = QVBoxLayout()
  151. selected_v_layout.addWidget(selected_label)
  152. selected_v_layout.addWidget(self.selected_widget)
  153. # buttons
  154. self.buttons_vertical_layout = QVBoxLayout()
  155. self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1)
  156. self.select_all_btn = SmallQPushButton('>>')
  157. self.deselect_all_btn = SmallQPushButton('<<')
  158. self.select_btn = SmallQPushButton('>')
  159. self.deselect_btn = SmallQPushButton('<')
  160. self.select_btn.setToolTip('Add the selected items')
  161. self.deselect_btn.setToolTip('Remove the selected items')
  162. self.select_all_btn.setToolTip('Add all')
  163. self.deselect_all_btn.setToolTip('Remove all')
  164. # add buttons
  165. spacer_label = QLabel() # pragmatic way to create a spacer with
  166. # the same height of the labels on top
  167. # of the lists, in order to align the
  168. # buttons with the lists.
  169. self.buttons_vertical_layout.addWidget(spacer_label)
  170. self.buttons_vertical_layout.addWidget(self.select_btn)
  171. self.buttons_vertical_layout.addWidget(self.deselect_btn)
  172. self.buttons_vertical_layout.addWidget(self.select_all_btn)
  173. self.buttons_vertical_layout.addWidget(self.deselect_all_btn)
  174. # add sub widgets
  175. self.main_horizontal_layout.addLayout(deselected_v_layout)
  176. self.main_horizontal_layout.addLayout(self.buttons_vertical_layout)
  177. self.main_horizontal_layout.addLayout(selected_v_layout)
  178. def _set_list_widget_defaults(self, widget):
  179. widget.setAlternatingRowColors(True)
  180. widget.setSortingEnabled(True)
  181. widget.setDragEnabled(True)
  182. widget.setDragDropMode(QAbstractItemView.DragDrop)
  183. widget.setDragDropOverwriteMode(False)
  184. widget.setDefaultDropAction(Qt.MoveAction)
  185. widget.setSelectionMode(QAbstractItemView.MultiSelection)
  186. class SmallQPushButton(QPushButton):
  187. def __init__(self, text):
  188. QPushButton.__init__(self)
  189. self.setText(text)
  190. buttons_size_policy = QSizePolicy(
  191. QSizePolicy.Fixed, QSizePolicy.Fixed)
  192. self.setSizePolicy(buttons_size_policy)
  193. self.setMaximumSize(QSize(30, 30))