Im trying to create a list of checkboxes, where the RunAllTest is checked by default. when I check any other multi-select checkbox, the RunAllTest will be unchecked and when i check RunAllTest, all other multi-select checkbox should get unchecked.
Challenge: checking RunAllTest only works when i click on the checkbox not on the label checking on other items only works when i click on the label not on the checkbox
I tried using using pressed which works on both checkbox and text label but it ignores the logic
please help me solve this behavior, either all item should respond to clicking/pressing on checkbox or on the label
code
import sys
from PyQt6.QtCore import Qt, QThreadPool
from PyQt6.QtGui import QStandardItemModel, QStandardItem
from PyQt6.QtWidgets import (
QApplication,
QComboBox,
QLabel,
QMainWindow,
QMessageBox,
QVBoxLayout,
QAbstractItemView,
QListView,
QWidget,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.threadpool = QThreadPool.globalInstance()
self.crawl = None
self.current_browser = None
self.driver = None
self.show_error_popup = True
self.error_popup_response = None
self.setWindowTitle("APP_NAME")
self.layout = QVBoxLayout()
self.ui_catg_names = QComboBox()
self.ui_catg_names.setModel(QStandardItemModel(self))
self.ui_catg_names.setView(QListView(self))
self.ui_catg_names.view().setSelectionMode(
QAbstractItemView.SelectionMode.MultiSelection
)
# self.ui_catg_names.view().pressed.connect(self.handle_item_pressed_or_clicked)
self.ui_catg_names.view().clicked.connect(self.handle_item_pressed_or_clicked)
self.ui_catg_names.setEditable(True)
self.ui_catg_names.lineEdit().setReadOnly(True)
self.ui_catg_names.lineEdit().setPlaceholderText("Select Categories")
self.layout.addWidget(QLabel("Select Test Categories:"))
self.layout.addWidget(self.ui_catg_names)
catgnames = self.get_catg_names()
self.set_catg_names(["Run All Tests"] + sorted(catgnames))
# Assuming "Run All Tests" is the first item
run_all_item = self.ui_catg_names.model().item(0)
if run_all_item and run_all_item.text() == "Run All Tests":
run_all_item.setCheckState(Qt.CheckState.Checked)
# Create a central widget and set the layout
central_widget = QWidget()
central_widget.setLayout(self.layout)
self.setCentralWidget(central_widget)
def handle_item_pressed_or_clicked(self, index):
item = self.ui_catg_names.model().itemFromIndex(index)
if not item:
return
# Toggle check state for "Run All Tests"
if item.text() == "Run All Tests":
if item.checkState() == Qt.CheckState.Checked:
self.select_all_items(False)
item.setCheckState(
Qt.CheckState.Checked
) # Keep "Run All Tests" checked
else:
item.setCheckState(Qt.CheckState.Unchecked)
else:
# Toggle check state for individual items
if item.checkState() == Qt.CheckState.Checked:
item.setCheckState(Qt.CheckState.Unchecked)
else:
item.setCheckState(Qt.CheckState.Checked)
# Uncheck "Run All Tests" if any other item is checked
run_all_item = self.ui_catg_names.model().item(0)
if run_all_item and run_all_item.text() == "Run All Tests":
run_all_item.setCheckState(Qt.CheckState.Unchecked)
self.update_text()
def select_all_items(self, check):
for index in range(1, self.ui_catg_names.model().rowCount()):
item = self.ui_catg_names.model().item(index)
item.setCheckState(
Qt.CheckState.Checked if check else Qt.CheckState.Unchecked
)
def update_text(self):
selected_items = []
for index in range(self.ui_catg_names.model().rowCount()):
item = self.ui_catg_names.model().item(index)
if item.checkState() == Qt.CheckState.Checked:
selected_items.append(item.text())
self.ui_catg_names.lineEdit().setText(", ".join(selected_items))
def add_items(self, items):
for item_text in items:
item = QStandardItem(item_text)
item.setFlags(Qt.ItemFlag.ItemIsUserCheckable | Qt.ItemFlag.ItemIsEnabled)
item.setData(Qt.CheckState.Unchecked, Qt.ItemDataRole.CheckStateRole)
self.ui_catg_names.model().appendRow(item)
def get_selected_items(self):
selected_items = []
for index in range(self.ui_catg_names.model().rowCount()):
item = self.ui_catg_names.model().item(index)
if item.checkState() == Qt.CheckState.Checked:
item_text = item.text()
if item_text != "Run All Tests":
selected_items.append(item_text)
return selected_items
def on_crawl_error(self, error_info):
global POPUP_RESPONSE
url, e = error_info
POPUP_RESPONSE = QMessageBox.critical(
self,
repr(e),
f"Error while crawling {url}\n(Ignore will prevent this popup for the rest of the run)",
buttons=QMessageBox.StandardButton.Retry
| QMessageBox.StandardButton.Ignore
| QMessageBox.StandardButton.Abort,
)
print("COMPLETE on_crawl_error() with", POPUP_RESPONSE)
def stop_test(self):
if "process" in locals():
self.process.stop()
del self.process
self.exit_app()
def exit_app(self):
if self.driver:
self.driver.quit()
sys.exit()
def closeEvent(self, event):
self.stop_test()
event.accept()
def get_catg_names(self):
return ["test1", "test2", "test3"]
def set_catg_names(self, catg_names):
self.ui_catg_names.clear()
self.add_items(catg_names)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())
using python v3.11 and pyqt6 v6.8.1
I found out few key points,
1. clicking on checkbox emits clicked signal and change the checkbox status ie checked
2. clicking on item text emits clicked pressed signal but doesnt change the checkbox status.
to over come this i used
self.ui_catg_names.model().dataChanged.connect(self.handle_data_changed)
But there is an issue with this code, since we change the checkbox status explicitly the datachanged func calls the handle_data_changed everytime. So to overcome this i habe to block the signal, change the checkbox status then unblock the signal.
def handle_item_clicked(self, index):
item = self.ui_catg_names.model().itemFromIndex(index)
if not item:
return
self.ui_catg_names.model().blockSignals(
True
) # Block signals to prevent recursion
if item.text() == "Run_All_Tests1":
# If Run_All_Tests1 is checked, uncheck all others
if item.checkState() == Qt.CheckState.Checked:
self.select_all_items(False) # Uncheck all other items
item.setCheckState(Qt.CheckState.Checked) # Keep Run_All_Tests1 checked
else:
item.setCheckState(Qt.CheckState.Unchecked)
else:
# Toggle the check state of the clicked item
if item.checkState() == Qt.CheckState.Checked:
item.setCheckState(Qt.CheckState.Unchecked)
else:
item.setCheckState(Qt.CheckState.Checked)
# Uncheck Run_All_Tests1 if any other item is checked
run_all_item = self.ui_catg_names.model().item(0)
if run_all_item and run_all_item.text() == "Run_All_Tests1":
run_all_item.setCheckState(Qt.CheckState.Unchecked)
self.update_text()
self.ui_catg_names.model().blockSignals(False) # Unblock signals after changes