-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCheckableComboBox.py
More file actions
166 lines (135 loc) · 5.44 KB
/
CheckableComboBox.py
File metadata and controls
166 lines (135 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
""" Checkable ComboBox implementation taken from:
https://gis.stackexchange.com/questions/350148/qcombobox-multiple-selection-pyqt5
and modified to work with pyqt6 """
from PyQt6.QtCore import (
Qt,
QEvent,
)
from PyQt6.QtWidgets import (
QComboBox,
QStyledItemDelegate,
QApplication,
QPushButton,
)
from PyQt6.QtGui import (
QStandardItem,
QFontMetrics,
QPalette,
QMouseEvent,
)
class CheckableComboBox(QComboBox):
# Subclass Delegate to increase item height
class Delegate(QStyledItemDelegate):
def sizeHint(self, option, index):
size = super().sizeHint(option, index)
size.setHeight(20)
return size
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Make the combo editable to set a custom text, but readonly
self.setEditable(True)
self.lineEdit().setReadOnly(True)
# Make the lineedit the same color as QPushButton
# palette = QApplication.palette(QPushButton())
# palette.setBrush(QPalette.base(), palette.button())
# self.lineEdit().setPalette(palette)
self.lineEdit().setPalette(QApplication.palette(QPushButton()))
# Use custom delegate
self.setItemDelegate(CheckableComboBox.Delegate())
# Update the text when an item is toggled
self.model().dataChanged.connect(self.updateText)
# Hide and show popup when clicking the line edit
self.lineEdit().installEventFilter(self)
self.closeOnLineEditClick = False
# Prevent popup from closing when clicking on an item
self.view().viewport().installEventFilter(self)
def resizeEvent(self, event):
# Recompute text to elide as needed
self.updateText()
super().resizeEvent(event)
def eventFilter(self, object, event):
if object == self.lineEdit():
if event.type() == QEvent.Type.MouseButtonRelease:
if self.closeOnLineEditClick:
self.hidePopup()
else:
self.showPopup()
return True
return False
if object == self.view().viewport():
if event.type() == QEvent.Type.MouseButtonRelease:
index = self.view().indexAt(event.pos())
item = self.model().item(index.row())
if item.checkState() == Qt.CheckState.Checked:
item.setCheckState(Qt.CheckState.Unchecked)
else:
item.setCheckState(Qt.CheckState.Checked)
return True
return False
def toggleItem(self, index, enable=True):
""" Enable the item at index index """
if index < self.count():
item = self.model().item(index)
if enable:
item.setCheckState(Qt.CheckState.Checked)
else:
item.setCheckState(Qt.CheckState.Unchecked)
def toggleItems(self, indices, enable=True):
for index in indices:
self.toggleItem(index, enable)
def showPopup(self):
super().showPopup()
# When the popup is displayed, a click on the lineedit should close it
self.closeOnLineEditClick = True
def hidePopup(self):
super().hidePopup()
# Used to prevent immediate reopening when clicking on the lineEdit
self.startTimer(100)
# Refresh the display text when closing
self.updateText()
def timerEvent(self, event):
# After timeout, kill timer, and reenable click on line edit
self.killTimer(event.timerId())
self.closeOnLineEditClick = False
def updateText(self):
texts = []
for i in range(self.model().rowCount()):
if self.model().item(i).checkState() == Qt.CheckState.Checked:
texts.append(self.model().item(i).text())
text = ", ".join(texts)
# Compute elided text (with "...")
metrics = QFontMetrics(self.lineEdit().font())
elidedText = metrics.elidedText(text, Qt.TextElideMode.ElideRight, self.lineEdit().width())
self.lineEdit().setText(elidedText)
def addItem(self, text, data=None, checked=False):
item = QStandardItem()
item.setText(text)
if data is None:
item.setData(text)
else:
item.setData(data)
# item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
# item.setData(Qt.Unchecked, Qt.CheckStateRole)
# self.model().appendRow(item)
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
if checked:
item.setData(Qt.CheckState.Checked, Qt.ItemDataRole.CheckStateRole)
else:
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
#self.addItem(text, data)
self.model().appendRow(item)
# self.model().insertRow(item)
def addItems(self, texts, datalist=None):
for i, text in enumerate(texts):
try:
data = datalist[i]
except (TypeError, IndexError):
data = None
self.addItem(text, data)
def currentData(self):
# Return the list of selected items data
res = []
for i in range(self.model().rowCount()):
if self.model().item(i).checkState() == Qt.CheckState.Checked:
res.append(self.model().item(i).data())
return res