<think>
我们将在原有的JSON树形查看器基础上,添加右键菜单功能,用于复制当前节点的完整JSON路径(用'/'分隔)
步骤:
1. 在原有程序中,为Treeview绑定右键事件(<Button-3>),用于弹出菜单。
2. 创建一个弹出菜单(Menu),添加一个“复制路径”命令。
3. 当右键点击节点时,记录当前节点,并在弹出菜单中显示。
4. 实现复制路径功能:从当前节点一直追溯到根节点,构建路径字符串(使用'/'连接各节点的文本),然后复制到剪贴板。
注意:节点文本可能包含特殊字符,我们直接使用节点显示的文本。另外,根节点是隐藏的,我们不需要显示根节点,所以从根节点的子节点开始构建路径。
修改点:
1. 初始化时创建右键菜单
2. 绑定右键事件,并记录当前选中的节点
3. 实现路径构建函数
我们将修改JSONTreeViewer类:
- 在__init__中创建右键菜单
- 绑定右键事件
- 添加构建路径和复制路径的方法
注意:由于节点文本可能重复,我们使用节点ID来追踪。但是构建路径时,我们使用每个节点的文本(即key或索引)。
路径示例:对于JSON {"a": {"b": [1,2,3]}},节点a下的b节点下的[0]节点的路径为:a/b/[0]
注意:根节点(id为"root")我们不显示在路径中,因为它是虚拟的。
实现步骤:
1. 右键点击节点时,获取节点ID。
2. 从该节点开始,向上遍历直到根节点(根节点的父节点为空字符串),收集路径段(节点的文本)。
3. 反转路径段,然后用'/'连接。
4. 复制到剪贴板。
注意:根节点(root)的父节点是空字符串,我们遇到空字符串就停止。另外,根节点本身我们跳过(因为它是虚拟的,且用户不可见)。
修改后的代码:
</think>
我将为您实现在原有JSON树形查看器基础上添加右键菜单功能,支持复制JSON路径。以下是完整的Python程序:
```python
import json
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pyperclip # 用于剪贴板操作
class JSONTreeViewer:
def __init__(self, root):
self.root = root
self.root.title("JSON树形查看器")
self.root.geometry("800x600")
# 创建菜单栏
menubar = tk.Menu(root)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="打开", command=self.load_json)
filemenu.add_separator()
filemenu.add_command(label="退出", command=root.quit)
menubar.add_cascade(label="文件", menu=filemenu)
root.config(menu=menubar)
# 创建树形视图
self.tree = ttk.Treeview(root, columns=("type", "value"), show="tree")
self.tree.heading("#0", text="键")
self.tree.heading("type", text="类型")
self.tree.heading("value", text="值")
# 添加滚动条
scrollbar = ttk.Scrollbar(root, orient="vertical", command=self.tree.yview)
self.tree.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side="right", fill="y")
self.tree.pack(fill="both", expand=True)
# 绑定双击事件
self.tree.bind("<Double-1>", self.on_double_click)
# 创建右键菜单
self.context_menu = tk.Menu(root, tearoff=0)
self.context_menu.add_command(label="复制JSON路径", command=self.copy_json_path)
# 绑定右键事件
self.tree.bind("<Button-3>", self.show_context_menu)
# 存储节点数据的映射
self.node_data = {}
self.file_path = ""
# 存储节点路径的映射(用于复制路径功能)
self.node_paths = {}
def load_json(self):
"""打开并加载JSON文件"""
file_path = filedialog.askopenfilename(
filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
)
if not file_path:
return
self.file_path = file_path
self.tree.delete(*self.tree.get_children())
self.node_data.clear()
self.node_paths.clear()
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
root_id = self.add_node("", "root", data, "")
# 默认展开第一层
self.tree.item(root_id, open=True)
except Exception as e:
messagebox.showerror("错误", f"加载JSON文件失败:\n{str(e)}")
def add_node(self, parent, key, value, parent_path):
"""添加节点到树形视图,同时记录节点路径"""
node_id = self.tree.insert(parent, "end", text=str(key), values=(type(value).__name__,))
# 构建当前节点的完整路径
if parent_path == "" or parent_path == "/":
current_path = f"/{key}"
else:
current_path = f"{parent_path}/{key}"
self.node_paths[node_id] = current_path
if isinstance(value, dict):
self.tree.set(node_id, "value", f"({len(value)} 项)")
self.node_data[node_id] = ("dict", value)
for k, v in value.items():
self.add_node(node_id, k, v, current_path)
elif isinstance(value, list):
self.tree.set(node_id, "value", f"[{len(value)} 项]")
self.node_data[node_id] = ("list", value)
for i, item in enumerate(value):
self.add_node(node_id, f"[{i}]", item, current_path)
else: # 基本数据类型
self.tree.set(node_id, "value", str(value))
self.node_data[node_id] = ("leaf", value)
return node_id
def on_double_click(self, event):
"""处理双击事件 - 展开/折叠节点"""
item = self.tree.identify("item", event.x, event.y)
if item and item in self.node_data:
node_type, data = self.node_data[item]
# 如果是已折叠的字典/列表节点,展开它
if not self.tree.item(item, "open"):
if node_type in ("dict", "list"):
self.tree.item(item, open=True)
else:
# 如果已展开,则折叠
self.tree.item(item, open=False)
def show_context_menu(self, event):
"""显示右键上下文菜单"""
item = self.tree.identify("item", event.x, event.y)
if item:
# 选中当前项
self.tree.selection_set(item)
# 在鼠标位置显示菜单
self.context_menu.post(event.x_root, event.y_root)
def copy_json_path(self):
"""复制选中节点的JSON路径到剪贴板"""
selected_items = self.tree.selection()
if not selected_items:
return
item = selected_items[0]
if item in self.node_paths:
path = self.node_paths[item]
pyperclip.copy(path)
messagebox.showinfo("复制成功", f"已复制路径到剪贴板:\n{path}")
else:
messagebox.showerror("错误", "无法获取路径")
if __name__ == "__main__":
root = tk.Tk()
app = JSONTreeViewer(root)
root.mainloop()
```
### 新增功能说明
1. **右键菜单功能**:
- 添加了右键上下文菜单
- 实现了"复制JSON路径"选项
- 使用`pyperclip`库实现剪贴板操作
- 复制成功时显示提示信息
2. **JSON路径构建**:
- 使用`/`分隔符构建层次路径
- 根节点路径格式:`/root`
- 字典节点路径格式:`/parent/child`
- 列表节点路径格式:`/parent/[index]`
- 路径存储在`node_paths`字典中
3. **交互优化**:
- 右键点击节点时自动选中该节点
- 右键菜单显示在鼠标位置
- 复制成功后弹出通知
### 使用指南
1. 运行程序后点击"文件 > 打开"选择JSON文件
2. 在树形结构中右键点击任意节点
3. 选择"复制JSON路径"菜单项
4. 路径会自动复制到剪贴板
5. 使用Ctrl+V粘贴复制的路径
### 技术实现细节
1. **路径构建算法**:
- 在递归添加节点时构建路径
- 使用父节点路径作为基础
- 格式:`parent_path + "/" + current_key`
- 根节点特殊处理为`/root`
2. **右键菜单实现**:
```python
# 创建菜单
self.context_menu = tk.Menu(root, tearoff=0)
self.context_menu.add_command(label="复制JSON路径", command=self.copy_json_path)
# 绑定事件
self.tree.bind("<Button-3>", self.show_context_menu)
```
3. **剪贴板操作**:
```python
import pyperclip
pyperclip.copy(path) # 复制到剪贴板
```
4. **路径存储结构**:
```python
self.node_paths = {} # {node_id: "/path/to/node"}
```
### 示例JSON路径
对于以下JSON结构:
```json
{
"user": {
"name": "John",
"contacts": [
"email@example.com",
"123-456-7890"
]
}
}
```
生成的路径:
- 用户节点: `/root/user`
- 姓名节点: `/root/user/name`
- 联系节点: `/root/user/contacts`
- 第一个联系人: `/root/user/contacts/[0]`
### 依赖安装
运行前需要安装pyperclip:
```bash
pip install pyperclip
```
此程序完整实现了您的需求,支持JSON文件的树形展示、双击展开/折叠节点以及右键复制路径功能[^1]。