QTreeWidget to Mirror python Dictionary

10,468

Solution 1

This is a straightforward implementation:

def fill_item(item, value):
  item.setExpanded(True)
  if type(value) is dict:
    for key, val in sorted(value.iteritems()):
      child = QTreeWidgetItem()
      child.setText(0, unicode(key))
      item.addChild(child)
      fill_item(child, val)
  elif type(value) is list:
    for val in value:
      child = QTreeWidgetItem()
      item.addChild(child)
      if type(val) is dict:      
        child.setText(0, '[dict]')
        fill_item(child, val)
      elif type(val) is list:
        child.setText(0, '[list]')
        fill_item(child, val)
      else:
        child.setText(0, unicode(val))              
      child.setExpanded(True)
  else:
    child = QTreeWidgetItem()
    child.setText(0, unicode(value))
    item.addChild(child)

def fill_widget(widget, value):
  widget.clear()
  fill_item(widget.invisibleRootItem(), value)

I added list support just in case anyone needs it.

Usage:

d = { 'key1': 'value1', 
  'key2': 'value2',
  'key3': [1,2,3, { 1: 3, 7 : 9}],
  'key4': object(),
  'key5': { 'another key1' : 'another value1',
            'another key2' : 'another value2'} }

widget = QTreeWidget()
fill_widget(widget, d)
widget.show()

Result:

screenshot

Solution 2

Just because I recently needed this implementation for Python3 and PyQt5 here is a slightly shorter (and complete) port of the given example:

from PyQt5.QtWidgets import  QApplication, QTreeWidget, QTreeWidgetItem

class ViewTree(QTreeWidget):
    def __init__(self, value):
        super().__init__()
        def fill_item(item, value):
            def new_item(parent, text, val=None):
                child = QTreeWidgetItem([text])
                fill_item(child, val)
                parent.addChild(child)
                child.setExpanded(True)
            if value is None: return
            elif isinstance(value, dict):
                for key, val in sorted(value.items()):
                    new_item(item, str(key), val)
            elif isinstance(value, (list, tuple)):
                for val in value:
                    text = (str(val) if not isinstance(val, (dict, list, tuple))
                            else '[%s]' % type(val).__name__)
                    new_item(item, text, val) 
            else:
                new_item(item, str(value))

        fill_item(self.invisibleRootItem(), value)

if __name__ == '__main__':
    app = QApplication([])
    window = ViewTree({ 'key1': 'value1', 'key3': [1,2,3, { 1: 3, 7 : 9}]})
    window.show()
    app.exec_()
Share:
10,468
chase
Author by

chase

Updated on August 05, 2022

Comments

  • chase
    chase almost 2 years

    Is there a way to make a QTreeWidget mirror the changes made to an internal data structure such as dictionary? It seems like they would have created this functionality within the api, because there are many programs which may interact with QTreeWidgets from multiple GUI areas, but the main purpose required of the QTreeWidget is to show a data structure at any point in time. The documentation for QtGui items is not that simple for me to grasp as it usually refers to C documentation, and I'm not certain how it transfers to python.

    So essentially what I would like is the simplest manner to make a QTreeWidget show a nested dictionary, where the top level corresponds to the keys and the sub level corresponds to the values. Also, if the values are dictionaries, use the keys in that level and make sub levels for the values, etc.

    Is this easily doable? I have not been able to find anything to do simple mirroring of data structres like this yet.

  • chase
    chase over 10 years
    This is absolutely Great! Thank you so much. This is awesome for calling each time a button is used to call some function which alters the dictionary. I ended up calling treeWidget.clear() before fill_item and treeWidget.show() in every function call from buttons which change the data structure.
  • Pavel Strakhov
    Pavel Strakhov over 10 years
    I added fill_widget function in my code for convenience.
  • Reactionic
    Reactionic almost 2 years
    This one behaves slightly different than the given example. It adds additional child item for the value of a non-container variable which is in a container. For example 1, 2, 3 in [1,2,3, { 1: 3, 7 : 9}] should not have a child. To fix it, the content of the loop for val in value: can be replaced by the code below. if isinstance(val, (str, int, float)): text = str(val) new_item(item, text) else: text = '[%s]' % type(val).__name__ new_item(item, text, val)
  • frans
    frans almost 2 years
    I've adapted your fix, thanks for the hint!