无法在tkinter gui中显示多个(JSON)“键”

时间:2015-01-25 00:57:19

标签: python tkinter

我一直在玩tkinter并使用了大量的网络资源,目前有以下代码:

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 = {'aeroplane': {'Configuration': 'air_travel',
               'Distance': '1000',
               'From': 'miami_airport',
               'Name': 'united',
               'To': 'pasco'},
            }
    # 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()

我使用python IDLE运行它并且它传递并生成一个带有aeroplane的窗口,下拉列表包含该字典的所有细节,它看起来像这样: showing just aeroplane

但如果我将Data更改为:

Data = {'aeroplane': {'Configuration': 'air_travel',
           'Distance': '1000',
           'From': 'miami_airport',
           'Name': 'united',
           'To': 'pasco'},
        'bus': {'Configuration': 'road_travel',
            'Distance': '15',
            'From': 'pasco',
            'Name': 'greyhound',
            'To': 'richland'},
        'car': {'Configuration': 'road_travel',
            'Distance': '160',
            'From': 'Richland',
            'Name': 'honda',
            'To': 'Seattle'},
        'train': {'Configuration': 'train_travel',
            'Distance': '30',
            'From': 'beach',
            'Name': 'gas_train',
            'To': 'miami_airport'}}

并运行此操作会产生错误:

Traceback (most recent call last):
  File "/Users/gour967/Desktop/json_gui_test.py", line 97, in <module>
    JSONTree(tree, '', Data)
  File "/Users/gour967/Desktop/json_gui_test.py", line 53, in JSONTree
    JSONTree(Tree, key, Dictionery[key], TagList)
  File "/Users/gour967/Desktop/json_gui_test.py", line 58, in JSONTree
    Tree.insert(Parent, 'end', key, text=key, value=Dictionery[key])
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/tkinter/ttk.py", line 1348, in insert
    "-id", iid, *opts)
_tkinter.TclError: Item Configuration already exists

如何处理此错误并显示aeroplane, bus, car, train,就像我上面的图片一样?

1 个答案:

答案 0 :(得分:4)

https://docs.python.org/3/library/tkinter.ttk.html#tkinter.ttk.Treeview.insert中记录的insert方法TreeView的签名是:

insert(parent, index, iid=None, **kw)

并且您的JsonTree

递归调用自身
JSONTree(Tree, key, Dictionery[key], TagList)

并将insert(对于“leaf”节点)调用为:

Tree.insert(Parent, 'end', key, text=key, value=Dictionery[key])

然而,有明确记载“如果指定了iid,它将被用作项目标识符; iid必须不存在于树中”(在我上面给出的URL中)。

那么,key是否应该是唯一的?错误消息告诉您它不是。事实上 - 您正在呼叫Tree.insert,例如key等于From(&amp; c)重复 - 因为每个子字典都有一把钥匙!

所以你需要“制作独特”你正在使用的键。最简单的方法是将签名更改为:

def JSONTree(Tree, Parent, Dictionery, TagList=[], prefix=''):

和递归调用:

JSONTree(Tree, key, Dictionery[key], TagList, prefix+key+'.')

并且在您现在只使用insert作为第三个(key)参数的每个iid中,请改用prefix+key,例如:

Tree.insert(Parent, 'end', prefix+key, text=key, value=Dictionery[key])

我之前已经展示的对insert的特定电话。

这样,树中的标识符就是'aeroplane.From''bus.From'等等 - 都是唯一的,所以你会没事的。

如果您实际上使用那些标识符,您也可以省略第三个参数 - 然后Tkinter将代表您在内部生成唯一标识符。但是,控制您自己的标识符可能会使您更容易准确地跟踪用户选择的内容,具体取决于您所追求的内容。所以,任何一种选择都可能是最好的!