Last active
April 1, 2024 17:11
-
-
Save HorseCheng/e2f4ad26dee478add4b4804c63f66373 to your computer and use it in GitHub Desktop.
CheckableComboBox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PyQt5.QtCore import QEvent, Qt | |
from PyQt5.QtGui import QFontMetrics, QPalette, QStandardItem | |
from PyQt5.QtWidgets import QComboBox, QStyledItemDelegate, qApp | |
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 = qApp.palette() | |
palette.setBrush(QPalette.Base, palette.button()) | |
self.lineEdit().setPalette(palette) | |
# 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.MouseButtonRelease: | |
if self.closeOnLineEditClick: | |
self.hidePopup() | |
else: | |
self.showPopup() | |
return True | |
return False | |
if object == self.view().viewport(): | |
if event.type() == QEvent.MouseButtonRelease: | |
index = self.view().indexAt(event.pos()) | |
item = self.model().item(index.row()) | |
if item.text() in ["Select All"]: | |
checked = item.checkState() == Qt.Checked | |
for i in range(self.model().rowCount()): | |
self.model().item(i).setCheckState(Qt.Checked if not checked else Qt.Unchecked) | |
else: | |
if item.checkState() == Qt.Checked: | |
item.setCheckState(Qt.Unchecked) | |
else: | |
item.setCheckState(Qt.Checked) | |
return True | |
return False | |
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).text()!="Select All": | |
if self.model().item(i).checkState() == Qt.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.ElideRight, self.lineEdit().width()) | |
self.lineEdit().setText(elidedText) | |
def addItem(self, text, data=None): | |
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) | |
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).text()!="Select All": | |
if self.model().item(i).checkState() == Qt.Checked: | |
res.append(self.model().item(i).data()) | |
return res |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from CheckableComboBox import CheckableComboBox | |
from PyQt5 import QtCore, QtGui, QtWidgets | |
class Ui_Form(object): | |
def setupUi(self, Form): | |
Form.setObjectName("Form") | |
Form.resize(400, 600) | |
self.horizontalLayout = QtWidgets.QHBoxLayout(Form) | |
self.horizontalLayout.setObjectName("horizontalLayout") | |
self.frame = CheckableComboBox(Form) | |
self.frame.setObjectName("frame") | |
comunes = ["Select All",'Ameglia', 'Arcola', 'Bagnone'] | |
self.frame.addItems(comunes) | |
self.frame.activated.connect(self.handleActivated) | |
self.horizontalLayout.addWidget(self.frame) | |
self.xx = QtWidgets.QPushButton(Form) | |
self.horizontalLayout.addWidget(self.xx) | |
self.xx.clicked.connect(self.handleActivated) | |
self.retranslateUi(Form) | |
QtCore.QMetaObject.connectSlotsByName(Form) | |
def retranslateUi(self, Form): | |
_translate = QtCore.QCoreApplication.translate | |
Form.setWindowTitle(_translate("Form", "Form")) | |
def handleActivated(self): | |
print(self.frame.currentData()) | |
if __name__ == '__main__': | |
import sys | |
app = QtWidgets.QApplication(sys.argv) | |
widget = QtWidgets.QWidget() | |
widget.show() | |
test = Ui_Form() | |
test.setupUi(widget) | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment