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 QTreeWidget
s 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.
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:
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) -> None:
super().__init__()
self.fill_item(self.invisibleRootItem(), value)
@staticmethod
def fill_item(item: QTreeWidgetItem, value) -> None:
if value is None:
return
elif isinstance(value, dict):
for key, val in sorted(value.items()):
ViewTree.new_item(item, str(key), val)
elif isinstance(value, (list, tuple)):
for val in value:
if isinstance(val, (str, int, float)):
ViewTree.new_item(item, str(val))
else:
ViewTree.new_item(item, f"[{type(val).__name__]}", val)
else:
ViewTree.new_item(item, str(value))
@staticmethod
def new_item(parent: QTreeWidgetItem, text:str, val=None) -> None:
child = QTreeWidgetItem([text])
ViewTree.fill_item(child, val)
parent.addChild(child)
child.setExpanded(True)
if __name__ == '__main__':
app = QApplication([])
window = ViewTree({
'key1': 'value1',
'key2': [1, 2, 3, {1: 3, 7: 9}]})
window.show()
app.exec_()
The code isn't a good example for today python coding, though. It's hard to read, poorly type-hintable, and doesn't cover all possible combinations. Please don't use it as-is.
Few days ago i searched such theme, but all i could find - how to fill tree, but not how to extract it. So i write some. Maybe someone still find this useful. Thanks to the prev author, i used his code with some improvements. This will load your collection (it may be list or dict or tuple with many children)
def load_tree(self, d):
self.fill_widget(self.tree, d)
def fill_widget(self, widget, value):
widget.clear()
self.fill_item(widget.invisibleRootItem(), value)
def fill_item(self, item, value):
def new_item(parent, text):
child = QTreeWidgetItem([str(text)])
if text not in ("[dict]", "[list]", "[tuple]"):
child.setFlags(child.flags() | Qt.ItemIsEditable)
parent.addChild(child)
child.setExpanded(True)
return child
if isinstance(value, dict):
new_parent = new_item(item, f"[{value.__class__.__name__}]")
for elem_k, elem_v in value.items():
sub_parent = new_item(new_parent, elem_k)
self.fill_item(sub_parent, elem_v)
elif isinstance(value, (tuple, list)):
new_parent = new_item(item, f"[{value.__class__.__name__}]")
for val in value:
self.fill_item(new_parent, val)
else:
new_item(item, f"{value}")
And extract code a little bit more.. complicated. If someone can make it more fancy - pls do.
def get_dict(self):
result = None
def unpack(to_unpack, key, source=None):
for child_index in range(to_unpack.childCount()):
child = to_unpack.child(child_index)
child_text = child.text(0)
try:
child_text = float(child_text)
except ValueError:
try:
child_text = int(child_text)
except ValueError:
pass
if source is None:
core = result
else:
core = source
if key == "[dict]":
core.update({child_text: None})
if child.childCount() > 0:
unpack(child, child_text, core)
elif key == "[list]" or key == "[tuple]":
if child_text == "[dict]":
core.append({})
elif child_text == "[list]" or child_text == "[tuple]":
core.append([])
else:
core.append(child_text)
if child.childCount() > 0:
unpack(child, child_text, core[child_index])
else:
if child_text == "[dict]":
core.update({key: {}})
elif child_text == "[list]" or child_text == "[tuple]":
core.update({key: []})
else:
core.update({key: child_text})
if child.childCount() > 0:
unpack(child, child_text, core[key])
for index in range(self.tree.topLevelItemCount()):
parent = self.tree.topLevelItem(index)
element_text = parent.text(0)
if element_text == "[dict]":
result = {}
unpack(parent, element_text)
elif element_text == "[list]" or element_text == "[tuple]":
result = []
unpack(parent, element_text)
else:
result = element_text
return result
Where self.tree - QTreeWidget object in your window.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With