Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
623 views
in Technique[技术] by (71.8m points)

tkinter - Python : Display a Dict of Dicts using a UI Tree for the keys and any other widget for the values

I have three dicts, one providing a list of all the available options, and two providing a subset of choices (one set for defaults and one for user choices). I get the three dicts using python's built in JSON parser.

I want display, in a UI, a tree on the left that is based on the keys in the dicts, on the right I would like to display either a combobox, a button, a listbox or some other appropriate widget to manipulate the data for that key. I need the tree since I'm really working with a dict of dicts and I want to allow folding.

So far I have looked into tkinter, tkinter's ttk and tix libraries and they allow trees but don't allow configurable lists on the right it seems. I've also seen some examples where the tree is borrowed from python's IDLE.

  1. Is there a GUI toolkit that provides such functionality or is there no such thing and I have to design my own ?
  2. If I have to design my own is there any GUI toolkit that you would recommend over tk ?
  3. Is there a basic tutorial on GUI design for the recommended toolkit if it doens't provide this kind of thing ?

I'd prefer it if the GUI toolkit was cross platform compatible (*nix and win) and free to distribute if possible. Out of interest is there a tutorial on creating custom widgets with tk, I have tried looking but I keep getting directed to tk's widget use instead of widget design :s

As a minimal example I've dropped the extra dicts for now 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()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I've modified John Gaines Jr.'s answer to handle lists. I didn't need editing or the Taglist for what I'm doing so I removed them. They could certainly be added back. Since lists can introduce duplication of keys, I replaced the keys with UUIDs while still showing the original key as text on the left side of the treeview.

UPDATE 07/18/2021 - I've added editing as found in the first answer. Alas my Tk layout skills aren't too impressive.

# https://gist.github.com/wware/a1d90a3ca3cbef31ed3fbb7002fd1318
import json
import uuid
import Tkinter as tk
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.wait_visibility()
        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, x/4, 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, 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')
    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()

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...