Having issues with selecting the checked item

93 Views Asked by At

I simplified the app to be presentable; the scenario here is user clicks on the "Themes" push-button and the Theme window will be opened to choose an option; Delegate class (from QStyledItemDelegate) was defined to uncheck the other options as soon as the editorEvent received the MouseButtonRelease event of the checked item (shown in code below) and eventually when the user pushes the Ok button the style will be set based on the chosen option; Here's my first issue: Checking the checkbox of an item doesn't select that item (index remains unchanged after checking), which means user has to not only check the checkbox but also click on the item to select it. Without clicking (selecting) the item, the index will remain on the parent ("Themes") instead of switching to the checked child and the item won't be highlighted. (I hope I clarified myself)

What I tried was to add the "paint" method to the Delegate class and hopefully by defining the state in QStyle automatically selecting the item as soon as it's checked, however, the item is not highlighted (indication of being selected) and the index will still remain on the parent "Themes"... So, to patch this up, in the "accept" method (Form class) I added:

if model.data(index) == 'Themes':
        index = self.theme.treeWidget.currentIndex().child(0,0)

While the Ok button is pushed the index will be forced to set on the first child, and then getting the parent and iterating over it to find which option was checked (and also setSelecting the current index which now the item will be highlighted)...well, this is not what I'm looking for, I would prefer to see the item is getting highlighted (and selected) as soon it's checked; I believe there has to be a nicer way to Select the Checked item in the Delagate class as soon as the MouseButtonRelease event is received... your guidance here is greatly appreciated; also since you're here now, I also want to keep and save settings so that next time the user is opening the Theme window, the app refers to the previously chosen option and shows it as a checked-item...

from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys, Preferences_test

class Delegate(QtWidgets.QStyledItemDelegate):
    def paint(self, painter, option, index):
        if not index.parent().isValid():
            QStyledItemDelegate.paint(self, painter, option, index)
        else:
            widget = option.widget
            style = widget.style() if widget else QApplication.style()
            opt = QStyleOptionButton()
            opt.rect = option.rect
            opt.text = index.data()
            opt.state |= QStyle.State_On if index.data(Qt.CheckStateRole) else QStyle.State_Off
            style.drawControl(QStyle.CE_CheckBox, opt, painter, widget)

    def editorEvent(self, event, model, option, index):
        value = QtWidgets.QStyledItemDelegate.editorEvent(self, event, model, option, index)        
        if value:
            if event.type() == QtCore.QEvent.MouseButtonRelease:
                if index.data(QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
                    parent = index.parent()
                    for i in range(model.rowCount(parent)):
                        if i != index.row():
                            ix = parent.child(i, 0)
                            model.setData(ix, QtCore.Qt.Unchecked, QtCore.Qt.CheckStateRole)
        return value

class Theme(QtWidgets.QMainWindow, Preferences_test.Ui_Preferences):
    def __init__(self, parent=None):
        super(Theme, self).__init__(parent)
        self.setupUi(self)
        self.treeWidget.setHeaderHidden(True)
        self.treeWidget.setItemDelegate(Delegate())
        self.treeWidget.expandAll()
        
class Form(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout()
        self.pushButton = QtWidgets.QPushButton('Themes')
        layout.addWidget(self.pushButton)
        self.setLayout(layout)
        self.pushButton.clicked.connect(self.theme_call)
        
    def accept(self):
        index = self.theme.treeWidget.currentIndex()
        model = self.theme.treeWidget.model()
        if model.data(index) == 'Themes':
            index = self.theme.treeWidget.currentIndex().child(0,0)
        parent_idx = index.parent()
        for i in range(model.rowCount(parent_idx)):
            idx = parent_idx.child(i, 0)
            if (model.data(idx) == "Fusion") and idx.data(QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
                app.setStyle('Fusion')
                # self.theme.treeWidget.itemFromIndex(idx).setSelected(True)
                return

            elif (model.data(idx) == "Light-Classic") and idx.data(QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
                app.setStyle('Light-Classic')
                # self.theme.treeWidget.itemFromIndex(idx).setSelected(True)
                return

            else:
                pass
        # self.theme.close()

    def reject(self):
        self.theme.close()

    def theme_call(self):
        self.theme = Theme()
        self.theme.show()
        self.theme.buttonBox.accepted.connect(self.accept)
        self.theme.buttonBox.rejected.connect(self.reject)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = Form()
    form.show()
    app.exec_()

And the designer file: [Preferences_test.py]

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Preferences(object):
    def setupUi(self, Preferences):
        Preferences.setObjectName("Preferences")
        Preferences.resize(278, 359)
        self.layoutWidget = QtWidgets.QWidget(Preferences)
        self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 258, 341))
        self.layoutWidget.setObjectName("layoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
        self.gridLayout.setObjectName("gridLayout")
        self.treeWidget = QtWidgets.QTreeWidget(self.layoutWidget)
        self.treeWidget.setHeaderHidden(True)
        self.treeWidget.setObjectName("treeWidget")
        item_0 = QtWidgets.QTreeWidgetItem(self.treeWidget)
        item_1 = QtWidgets.QTreeWidgetItem(item_0)
        item_1.setCheckState(0, QtCore.Qt.Unchecked)
        item_1 = QtWidgets.QTreeWidgetItem(item_0)
        item_1.setCheckState(0, QtCore.Qt.Unchecked)
        self.gridLayout.addWidget(self.treeWidget, 0, 0, 1, 1)
        self.buttonBox = QtWidgets.QDialogButtonBox(self.layoutWidget)
        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox.setObjectName("buttonBox")
        self.gridLayout.addWidget(self.buttonBox, 3, 0, 1, 1)
        self.retranslateUi(Preferences)
        QtCore.QMetaObject.connectSlotsByName(Preferences)

    def retranslateUi(self, Preferences):
        _translate = QtCore.QCoreApplication.translate
        Preferences.setWindowTitle(_translate("Preferences", "Preferences"))
        self.treeWidget.headerItem().setText(0, _translate("Preferences", "1"))
        self.treeWidget.topLevelItem(0).setText(0, _translate("Preferences", "Themes"))
        self.treeWidget.topLevelItem(0).child(0).setText(0, _translate("Preferences", "Light-Classic"))
        self.treeWidget.topLevelItem(0).child(1).setText(0, _translate("Preferences", "Fusion"))
0

There are 0 best solutions below