Source code for test_plugin.QCheckableComboBox

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QColor, QPalette, QStandardItem
from PyQt5.QtWidgets import QGraphicsDropShadowEffect, QGridLayout, QComboBox, QStyledItemDelegate, qApp


[docs]class QCheckableComboBox(QComboBox): # Subclass Delegate to increase item height
[docs] class Delegate(QStyledItemDelegate):
[docs] def sizeHint(self, option, index): size = super().sizeHint(option, index) size.setHeight(20) return size
def __init__(self, parent=None): if parent is None: super().__init__() else: super().__init__(parent=parent) # 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(QCheckableComboBox.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)
[docs] def resizeEvent(self, event): # Recompute text to elide as needed self.updateText() super().resizeEvent(event)
[docs] def eventFilter(self, obj, event): if obj == self.lineEdit(): if event.type() == QEvent.MouseButtonRelease: if self.closeOnLineEditClick: self.hidePopup() else: self.showPopup() return True return False if obj == self.view().viewport(): if event.type() == QEvent.MouseButtonRelease: index = self.view().indexAt(event.pos()) item = self.model().item(index.row()) if item.checkState() == Qt.Checked: item.setCheckState(Qt.Unchecked) if item == self.model().item(0): # deselect all items if item check is all for i in range(1, self.model().rowCount()): item = self.model().item(i) item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) if item == self.model().item(0): # deselect all items if item check is all for i in range(1, self.model().rowCount()): item = self.model().item(i) item.setCheckState(Qt.Checked) return True return False
[docs] def showPopup(self): super().showPopup() # When the popup is displayed, a click on the lineedit should close it self.closeOnLineEditClick = True
[docs] 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()
[docs] def timerEvent(self, event): # After timeout, kill timer, and reenable click on line edit self.killTimer(event.timerId()) self.closeOnLineEditClick = False
[docs] def updateText(self): texts = [] for i in range(1, self.model().rowCount()): if self.model().item(i).checkState() == Qt.Checked: texts.append(self.model().item(i).text()) text = ", ".join(texts) self.lineEdit().setText(text)
# # Compute elided text (with "...") # metrics = QFontMetrics(self.lineEdit().font()) # elidedText = metrics.elidedText(text, Qt.ElideRight, self.lineEdit().width()) # self.lineEdit().setText(elidedText)
[docs] 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)
[docs] 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)
[docs] def currentData(self, role=None): # Return the list of selected items data res = [] for i in range(self.model().rowCount()): if self.model().item(i).checkState() == Qt.Checked: res.append(self.model().item(i).data()) return res