可以将JSON树状展开,列表也会按照顺序列出。暂时没做添加新键的功能。
发不了图,我直接把源代码贴上来吧
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import json
class JSONEditor:
def __init__(self, root):
self.root = root
self.root.title("JSON Editor")
self.current_file = None
self.data = None
# 创建菜单栏
self.menu_bar = tk.Menu(root)
self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
self.file_menu.add_command(label="打开", command=self.open_file)
self.file_menu.add_command(label="保存", command=self.save_file)
self.file_menu.add_command(label="另存为", command=self.save_as_file)
self.menu_bar.add_cascade(label="文件", menu=self.file_menu)
self.root.config(menu=self.menu_bar)
# 创建Treeview和滚动条
self.tree_frame = ttk.Frame(root)
self.tree = ttk.Treeview(self.tree_frame, columns=("value"), show="tree")
self.tree.heading("#0", text="键", anchor="w")
self.tree.heading("value", text="值", anchor="w")
self.vsb = ttk.Scrollbar(self.tree_frame, orient="vertical", command=self.tree.yview)
self.hsb = ttk.Scrollbar(self.tree_frame, orient="horizontal", command=self.tree.xview)
self.tree.configure(yscrollcommand=self.vsb.set, xscrollcommand=self.hsb.set)
self.tree.grid(row=0, column=0, sticky="nsew")
self.vsb.grid(row=0, column=1, sticky="ns")
self.hsb.grid(row=1, column=0, sticky="ew")
self.tree_frame.pack(fill="both", expand=True)
# 创建编辑区域
self.edit_frame = ttk.Frame(root)
self.value_label = ttk.Label(self.edit_frame, text="值:")
self.value_entry = ttk.Entry(self.edit_frame, width=50)
self.modify_btn = ttk.Button(self.edit_frame, text="修改", command=self.modify_value)
self.value_label.pack(side="left", padx=5)
self.value_entry.pack(side="left", fill="x", expand=True, padx=5)
self.modify_btn.pack(side="left", padx=5)
self.edit_frame.pack(fill="x", padx=5, pady=5)
# 绑定事件
self.tree.bind("<<TreeviewSelect>>", self.on_tree_select)
def open_file(self):
file_path = filedialog.askopenfilename(filetypes=[("JSON文件", "*.json")])
if not file_path:
return
try:
with open(file_path, "r", encoding="utf-8") as f:
self.data = json.load(f)
self.current_file = file_path
self.refresh_tree()
except Exception as e:
messagebox.showerror("错误", f"文件读取失败: {str(e)}")
def save_file(self):
if not self.current_file:
self.save_as_file()
return
try:
with open(self.current_file, "w", encoding="utf-8") as f:
json.dump(self.data, f, indent=2, ensure_ascii=False)
messagebox.showinfo("保存成功", "文件已成功保存!")
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
def save_as_file(self):
file_path = filedialog.asksaveasfilename(
defaultextension=".json",
filetypes=[("JSON文件", "*.json")]
)
if not file_path:
return
self.current_file = file_path
self.save_file()
def refresh_tree(self):
self.tree.delete(*self.tree.get_children())
if isinstance(self.data, dict):
self._add_tree_node("", self.data)
elif isinstance(self.data, list):
self._add_tree_node("", self.data)
def _add_tree_node(self, parent, data):
if isinstance(data, dict):
for key, value in data.items():
node = self.tree.insert(parent, "end", text=str(key), values=("" if isinstance(value, (dict, list)) else str(value)))
self._add_tree_node(node, value)
elif isinstance(data, list):
for idx, value in enumerate(data):
node = self.tree.insert(parent, "end", text=f"[{idx}]", values=("" if isinstance(value, (dict, list)) else str(value)))
self._add_tree_node(node, value)
def on_tree_select(self, event):
selected = self.tree.selection()
if not selected:
return
item = selected[0]
path = self._get_item_path(item)
try:
value = self._get_value_from_path(path)
# print(selected)
if isinstance(value, (dict, list)):
self.value_entry.config(state="disabled")
self.value_entry.delete(0, "end")
self.value_entry.insert(0, "[复合类型] 不可直接编辑")
else:
self.value_entry.config(state="normal")
self.value_entry.delete(0, "end")
self.value_entry.insert(0, json.dumps(value, ensure_ascii=False))
except:
self.value_entry.config(state="disabled")
self.value_entry.delete(0, "end")
def _get_item_path(self, item):
path = []
while item:
print(str(item),end=',')
text = self.tree.item(item, "text")
path.append(text)
item = self.tree.parent(item)
print(str(path))
return list(reversed(path))[0:] # 排除根节点
def _get_value_from_path(self, path):
current = self.data
for part in path:
if part.startswith("[") and part.endswith("]"):
index = int(part[1:-1])
current = current[index]
else:
current = current[part]
return current
def modify_value(self):
selected = self.tree.selection()
if not selected:
return
item = selected[0]
path = self._get_item_path(item)
try:
current_value = self._get_value_from_path(path)
if isinstance(current_value, (dict, list)):
messagebox.showwarning("警告", "不能直接编辑复合类型")
return
new_value = json.loads(self.value_entry.get())
self._set_value_at_path(path, new_value)
self.refresh_tree()
except json.JSONDecodeError:
messagebox.showerror("错误", "请输入有效的JSON格式数据")
except Exception as e:
messagebox.showerror("错误", f"更新失败: {str(e)}")
def _set_value_at_path(self, path, value):
current = self.data
for part in path[:-1]:
if part.startswith("[") and part.endswith("]"):
current = current[int(part[1:-1])]
else:
current = current[part]
last_part = path[-1]
if last_part.startswith("[") and last_part.endswith("]"):
current[int(last_part[1:-1])] = value
else:
current[last_part] = value
if __name__ == "__main__":
root = tk.Tk()
app = JSONEditor(root)
root.mainloop()