I'm trying to implement a code capable to create a checkable combobox with PyQt6. My problem is that when I open the combobox for the first time, it stays opened and never hides. The functions seem to work well but I guess when it shows up for the first time it has a bug that I'm not able to fix. Please someone help me. The code I'm using is:
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QComboBox
from PyQt6.QtCore import Qt, QEvent
from PyQt6.QtGui import QStandardItem
class CheckableComboBox(QComboBox):
def __init__(self, parent=None):
super(CheckableComboBox,self).__init__(parent)
self.setEditable(True)
#self.lineEdit().setReadOnly(True)
self.model().dataChanged.connect(self.updateLineEditField)
def addItems(self, items, itemList=None):
itemList = [] if itemList is None else itemList
for indx, text in enumerate(items):
try:
data = itemList[indx]
except(IndexError):
data = None
self.addItem(text,data)
def addItem(self,text,userData=None):
item = QStandardItem()
item.setText(text)
if not userData is None:
item.setData(userData)
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
self.model().appendRow(item)
def updateLineEditField(self):
text_container = []
for i in range(self.model().rowCount()):
if self.model().item(i).checkState() == Qt.CheckState.Checked:
text_container.append(self.model().item(i).text())
text_string=', '.join(text_container)
self.lineEdit().setText(text_string)
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 1200,800
self.setMinimumSize(self.window_width, self.window_height)
self.setStyleSheet('''
QWidget {
font-size: 10px;
}
''')
self.layout = QVBoxLayout()
self.setLayout(self.layout)
combobox = CheckableComboBox()
combobox.addItems(colors)
self.layout.addWidget(combobox)
def closeEvent(self, event):
event.accept()
if __name__ == '__main__':
colors=["Blue", "Yellow", "Green", "Red"]
app = QApplication(sys.argv)
myApp = MyApp()
myApp.show()
try:
sys.exit(app.exec())
except SystemExit:
print("Closing window...")
I just expect it to hide when it's opened and I click the box. Just for you to know, i'm using a Mac and the python version 3.9.6 64-bit. I don't know if this is significant but just in case. I've tried functions like eventFilter but it doesn't work for me neither.
#def eventFilter(self, widget, event: QEvent) -> bool:
# if widget == self.lineEdit():
# if event.type() == QEvent.Type.MouseButtonPress:
# if self.closeOnLineEditClick:
# print("hide")
# self.hidePopup()
# else:
# print("show")
# self.showPopup()
# return True
# return super().eventFilter(widget, event)
#
# if widget == self.view().viewport():
# if event.type() == QEvent.Type.MouseButtonRelease:
# indx = self.view().indexAt(event.pos())
# item = self.model().item(indx.row())
#
# if item.checkState() == Qt.CheckState.Checked:
# item.setCheckState(Qt.CheckState.Unchecked)
# else:
# item.setCheckState(Qt.CheckState.Checked)
# return True
# return super().eventFilter(widget, event)
As @musicamante asked, here you have the complete code when I use eventFilter() but for me it fixes nothing:
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QComboBox
from PyQt6.QtCore import Qt, QEvent
from PyQt6.QtGui import QStandardItem
class CheckableComboBox(QComboBox):
def __init__(self, parent=None):
super(CheckableComboBox,self).__init__(parent)
self.setEditable(True)
#self.lineEdit().setReadOnly(True)
self.closeOnLineEditClick = False
self.lineEdit().installEventFilter(self)
self.model().dataChanged.connect(self.updateLineEditField)
def eventFilter(self, widget, event: QEvent) -> bool:
if widget == self.lineEdit():
if event.type() == QEvent.Type.MouseButtonPress:
if self.closeOnLineEditClick:
print("hide")
self.hidePopup()
else:
print("show")
self.showPopup()
self.closeOnLineEditClick = True
return True
return super().eventFilter(widget, event)
def addItems(self, items, itemList=None):
itemList = [] if itemList is None else itemList
for indx, text in enumerate(items):
try:
data = itemList[indx]
except(IndexError):
data = None
self.addItem(text,data)
def addItem(self,text,userData=None):
item = QStandardItem()
item.setText(text)
if not userData is None:
item.setData(userData)
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
self.model().appendRow(item)
def updateLineEditField(self):
text_container = []
for i in range(self.model().rowCount()):
if self.model().item(i).checkState() == Qt.CheckState.Checked:
text_container.append(self.model().item(i).text())
text_string=', '.join(text_container)
self.lineEdit().setText(text_string)
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 1200,800
self.setMinimumSize(self.window_width, self.window_height)
self.setStyleSheet('''
QWidget {
font-size: 10px;
}
''')
self.layout = QVBoxLayout()
self.setLayout(self.layout)
combobox = CheckableComboBox()
combobox.addItems(colors)
self.layout.addWidget(combobox)
def closeEvent(self, event):
event.accept()
if __name__ == '__main__':
colors=["Blue", "Yellow", "Green", "Red"]
app = QApplication(sys.argv)
myApp = MyApp()
myApp.show()
try:
sys.exit(app.exec())
except SystemExit:
print("Closing window...")
The code implementation exposed works. It seems the problem is caused by an specific Qt bug to macOS. Didn't find how to fix it. Just answered because the code is correct and you can use it in other operating systems. If someone someday finds out what bug I'm talking about and how to fix it, answer whenever! Thanks.
import sys
from PyQt6.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QPushButton, QComboBox
from PyQt6.QtCore import Qt, QEvent
from PyQt6.QtGui import QStandardItem
class CheckableComboBox(QComboBox):
def __init__(self, parent=None):
super(CheckableComboBox,self).__init__(parent)
self.setEditable(True)
#self.lineEdit().setReadOnly(True)
self.closeOnLineEditClick = False
self.lineEdit().installEventFilter(self)
self.model().dataChanged.connect(self.updateLineEditField)
def eventFilter(self, widget, event: QEvent) -> bool:
if widget == self.lineEdit():
if event.type() == QEvent.Type.MouseButtonPress:
if self.closeOnLineEditClick:
print("hide")
self.hidePopup()
else:
print("show")
self.showPopup()
self.closeOnLineEditClick = True
return True
return super().eventFilter(widget, event)
def addItems(self, items, itemList=None):
itemList = [] if itemList is None else itemList
for indx, text in enumerate(items):
try:
data = itemList[indx]
except(IndexError):
data = None
self.addItem(text,data)
def addItem(self,text,userData=None):
item = QStandardItem()
item.setText(text)
if not userData is None:
item.setData(userData)
item.setFlags(Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsUserCheckable)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
self.model().appendRow(item)
def updateLineEditField(self):
text_container = []
for i in range(self.model().rowCount()):
if self.model().item(i).checkState() == Qt.CheckState.Checked:
text_container.append(self.model().item(i).text())
text_string=', '.join(text_container)
self.lineEdit().setText(text_string)
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.window_width, self.window_height = 1200,800
self.setMinimumSize(self.window_width, self.window_height)
self.setStyleSheet('''
QWidget {
font-size: 10px;
}
''')
self.layout = QVBoxLayout()
self.setLayout(self.layout)
combobox = CheckableComboBox()
combobox.addItems(colors)
self.layout.addWidget(combobox)
def closeEvent(self, event):
event.accept()
if __name__ == '__main__':
colors=["Blue", "Yellow", "Green", "Red"]
app = QApplication(sys.argv)
myApp = MyApp()
myApp.show()
try:
sys.exit(app.exec())
except SystemExit:
print("Closing window...")