如何禁用PYQT5中编程的TreeWidget中的项目

时间:2017-02-05 03:38:54

标签: python-3.x pyqt5

我绘制了一个树窗口小部件,并在窗口小部件中设置了包含许多项目的列表。

enter image description here

默认情况下都会检查这些项目。我想要的是每当取消选中更高级别的项目时,应该禁用该项目下的所有子项。我的程序成功找到应禁用的项目,但函数(setDisabled)不起作用。我尝试了这个函数(setHidden),但它也没有用。

from uitest import Ui_MainWindow
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel)

Menulevel1 = ["Action1", "Action2", "Action3"]
Menulevel2_1 = ["Action4", "Action6", "Action7"]
Menulevel3_1_1 = ["Action8", "Action9", "Action10"]
Menulevel3_1_2 = ["Action11", "Action12", "Action13"]
Menulevel3_1_3 = ["Action14", "Action15", "Action16"]
Menulevel2_2 = ["Action17", "Action18", "Action19"]
Menulevel3_2_1 = ["Action20", "Action21", "Action22"]
Menulevel3_2_2 = ["Action23", "Action24"]
Menulevel3_2_3 = [""]
Menulevel4_2_1_1 = ["Action25", "Action26", "Action27"]
Menulevel4_2_2_1 = ["Action28", "Action29", "Action30"]
Menulevel2_3 = ["Action31", "Action32", "Action33"]
Menulevel3_3_1 = ["Action34", "Action35", "Action36"]
Menulevel3_3_2 = ["Action37", "Action38", "Action39"]
Menulevel3_3_3 = [""]

class window(QMainWindow):
    def __init__(self, parent=None):
        super(window, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.treeload = False
        self.changedact_name = ""  # save the name of actions which will be dropped during trim process
        self.changedact_dir = ""
        self.changedact_pos = []

        menutree = self.ui.Menutree  # get the instance of tree widget
        # check whether the tree has been set up before,
        # if it has, there's no need to set up again (otherwise program will be stop)
        if not self.treeload:
            for i in range(len(Menulevel1)):
                lvl1item = QTreeWidgetItem(menutree)  # get the tree widget instance
                lvl1item.setText(0, Menulevel1[i])  # set the first level text to be actions in menu level 1
                # set up the first level elements to be a three state check box
                # three state: checked, unchecked and partiallychecked, all used in handleItemChanged function
                lvl1item.setFlags(lvl1item.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
                # lvl1item.setDisabled(True)
                lvl2 = eval("Menulevel2_%d" % (i + 1))  # set up a command for adding item of second level
                for j in range(len(lvl2)):  # loop to add menu level 2 action name
                    lvl2item = QTreeWidgetItem(lvl1item)  # set up the parent level
                    # set up the child to be selectable
                    # If the child's checkbox isn't given a state, the checkbox element does not appear
                    lvl2item.setFlags(lvl2item.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
                    lvl2item.setText(0, lvl2[j])  # set up the item text to action name of menu level 2
                    lvl2item.setCheckState(0, Qt.Checked)  # set the item default to checked
                    # set up a command for adding item of third level
                    lvl3 = eval("Menulevel3_%d_%d" % (i + 1, j + 1))
                    for k in range(len(lvl3)):  # loop to add menu level 3 action name
                        if lvl3[k] is not "":  # check whether there is action in menu level 3
                            lvl3item = QTreeWidgetItem(lvl2item)  # set up the parent level
                            lvl3item.setFlags(lvl3item.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
                            lvl3item.setText(0, lvl3[k])  # set up the item text to action name of menu level 3
                            lvl3item.setCheckState(0, Qt.Checked)  # set the item default to checked
                            # check whether the action is one of the first three actions, if it is,
                            # the action cannot be changed
                            try:  # try add menu level 4 action name
                                # set up the command
                                lvl4 = eval("Menulevel4_%d_%d_%d" % (i + 1, j + 1, k + 1))
                                for l in range(len(lvl4)):  # set up the loop
                                    lvl4item = QTreeWidgetItem(lvl3item)  # set up the parent
                                    lvl4item.setFlags(lvl4item.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
                                    lvl4item.setText(0, lvl4[l])
                                    lvl4item.setCheckState(0, Qt.Checked)
                            except:  # if there's no action in menu level 4
                                pass  # continue the rest
            menutree.expandAll()  # expand all items on the tree
            # put the tree load variable to true, whenever get in this section again there's no need to build tree again
            # addition: whenever build the tree if try to build again, the program will
            # stop at lvl1item.setText(0, Menulevel1_EN[i])
            self.treeload = True
            # whenever a checkbox is changed, handle item changed function will be called
            # and the item will be determined
            menutree.itemChanged.connect(self.handleItemChanged)

    # the function to process changed check box in tree widget
    def handleItemChanged(self, item, column):
        if item.checkState(column) == Qt.Unchecked:  # determine whether the checkbox of item is unchecked
            self.changedact_name = item.text(column) + ";"  # save the text of item (action name) in global variable
            self.changedact_dir = "Drop"  # set the change direction variable to drop - drop the action
        # determine whether the checkbox of item is partiallychecked, usually the higher level will be the status
        elif item.checkState(column) == Qt.PartiallyChecked:
            # save the text of item (action name) in global variable, help determine which action is changed later
            self.changedact_name += item.text(column) + ";"
        elif item.checkState(column) == Qt.Checked:  # determine whether the checkbox of item is unchecked
            self.changedact_name += item.text(column) + ";"  # save the text of item (action name) in global variable
            # set the change direction variable to add - add the action which is dropped before
            self.changedact_dir = "Add"

        if item.text(column) in Menulevel1:  # check whether the item reaches the menu level 1 - the top level
            # actindex = Menulevel1_EN.index(item.text(column))
            # call find changed action function to determine the status of which action is changed
            self.findChangedAction()
            self.changedact_name = ""  # clear the variable for saving new changed item
            self.changedact_dir = ""  # clear the variable for saving new changed direction


    def findChangedAction(self):  # function of determining the status of which action is changed
        changedactionlist = self.changedact_name.split(";")  # using ; to separate the variable into list
        changedaction = list(filter(None, changedactionlist))  #
        changedaction.reverse()
        length = len(changedaction)  # determine the length of the list
        # because the last item in the list is empty, the second last item will be the action in menu level 1
        # determine the changed item is in which menu level
        lvl1pos = Menulevel1.index(changedaction[0]) + 1
        lvl2 = lvl3 = lvl4 = ""
        lvl2pos = lvl3pos = lvl4pos = 0
        if length >1:
            lvl2 = eval("Menulevel2_%d" %lvl1pos)
            lvl2pos = lvl2.index(changedaction[1])+1
            try:
                lvl3 = eval("Menulevel3_%d_%d" % (lvl1pos, lvl2pos))
                lvl3pos = lvl3.index(changedaction[2]) + 1
                try:
                    lvl4 = eval("Menulevel4_%d_%d_%d" % (lvl1pos, lvl2pos, lvl3pos))
                    lvl4pos = lvl4.index(changedaction[-1])+1
                except:
                    pass
            except:
                pass
        else:
            lvl2 = ""
            lvl2pos = 0
            lvl3 = ""
            lvl3pos = 0
            lvl4 = ""
            lvl4pos = 0
            lvl2 = eval("Menulevel2_%d" %lvl1pos)

        fullpos = lvl1pos * 1000 + lvl2pos * 100 + lvl3pos * 10 + lvl4pos
        threepos = lvl1pos * 1000 + lvl2pos * 100 + lvl3pos * 10
        twopos = lvl1pos * 1000 + lvl2pos * 100
        onepos = lvl1pos * 1000
        if fullpos == threepos and lvl4 != "":
            for i in lvl4:
                disableitem = self.ui.Menutree.findItems(i, Qt.MatchExactly | Qt.MatchRecursive, 0)
                try:
                    disableitem.setDisabled(True)
                except:
                    pass
        elif fullpos == twopos and lvl3 != "":
            for i in lvl3:
                disableitem = self.ui.Menutree.findItems(i, Qt.MatchExactly | Qt.MatchRecursive, 0)
                try:
                    disableitem.setDisabled(True)
                except:
                    pass


if __name__ == "__main__":
    app = QApplication([])
    gui = window()
    gui.show()
    app.exec_()

UI代码在这里:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(816, 549)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 796, 440))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.Menutree = QtWidgets.QTreeWidget(self.scrollAreaWidgetContents)
        self.Menutree.setObjectName("Menutree")
        self.Menutree.header().setVisible(False)
        self.verticalLayout.addWidget(self.Menutree)
        spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
        self.verticalLayout.addItem(spacerItem)
        self.groupBox = QtWidgets.QGroupBox(self.scrollAreaWidgetContents)
        self.groupBox.setTitle("")
        self.groupBox.setObjectName("groupBox")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox)
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.Previous = QtWidgets.QPushButton(self.groupBox)
        font = QtGui.QFont()
        font.setPointSize(14)
        font.setBold(True)
        font.setWeight(75)
        self.Previous.setFont(font)
        self.Previous.setObjectName("Previous")
        self.horizontalLayout.addWidget(self.Previous)
        spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem2)
        self.Next = QtWidgets.QPushButton(self.groupBox)
        font = QtGui.QFont()
        font.setPointSize(14)
        font.setBold(True)
        font.setWeight(75)
        self.Next.setFont(font)
        self.Next.setObjectName("Next")
        self.horizontalLayout.addWidget(self.Next)
        spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem3)
        self.Next_Save = QtWidgets.QPushButton(self.groupBox)
        font = QtGui.QFont()
        font.setPointSize(14)
        font.setBold(True)
        font.setWeight(75)
        self.Next_Save.setFont(font)
        self.Next_Save.setObjectName("Next_Save")
        self.horizontalLayout.addWidget(self.Next_Save)
        spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem4)
        self.verticalLayout.addWidget(self.groupBox)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.gridLayout.addWidget(self.scrollArea, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 816, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.TBlevel1 = QtWidgets.QToolBar(MainWindow)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.TBlevel1.sizePolicy().hasHeightForWidth())
        self.TBlevel1.setSizePolicy(sizePolicy)
        font = QtGui.QFont()
        font.setPointSize(14)
        self.TBlevel1.setFont(font)
        self.TBlevel1.setObjectName("TBlevel1")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.TBlevel1)
        self.TBlevel2 = QtWidgets.QToolBar(MainWindow)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.TBlevel2.setFont(font)
        self.TBlevel2.setObjectName("TBlevel2")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.TBlevel2)
        MainWindow.insertToolBarBreak(self.TBlevel2)
        self.TBlevel3 = QtWidgets.QToolBar(MainWindow)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.TBlevel3.setFont(font)
        self.TBlevel3.setObjectName("TBlevel3")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.TBlevel3)
        MainWindow.insertToolBarBreak(self.TBlevel3)
        self.TBlevel4 = QtWidgets.QToolBar(MainWindow)
        self.TBlevel4.setObjectName("TBlevel4")
        MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.TBlevel4)
        MainWindow.insertToolBarBreak(self.TBlevel4)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Previous.setText(_translate("MainWindow", "Previous"))
        self.Next.setText(_translate("MainWindow", "Next"))
        self.Next_Save.setText(_translate("MainWindow", "Next and Save"))
        self.TBlevel1.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.TBlevel2.setWindowTitle(_translate("MainWindow", "toolBar"))
        self.TBlevel3.setWindowTitle(_translate("MainWindow", "toolBar_2"))
        self.TBlevel4.setWindowTitle(_translate("MainWindow", "toolBar"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

在我问之前,我读了那个答案(PyQt Tree Widget, adding check boxes for dynamic removal)。我的问题是不同的,因为正如我上面所描述的那样,每当我取消选中父项时,我想要禁用它拥有的子项。我确实使用了parent.setCheckState(0,Qt.Unchecked),但每当我发现未选中哪个父项并尝试禁用子项时,我的程序总是崩溃。我不知道为什么。这就是我将代码发布到此处并寻求帮助的原因。有人可以帮我吗?谢谢!

1 个答案:

答案 0 :(得分:1)

您定义菜单结构的方式使得其余代码非常复杂。您的菜单采用树形式,因此您的数据结构应该反映出来。

以下是您的结构应该是什么样子。对于每个父菜单,都有<div class="formEntryArea"> <div class="formEntryLabel"> <span class="formLabel"><label for=" langdropdown">Would you like to receive library notices in English or Spanish? ><span class="formRequired">*</span></label></span> </div> <div class="formMultiSelect" id=”langdropdown”> <select name=" langdropdown "> <option value="0" selected="selected">Choose language</option> <option value="eng">English</option> <option value="spa">Spanish</option> </select> <input type="text" id="ddepartment" name="ddepartment"> </div>包含其文本及其子项列表。每个项目都以完全相同的方式处理,以便您可以以一致的方式遍历结构:

tuple

有了这个,你的MENUS = [ ("Action1", [ ("Action4", [ ("Action8", []), ("Action9", []), ("Action10", []) ]), ("Action6", [ ("Action11", []), ("Action12", []), ("Action13", []) ]), ("Action7", [ ("Action14", []), ("Action15", []), ("Action16", []) ]), ]), ("Action2", [ ("Action17", [ ("Action20", [ ("Action25", []), ("Action26", []), ("Action27", []) ]), ("Action21", []), ("Action22", []), ]), ("Action18", [ ("Action23", [ ("Action28", []), ("Action29", []), ("Action30", []) ]), ("Action24", []), ]), ("Action19", []), ]), ("Action3", [ ("Action31", [ ("Action34", []), ("Action35", []), ("Action36", []) ]), ("Action32", [ ("Action37", []), ("Action38", []), ("Action39", []) ]), ("Action33", []), ]), ] 课就会减少到这个:

window

选择正确的数据结构确实可以对代码的其余部分产生影响!