Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Iterate through QTreeWidget



  • I have created a QTreeWidget with checkboxes and all that works fine. Now I am trying to iterate through the tree to find out which checkboxes are checked and I am stuck. I have commented out the code in line 49 to 51 which checks the status of the boxes. When I run it I get error "TypeError: 'NoneType' object is not iterable". Any help appreciated. I am a newbie at this so please excuse poor coding.

    from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QApplication, QWidget
    from PyQt5 import QtWidgets
    from PyQt5.Qt import Qt
    import sys

    def tree_checkbox_selected(tw):
    iterator = QtWidgets.QTreeWidgetItemIterator(tw, QtWidgets.QTreeWidgetItemIterator.Checked)
    while iterator.value():
    item = iterator.value()
    print (item.text(0))
    iterator += 1

    if name == 'main':
    app = 0
    if QApplication.instance():
    app = QApplication.instance()
    else:
    app = QApplication(sys.argv)

    l1 = QTreeWidgetItem(["String A", "String B", "String C"])
    l1.setFlags(l1.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
    l2 = QTreeWidgetItem(["String AA", "String BB", "String CC"])
    
    for i in range(3):
        l1_child = QTreeWidgetItem(["Child A" + str(i), "Child B" + str(i), "Child C" + str(i)])
        l1_child.setFlags(l1_child.flags() | Qt.ItemIsUserCheckable)
        l1_child.setCheckState(0, Qt.Unchecked)
        l1.addChild(l1_child)
    
    for j in range(2):
        l2_child = QTreeWidgetItem(["Child AA" + str(j), "Child BB" + str(j), "Child CC" + str(j)])
        l2.addChild(l2_child)
        
    for j in range(2):
        l1_grandchild = QTreeWidgetItem(["GChild XX" + str(j), "GChild YY" + str(j), "GChild ZZ" + str(j)])
        l1_child.addChild(l1_grandchild)
    
    w = QWidget()
    w.resize(510, 210)
    
    tw = QTreeWidget(w)
    tw.resize(500, 200)
    tw.setColumnCount(4)
    tw.setHeaderLabels(["Column 1", "Column 2", "Column 3","Del"])
    tw.addTopLevelItem(l1)
    tw.addTopLevelItem(l2)
    column = 0
    
    for item in tree_checkbox_selected(tw):
        print('State: %s, Text: "%s"' % (
              item.checkState(column), item.text(column)))
    
    w.show()
    sys.exit(app.exec_())


  • @ACollins
    Hi. Please put all of your code in tags, it makes it much easier to read!

    Also, when you tell us in line 49 to 51, it would help if you, say, put a comment into the code indicating where these lines are. I can't tell!

    Having said that. I think the content of your def tree_checkbox_selected(tw): looks OK (and should be print()ing stuff?). Your problem looks to me like where you call it:

    for item in tree_checkbox_selected(tw):
    

    Your def tree_checkbox_selected() function does not return any result, so the error message shows it is treated as returning None; and you cannot iterate through for item in None:.

    You perhaps intend that method to either use yield to return items as it iterates along, or just build up a list of the checked items and return that to the caller?


  • Banned

    Okay I tried to use that QTreeWidgetItemIterator but it turned out to be non-intuitive and problematic and I went through the documentation thoroughly and tested various means of using it to no avail so I went more simplistic and simply built my own iterator that does what I know its supposed to do -- I hope this proves to be useful for what you are trying to achieve. I also moved your checkbox to your Del column as I am assuming that was where you wanted it based on what you appeared to be doing. Further this example also shows you how to design a MUC which can then be used for an MRE if you are having trouble but a MUC is the best way to handle figuring out how a specific thing you are attempting to use works in a structured and clear concise manner.

    from PyQt5.QtCore    import Qt
    #from PyQt5.QtGui     import
    from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
    from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QTreeWidgetItemIterator
    from PyQt5.QtWidgets import QPushButton
    
    
    class MainDisply(QWidget):
        def __init__(self):
            QWidget.__init__(self)
            Top=300; Left=700; Width=510; Hight=210
            self.setGeometry(Left, Top, Width, Hight)
    
            treItem_1 = QTreeWidgetItem(["String A", "String B", "String C"])
            treItem_1.setFlags(treItem_1.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
    
            for i in range(3):
                treItemChild = QTreeWidgetItem(["Child A" + str(i), "Child B" + str(i), "Child C" + str(i)])
                treItemChild.setFlags(treItemChild.flags() | Qt.ItemIsUserCheckable)
                treItemChild.setCheckState(3, Qt.Unchecked)
                treItem_1.addChild(treItemChild)
    
            for j in range(2):
                treItemGrandChild = QTreeWidgetItem(["GChild XX" + str(j), "GChild YY" + str(j), "GChild ZZ" + str(j)])
                treItemChild.addChild(treItemGrandChild)
    
            treItem_2 = QTreeWidgetItem(["String AA", "String BB", "String CC"])
    
            for j in range(2):
                treItemChild = QTreeWidgetItem(["Child AA" + str(j), "Child BB" + str(j), "Child CC" + str(j)])
                treItem_2.addChild(treItemChild)
    
    
            self.treView = QTreeWidget()
            self.treView.setColumnCount(4)
            self.treView.setHeaderLabels(['Column 1', 'Column 2', 'Column 3', 'Del'])
            self.treView.addTopLevelItem(treItem_1)
            self.treView.addTopLevelItem(treItem_2)
    
            HBox1 = QHBoxLayout()
            HBox1.addWidget(self.treView)
    
            self.btnStatus = QPushButton('Status')
            self.btnStatus.clicked.connect(self.Status)
    
            HBox2 = QHBoxLayout()
            HBox2.addStretch(1)
            HBox2.addWidget(self.btnStatus)
    
            VBox = QVBoxLayout()
            VBox.addLayout(HBox1)
            VBox.addLayout(HBox2)
    
            self.setLayout(VBox)
    
        def Status(self):
            print('**** Starting Iterator')
            Root = self.treView.invisibleRootItem()
            ChldCnt = Root.childCount()
    
            self.Iterator(Root, ChldCnt)
            print('**** Finished Iterator')
    
        def Iterator(self, Node, Cnt):
            for idx in range(Cnt):
                Item = Node.child(idx)
                print('******* State: %s, Text: "%s"' % (Item.checkState(3), Item.text(0)))
                ChldCnt = Item.childCount()
                if ChldCnt > 0:
                    self.Iterator(Item, ChldCnt)
        
    
    if __name__ == "__main__":
        MainEventHandler = QApplication([])
    
        MainApplication = MainDisply()
        MainApplication.show()
        
        MainEventHandler.exec()
    

Log in to reply