无法在 tkinter gui 中显示多个 (JSON) "keys"
not able to display multiple (JSON) "keys" in tkinter gui
我一直在玩 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()
I 运行 它使用 python IDLE 并且它传递并生成一个 window 和 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
就像我上面的图像一样?
TreeView
的方法 insert
的签名,如 https://docs.python.org/3/library/tkinter.ttk.html#tkinter.ttk.Treeview.insert 中所述,是:
insert(parent, index, iid=None, **kw)
并且您的 JsonTree
正在使用
递归调用自身
JSONTree(Tree, key, Dictionery[key], TagList)
并将插入(对于 "leaf" 个节点)调用为:
Tree.insert(Parent, 'end', key, text=key, value=Dictionery[key])
然而,清楚地记录了 "If iid is specified, it is used as the item identifier; iid must not already exist in the tree"(在我上面给出的 URL)。
那么,key
是不是应该如此独特?错误消息告诉您它不是。事实上——你正在调用 Tree.insert
,例如 key
等于 From
(&c) repeatedly——因为每个子字典都有这么一把钥匙!
因此您需要 "make unique" 您正在使用的密钥。最简单的可能是将签名更改为:
def JSONTree(Tree, Parent, Dictionery, TagList=[], prefix=''):
和递归调用:
JSONTree(Tree, key, Dictionery[key], TagList, prefix+key+'.')
并且在您现在仅使用 key
作为第三个 (iid
) 参数的每个 insert
中,改用 prefix+key
,例如:
Tree.insert(Parent, 'end', prefix+key, text=key, value=Dictionery[key])
对于我之前展示的 insert
的特定调用。
这样,树中的标识符将是例如 'aeroplane.From'
、'bus.From'
等等——都是唯一的,所以你会没事的。
如果您实际上没有使用 这些标识符,您也可以省略第三个参数——然后 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()
I 运行 它使用 python IDLE 并且它传递并生成一个 window 和 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
就像我上面的图像一样?
TreeView
的方法 insert
的签名,如 https://docs.python.org/3/library/tkinter.ttk.html#tkinter.ttk.Treeview.insert 中所述,是:
insert(parent, index, iid=None, **kw)
并且您的 JsonTree
正在使用
JSONTree(Tree, key, Dictionery[key], TagList)
并将插入(对于 "leaf" 个节点)调用为:
Tree.insert(Parent, 'end', key, text=key, value=Dictionery[key])
然而,清楚地记录了 "If iid is specified, it is used as the item identifier; iid must not already exist in the tree"(在我上面给出的 URL)。
那么,key
是不是应该如此独特?错误消息告诉您它不是。事实上——你正在调用 Tree.insert
,例如 key
等于 From
(&c) repeatedly——因为每个子字典都有这么一把钥匙!
因此您需要 "make unique" 您正在使用的密钥。最简单的可能是将签名更改为:
def JSONTree(Tree, Parent, Dictionery, TagList=[], prefix=''):
和递归调用:
JSONTree(Tree, key, Dictionery[key], TagList, prefix+key+'.')
并且在您现在仅使用 key
作为第三个 (iid
) 参数的每个 insert
中,改用 prefix+key
,例如:
Tree.insert(Parent, 'end', prefix+key, text=key, value=Dictionery[key])
对于我之前展示的 insert
的特定调用。
这样,树中的标识符将是例如 'aeroplane.From'
、'bus.From'
等等——都是唯一的,所以你会没事的。
如果您实际上没有使用 这些标识符,您也可以省略第三个参数——然后 Tkinter 将在内部代表您生成一个唯一标识符。但是控制您自己的标识符可能会让您更容易准确地跟踪用户选择的内容,具体取决于您所追求的内容。所以,任何一个选择都可能是最好的!