I draw a tree widget and set up a list with many items in the widget.
The items are all checked by default. What I want is whenever a higher level item is unchecked then all children under the item should be disabled. My program successfully finds the items which should be disabled but the function (setDisabled) doesn't work. I tried the function (setHidden) but it's also not working.
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_()
And the UI code is here:
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_())
I read that answer(PyQt Tree Widget, adding check boxes for dynamic removal) before I ask. My question is different because as I described above whenever I unchecked the parent item I want to disable the children it has. I did really use parent.setCheckState(0, Qt.Unchecked), but whenever I find out which parent item is unchecked and try to disable the children, my program always crashed. I don't know why. So that's why I post my code to here and ask for help. Can someone help me out? Thank you!