2017-02-05 1 views
1

Я рисую виджет дерева и настраиваю список со множеством элементов в виджетах.Как отключить элемент в TreeWidget, запрограммированном в 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), но всякий раз, когда я узнаю, какой родительский элемент не отмечен, и попытайтесь отключить детей, моя программа всегда разбилась. Я не знаю почему. Поэтому я отправляю свой код и прошу помощи. Кто-нибудь может мне помочь? Спасибо!

+0

Возможный дубликат [PyQt Tree Widget, добавление флажков для динамического удаления] (http://stackoverflow.com/questions/31342228/pyqt-tree-widget-adding-check-boxes-for-dynamic-removal) – ekhumoro

+0

Спасибо за информацию. Я прочитал этот ответ, прежде чем спросить. Мой вопрос отличается от того, что, как я описал выше всякий раз, когда я снял флажок с родительского элемента, я хочу отключить его дочерние элементы. Я действительно использовал parent.setCheckState (0, Qt.Unchecked), но всякий раз, когда я узнаю, какой родительский элемент не отмечен, и попытайтесь отключить детей, моя программа всегда разбилась. Я не знаю почему. Поэтому я отправляю свой код и прошу помощи. – Zhentao

ответ

0

Способ, которым вы определили структуру вашего меню, сделал остальную часть вашего кода чрезмерно сложной. Ваши меню представлены в виде дерева, поэтому ваша структура данных должна отражать это.

Вот что должен выглядеть ваш structutre. Для каждого родительского меню есть 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 класс сводится к этому:

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

MENUS = ... 

class window(QMainWindow): 
    def __init__(self, parent=None): 
     super(window, self).__init__(parent) 
     self.ui = Ui_MainWindow() 
     self.ui.setupUi(self) 
     self.loadMenus(MENUS) 
     self.ui.Menutree.itemChanged.connect(self.handleItemChanged) 

    def loadMenus(self, menus): 
     def recurse(parent, items): 
      for text, children in items: 
       item = QTreeWidgetItem(parent) 
       item.setText(0, text) 
       item.setCheckState(0, Qt.Checked) 
       recurse(item, children) 
     recurse(self.ui.Menutree, menus) 
     self.ui.Menutree.expandAll() 

    def handleItemChanged(self, item, column): 
     def recurse(parent, state): 
      for index in range(parent.childCount()): 
       child = parent.child(index) 
       # child.setCheckState(0, state) 
       child.setDisabled(state == Qt.Unchecked) 
       recurse(child, state) 
     recurse(item, item.checkState(0)) 

if __name__ == "__main__": 

    app = QApplication(['']) 
    gui = window() 
    gui.show() 
    app.exec_() 

выбирающем правильная структура данных может действительно повлиять на остальную часть вашего кода!

+0

Большое спасибо. Это то, что я хочу. – Zhentao

+0

Фактически, эти действия в меню будут загружены и отображены на панели инструментов. Поэтому я должен использовать структуру таким образом, как: Menulevel1 = ["Action1", "Action2", "Action3"], потому что будет 3 уровня панели инструментов. И в некоторых местах будет показана четвертая панель инструментов. Я попытаюсь изменить лучшую структуру данных. Спасибо за ваше предложение. – Zhentao

Смежные вопросы