我绘制了一个树窗口小部件,并在窗口小部件中设置了包含许多项目的列表。
默认情况下都会检查这些项目。我想要的是每当取消选中更高级别的项目时,应该禁用该项目下的所有子项。我的程序成功找到应禁用的项目,但函数(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),但每当我发现未选中哪个父项并尝试禁用子项时,我的程序总是崩溃。我不知道为什么。这就是我将代码发布到此处并寻求帮助的原因。有人可以帮我吗?谢谢!
答案 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
选择正确的数据结构确实可以对代码的其余部分产生影响!