python-3.xpyqtpyside6pyqt6

PySide6 - QTreeWidget - dynamic items built from list


I'm trying to get a QTreeWidget built dynamically from a list like below.

list = ['pear.green', 'banana.yellow', 'banana.green', 'apple.green', 'apple.red']

Here is a mock up I hardcoded to demonstrate what I would like build dynamically.

Like mock up image

    apple_item = QtWidgets.QTreeWidgetItem(self.list)
    apple_item.setText(0, "apple")
    apple_item.setFont(0, self.TOP_LEVEL_TREE_ITEM_FONT)

    apple_green_item = QtWidgets.QTreeWidgetItem(apple_item)
    apple_green_item.setText(0, "green")
    apple_red_item = QtWidgets.QTreeWidgetItem(apple_item)
    apple_red_item.setText(0, "red")

    banana_item = QtWidgets.QTreeWidgetItem(self.list)
    banana_item.setText(0, "banana")
    banana_item.setFont(0, self.TOP_LEVEL_TREE_ITEM_FONT)

    banana_green_item = QtWidgets.QTreeWidgetItem(banana_item)
    banana_green_item.setText(0, "green")
    banana_yellow_item = QtWidgets.QTreeWidgetItem(banana_item)
    banana_yellow_item.setText(0, "yellow")

    pear_item = QtWidgets.QTreeWidgetItem(self.list)
    pear_item.setFont(0, self.TOP_LEVEL_TREE_ITEM_FONT)
    pear_item.setText(0, "pear")

    pear_green_item = QtWidgets.QTreeWidgetItem(pear_item)
    pear_green_item.setText(0, "green")

Solution

  • This comes down to constructing the tree structure from the list items, and then adding them dynamically to the QTreeWidget.

    For this you can first loop the list, and find the . in the string. The part before it is the top category and the part after it is the subcategory. You can store them in a dictionary with the top categories as keys and the subcategories as values.

    Note that in the code below I added a try-except block in case there is no . in the list item, then the full item becomes the top category and the subcategory is empty.

    This leads to the following dictionary:

    {'pear': ['green'], 'banana': ['yellow', 'green'], 'apple': ['green', 'red'], 'peach': []}
    

    Afterwards you can do a second loop over this dictionary. In the code below the loop is over the sorted keys to get them in alphabetical order in the tree widget. Now you create a dynamic item for each top category with (also dynamic) subitems for the list of subcategories (which can be empty, like for peach in the example). Adding the items and subitems is done in the same way as in your mockup, but just using the same variables item and subitem in a loop.

    Full code:

    from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QTreeWidget, QTreeWidgetItem
    
    class TreeWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
            treeWidget = QTreeWidget(self)
            treeWidget.setColumnCount(2)
            treeWidget.setHeaderHidden(True)
            
            mylist = ['pear.green', 'banana.yellow', 'banana.green', 'apple.green', 'apple.red', 'peach']
            categories = dict()
            for item in mylist:
                try:
                    pos = item.index(".")
                    topcat = item[:pos]
                    subcat = [item[pos+1:]]
                except ValueError:
                    topcat = item
                    subcat = []
                if topcat in categories:
                    categories[topcat].extend(subcat)
                else:
                    categories[topcat] = subcat
    
            for topcat in sorted(categories):
                item = QTreeWidgetItem(treeWidget)
                item.setText(0, topcat)
                for subcat in categories[topcat]:
                    subitem = QTreeWidgetItem(item)
                    subitem.setText(0, subcat)
                
            self.setCentralWidget(treeWidget)
    
            
    app = QApplication([])
    mainWin = TreeWindow()
    mainWin.show()
    app.exec()
    

    Result:

    screenshot of dynamically generated tree