递归json可视化
import tkinter as tk
from tkinter import simpledialog, filedialog, messagebox
import json
class TreeEditor:
def init(self, root):
self.root = root
self.root.title("Tree Editor")
# Canvas for drawing
self.canvas = tk.Canvas(root, bg="white", width=800, height=600)
self.canvas.pack(fill=tk.BOTH, expand=True)
# Menus
self.menu = tk.Menu(root)
root.config(menu=self.menu)
self.file_menu = tk.Menu(self.menu, tearoff=0)
self.file_menu.add_command(label="Open JSON", command=self.load_json)
self.file_menu.add_command(label="Save JSON", command=self.save_json)
self.menu.add_cascade(label="File", menu=self.file_menu)
# Data structures
self.nodes = {} # node_id -> {"data": node_data, "oval": canvas_id, "text": canvas_id}
self.current_node_id = 0
self.connections = [] # (start_node_id, end_node_id)
# Event bindings
self.canvas.bind("<Double-Button-1>", self.add_node)
self.canvas.bind("<Button-3>", self.delete_node)
def add_node(self, event):
"""Add a new node."""
x, y = event.x, event.y
key = simpledialog.askstring("Node Key", "Enter key:")
result = simpledialog.askstring("Node Result", "Enter result:")
if key and result:
node_data = {"key": key, "result": result, "next": []}
self.draw_node(node_data, x, y)
def draw_node(self, node_data, x, y):
"""Draw a node and add it to the structure."""
node_id = self.current_node_id
self.current_node_id += 1
# Draw the node
radius = 30
oval = self.canvas.create_oval(x-radius, y-radius, x+radius, y+radius, fill="lightblue")
text = self.canvas.create_text(x, y, text=f"{node_data['key']}\n{node_data['result']}")
# Bind editing to the node
self.canvas.tag_bind(oval, "<Button-1>", lambda e, nid=node_id: self.edit_node(nid))
# Save the node
self.nodes[node_id] = {"data": node_data, "oval": oval, "text": text}
def delete_node(self, event):
"""Delete a node and its connections."""
x, y = event.x, event.y
node_id = self.get_node_at_position(x, y)
if node_id is not None:
# Remove from canvas
self.canvas.delete(self.nodes[node_id]["oval"])
self.canvas.delete(self.nodes[node_id]["text"])
# Remove connections
self.connections = [(s, e) for s, e in self.connections if s != node_id and e != node_id]
# Remove from data
del self.nodes[node_id]
def edit_node(self, node_id):
"""Edit the node's key and result."""
node = self.nodes[node_id]
key = simpledialog.askstring("Edit Key", "Enter new key:", initialvalue=node["data"]["key"])
result = simpledialog.askstring("Edit Result", "Enter new result:", initialvalue=node["data"]["result"])
if key and result:
node["data"]["key"] = key
node["data"]["result"] = result
self.canvas.itemconfig(node["text"], text=f"{key}\n{result}")
def get_node_at_position(self, x, y):
"""Get the node at the given canvas position."""
for node_id, node in self.nodes.items():
x1, y1, x2, y2 = self.canvas.coords(node["oval"])
if x1 <= x <= x2 and y1 <= y <= y2:
return node_id
return None
def load_json(self):
"""Load JSON data and visualize."""
filepath = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])
if filepath:
with open(filepath, "r") as f:
data = json.load(f)
self.nodes.clear()
self.connections.clear()
self.canvas.delete("all")
self.current_node_id = 0
self.visualize(data, 400, 50, 200)
def visualize(self, data, x, y, x_offset):
"""Recursively visualize JSON data."""
node_id = self.current_node_id
self.current_node_id += 1
# Draw node
self.draw_node(data, x, y)
# Draw children
for child in data.get("next", []):
child_id = self.current_node_id
self.visualize(child, x-x_offset, y+100, x_offset//2)
# Draw connection
start_coords = self.canvas.coords(self.nodes[node_id]["oval"])
end_coords = self.canvas.coords(self.nodes[child_id]["oval"])
start_x, start_y = (start_coords[0] + start_coords[2]) / 2, (start_coords[1] + start_coords[3]) / 2
end_x, end_y = (end_coords[0] + end_coords[2]) / 2, (end_coords[1] + end_coords[3]) / 2
self.connections.append((node_id, child_id))
self.canvas.create_line(start_x, start_y, end_x, end_y, arrow=tk.LAST)
def save_json(self):
"""Save the current nodes and connections to JSON."""
filepath = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])
if filepath:
def build_json(node_id):
node = self.nodes[node_id]["data"]
children = [build_json(child_id) for _, child_id in self.connections if _ == node_id]
node["next"] = children
return node
# Find the root node (no parent)
root_id = next(iter(self.nodes))
data = build_json(root_id)
with open(filepath, "w") as f:
json.dump(data, f, indent=4)
if name == "main":
root = tk.Tk()
app = TreeEditor(root)
root.mainloop()