Python: displaying Dict of Dicts using the user interface tree for keys and any other widget for values

I have three dicts, one of which contains a list of all available options, and two provide a subset of options (one for defaults and one for custom options). I get three dicts using python built in JSON parser.

I want to display in the user interface a tree on the left based on the keys in dicts, on the right I would like to display either a combobox, button, list, or some other appropriate widget to manipulate the data for this key. I need a tree, since I really work with the recorder, and I want to enable folding.

So far, I have studied the tkinter, tkinter ttk, and tix libraries, and they allow trees, but do not allow lists to be configured on the right. I also saw some examples where the tree is borrowed from python IDLE.

  • Is there a GUI toolkit that provides such functionality, or is there no such thing, and I need to create my own?
  • If I need to design my own, is there any GUI toolkit that you would recommend on tk?
  • Is there a basic GUI design tutorial for the recommended toolkit if it doesn't provide this kind of thing?

I would prefer the cross-platform GUI toolkit (* nix and win) to be available for distribution, if possible. Of interest there is a tutorial on creating custom widgets with tk, I tried to search, but I continue to use the tk widget instead of the widget design: s

As a minimal example, I now abandoned the extra dicts and have the following:

import json import tkinter as tk from tkinter import ttk from pprint import pprint as pprint def JSONTree(Tree, Parent, Dictionery, TagList = []): for key in Dictionery : if isinstance(Dictionery[key],dict): Tree.insert(Parent, 'end', key, text = key) TagList.append(key) JSONTree(Tree, key, Dictionery[key], TagList) pprint(TagList) elif isinstance(Dictionery[key],list): Tree.insert(Parent, 'end', key, text = key) # Still working on this else : Tree.insert(Parent, 'end', key, text = key, value = Dictionery[key]) if __name__ == "__main__" : # Setup the root UI root = tk.Tk() root.title("JSON editor") root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) # Setup Data Data = {"firstName": "John", "lastName": "Smith", "gender": "man", "age": 32, "address": {"streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021"}, "phoneNumbers": [{ "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" }]} # Setup the Frames TreeFrame = ttk.Frame(root, padding = "3") TreeFrame.grid(row = 0, column = 0, sticky = tk.NSEW) # Setup the Tree tree = ttk.Treeview(TreeFrame, columns = ('Values')) tree.column('Values', width = 100, anchor = 'center') tree.heading('Values', text = 'Values') JSONTree(tree, '', Data) tree.pack(fill=tk.BOTH, expand = 1) # Limit windows minimum dimensions root.update_idletasks() root.minsize(root.winfo_reqwidth(),root.winfo_reqheight()) root.mainloop() 
+1
source share
2 answers

OK, so it’s not very pretty, and I’m not very good at creating such code in the production process, but it really works. To make it more reasonable and quality, I would probably make a JSONTree class with all this code wrapped in methods. This would simplify and clear the code and pass objects to event handlers a little less.

 import json import tkinter as tk from tkinter import ttk from pprint import pprint as pprint # opt_name: (from_, to, increment) IntOptions = { 'age': (1.0, 200.0, 1.0), } def close_ed(parent, edwin): parent.focus_set() edwin.destroy() def set_cell(edwin, w, tvar): value = tvar.get() w.item(w.focus(), values=(value,)) close_ed(w, edwin) def edit_cell(e): w = e.widget if w and len(w.item(w.focus(), 'values')) > 0: edwin = tk.Toplevel(e.widget) edwin.protocol("WM_DELETE_WINDOW", lambda: close_ed(w, edwin)) edwin.grab_set() edwin.overrideredirect(1) opt_name = w.focus() (x, y, width, height) = w.bbox(opt_name, 'Values') edwin.geometry('%dx%d+%d+%d' % (width, height, w.winfo_rootx() + x, w.winfo_rooty() + y)) value = w.item(opt_name, 'values')[0] tvar = tk.StringVar() tvar.set(str(value)) ed = None if opt_name in IntOptions: constraints = IntOptions[opt_name] ed = tk.Spinbox(edwin, from_=constraints[0], to=constraints[1], increment=constraints[2], textvariable=tvar) else: ed = tk.Entry(edwin, textvariable=tvar) if ed: ed.config(background='LightYellow') #ed.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.W, tk.E)) ed.pack() ed.focus_set() edwin.bind('<Return>', lambda e: set_cell(edwin, w, tvar)) edwin.bind('<Escape>', lambda e: close_ed(w, edwin)) def JSONTree(Tree, Parent, Dictionery, TagList=[]): for key in Dictionery : if isinstance(Dictionery[key], dict): Tree.insert(Parent, 'end', key, text=key) TagList.append(key) JSONTree(Tree, key, Dictionery[key], TagList) pprint(TagList) elif isinstance(Dictionery[key], list): Tree.insert(Parent, 'end', key, text=key) # Still working on this else: Tree.insert(Parent, 'end', key, text=key, value=Dictionery[key]) if __name__ == "__main__" : # Setup the root UI root = tk.Tk() root.title("JSON editor") root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) # Setup Data Data = { "firstName": "John", "lastName": "Smith", "gender": "man", "age": 32, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021"}, "phoneNumbers": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" }, ]} # Setup the Frames TreeFrame = ttk.Frame(root, padding="3") TreeFrame.grid(row=0, column=0, sticky=tk.NSEW) # Setup the Tree tree = ttk.Treeview(TreeFrame, columns=('Values')) tree.column('Values', width=100, anchor='center') tree.heading('Values', text='Values') tree.bind('<Double-1>', edit_cell) tree.bind('<Return>', edit_cell) JSONTree(tree, '', Data) tree.pack(fill=tk.BOTH, expand=1) # Limit windows minimum dimensions root.update_idletasks() root.minsize(root.winfo_reqwidth(), root.winfo_reqheight()) root.mainloop() 
+3
source

I modified the answer of John Gaines Jr. for list processing. I did not need to edit or taglist for what I am doing, so I deleted them. They could be added back. Since lists can introduce duplicate keys, I replaced the keys with a UUID, still showing the original key as text on the left side of the tree.

 import json import uuid import Tkinter as tk import ttk from pprint import pprint as pprint def JSONTree(Tree, Parent, Dictionary): for key in Dictionary : uid = uuid.uuid4() if isinstance(Dictionary[key], dict): Tree.insert(Parent, 'end', uid, text=key) JSONTree(Tree, uid, Dictionary[key]) elif isinstance(Dictionary[key], list): Tree.insert(Parent, 'end', uid, text=key + '[]') JSONTree(Tree, uid, dict([(i, x) for i, x in enumerate(Dictionary[key])])) else: value = Dictionary[key] if isinstance(value, str) or isinstance(value, unicode): value = value.replace(' ', '_') Tree.insert(Parent, 'end', uid, text=key, value=value) if __name__ == "__main__" : # Setup the root UI root = tk.Tk() root.title("JSON editor") root.columnconfigure(0, weight=1) root.rowconfigure(0, weight=1) # Setup Data Data = { "firstName": "John", "lastName": "Smith", "gender": "male", "age": 32, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021"}, "phoneNumbers": [ {"type": "home", "number": "212 555-1234" }, {"type": "fax", "number": "646 555-4567", "alphabet": [ "abc", "def", "ghi"] } ]} # Setup the Frames TreeFrame = ttk.Frame(root, padding="3") TreeFrame.grid(row=0, column=0, sticky=tk.NSEW) # Setup the Tree tree = ttk.Treeview(TreeFrame, columns=('Values')) tree.column('Values', width=100, anchor='center') tree.heading('Values', text='Values') JSONTree(tree, '', Data) tree.pack(fill=tk.BOTH, expand=1) # Limit windows minimum dimensions root.update_idletasks() root.minsize(root.winfo_reqwidth(), root.winfo_reqheight()) root.mainloop() 
+2
source

All Articles