Initial commit

This commit is contained in:
Mimikko-zeus
2026-01-07 00:17:46 +08:00
commit 4b3286f546
49 changed files with 2492 additions and 0 deletions

524
ui/task_guide_view.py Normal file
View File

@@ -0,0 +1,524 @@
"""
任务引导视图组件
执行任务的引导式 UI - 支持文件拖拽和 Markdown 渲染
"""
import tkinter as tk
from tkinter import scrolledtext, messagebox
from tkinter import ttk
from typing import Callable, Optional, List
from pathlib import Path
import shutil
import re
class MarkdownText(tk.Text):
"""支持简单 Markdown 渲染的 Text 组件"""
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self._setup_tags()
def _setup_tags(self):
"""设置 Markdown 样式标签"""
# 标题样式
self.tag_configure('h1', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#ffd54f', spacing1=10, spacing3=5)
self.tag_configure('h2', font=('Microsoft YaHei UI', 12, 'bold'), foreground='#81c784', spacing1=8, spacing3=4)
self.tag_configure('h3', font=('Microsoft YaHei UI', 11, 'bold'), foreground='#4fc3f7', spacing1=6, spacing3=3)
# 列表样式
self.tag_configure('bullet', foreground='#ce93d8', lmargin1=20, lmargin2=35)
self.tag_configure('numbered', foreground='#ce93d8', lmargin1=20, lmargin2=35)
# 代码样式
self.tag_configure('code', font=('Consolas', 10), background='#3c3c3c', foreground='#f8f8f2')
# 粗体和斜体
self.tag_configure('bold', font=('Microsoft YaHei UI', 10, 'bold'))
self.tag_configure('italic', font=('Microsoft YaHei UI', 10, 'italic'))
# 普通文本
self.tag_configure('normal', font=('Microsoft YaHei UI', 10), foreground='#d4d4d4')
def set_markdown(self, text: str):
"""设置 Markdown 内容并渲染"""
self.config(state=tk.NORMAL)
self.delete(1.0, tk.END)
lines = text.split('\n')
for line in lines:
self._render_line(line)
self.config(state=tk.DISABLED)
def _render_line(self, line: str):
"""渲染单行 Markdown"""
stripped = line.strip()
# 标题
if stripped.startswith('### '):
self.insert(tk.END, stripped[4:] + '\n', 'h3')
elif stripped.startswith('## '):
self.insert(tk.END, stripped[3:] + '\n', 'h2')
elif stripped.startswith('# '):
self.insert(tk.END, stripped[2:] + '\n', 'h1')
# 无序列表
elif stripped.startswith('- ') or stripped.startswith('* '):
self.insert(tk.END, '' + stripped[2:] + '\n', 'bullet')
# 有序列表
elif re.match(r'^\d+\.\s', stripped):
match = re.match(r'^(\d+\.)\s(.*)$', stripped)
if match:
self.insert(tk.END, ' ' + match.group(1) + ' ' + match.group(2) + '\n', 'numbered')
# 普通文本
else:
# 处理行内格式
self._render_inline(line + '\n')
def _render_inline(self, text: str):
"""渲染行内 Markdown粗体、斜体、代码"""
# 简化处理:直接插入普通文本
# 完整实现需要更复杂的解析
self.insert(tk.END, text, 'normal')
class DropZone(tk.Frame):
"""文件拖拽区域"""
def __init__(
self,
parent,
title: str,
target_dir: Path,
is_input: bool = True,
**kwargs
):
super().__init__(parent, **kwargs)
self.target_dir = target_dir
self.is_input = is_input
self.configure(bg='#2d2d2d', relief=tk.GROOVE, bd=2)
# 确保目录存在
self.target_dir.mkdir(parents=True, exist_ok=True)
self._create_widgets(title)
self._setup_drag_drop()
self._refresh_file_list()
def _create_widgets(self, title: str):
"""创建组件"""
# 标题
title_frame = tk.Frame(self, bg='#2d2d2d')
title_frame.pack(fill=tk.X, padx=5, pady=5)
tk.Label(
title_frame,
text=title,
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#4fc3f7' if self.is_input else '#81c784',
bg='#2d2d2d'
).pack(side=tk.LEFT)
# 打开文件夹按钮
open_btn = tk.Button(
title_frame,
text="📂",
font=('Microsoft YaHei UI', 10),
bg='#424242',
fg='white',
relief=tk.FLAT,
cursor='hand2',
command=self._open_folder
)
open_btn.pack(side=tk.RIGHT)
# 刷新按钮
refresh_btn = tk.Button(
title_frame,
text="🔄",
font=('Microsoft YaHei UI', 10),
bg='#424242',
fg='white',
relief=tk.FLAT,
cursor='hand2',
command=self._refresh_file_list
)
refresh_btn.pack(side=tk.RIGHT, padx=(0, 5))
# 拖拽提示区域
self.drop_label = tk.Label(
self,
text="将文件拖拽到此处\n或点击 📂 打开文件夹",
font=('Microsoft YaHei UI', 10),
fg='#888888',
bg='#3c3c3c',
relief=tk.SUNKEN,
padx=20,
pady=15
)
self.drop_label.pack(fill=tk.X, padx=5, pady=5)
# 文件列表
self.file_listbox = tk.Listbox(
self,
font=('Microsoft YaHei UI', 9),
bg='#2d2d2d',
fg='#d4d4d4',
selectbackground='#0078d4',
relief=tk.FLAT,
height=4
)
self.file_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 文件计数
self.count_label = tk.Label(
self,
text="0 个文件",
font=('Microsoft YaHei UI', 9),
fg='#888888',
bg='#2d2d2d'
)
self.count_label.pack(pady=(0, 5))
def _setup_drag_drop(self):
"""设置拖拽功能Windows 需要 windnd 库,这里用简化方案)"""
# 由于 Tkinter 原生不支持文件拖拽,使用点击打开文件夹的方式
self.drop_label.bind('<Button-1>', lambda e: self._open_folder())
def _open_folder(self):
"""打开目标文件夹"""
import os
os.startfile(str(self.target_dir))
def _refresh_file_list(self):
"""刷新文件列表"""
self.file_listbox.delete(0, tk.END)
files = list(self.target_dir.glob('*'))
files = [f for f in files if f.is_file()]
for f in files:
self.file_listbox.insert(tk.END, f.name)
self.count_label.config(text=f"{len(files)} 个文件")
def get_files(self) -> List[Path]:
"""获取目录中的文件列表"""
files = list(self.target_dir.glob('*'))
return [f for f in files if f.is_file()]
def clear_files(self):
"""清空目录中的文件"""
for f in self.target_dir.glob('*'):
if f.is_file():
f.unlink()
self._refresh_file_list()
class TaskGuideView:
"""
任务引导视图
小白引导式界面,包含:
- 意图识别结果
- 文件拖拽区域(输入/输出)
- 执行计划展示Markdown 渲染)
- 风险提示
- 执行按钮
"""
def __init__(
self,
parent: tk.Widget,
on_execute: Callable[[], None],
on_cancel: Callable[[], None],
workspace_path: Optional[Path] = None
):
self.parent = parent
self.on_execute = on_execute
self.on_cancel = on_cancel
if workspace_path:
self.workspace = workspace_path
else:
self.workspace = Path(__file__).parent.parent / "workspace"
self.input_dir = self.workspace / "input"
self.output_dir = self.workspace / "output"
self._create_widgets()
def _create_widgets(self):
"""创建 UI 组件"""
# 主框架
self.frame = tk.Frame(self.parent, bg='#1e1e1e')
# 标题
title_label = tk.Label(
self.frame,
text="执行任务确认",
font=('Microsoft YaHei UI', 16, 'bold'),
fg='#ffd54f',
bg='#1e1e1e'
)
title_label.pack(pady=(10, 15))
# 上半部分:文件区域
file_section = tk.Frame(self.frame, bg='#1e1e1e')
file_section.pack(fill=tk.X, padx=10, pady=5)
# 输入文件区域
input_frame = tk.LabelFrame(
file_section,
text=" 📥 输入文件 ",
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#4fc3f7',
bg='#1e1e1e',
relief=tk.GROOVE
)
input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
self.input_zone = DropZone(
input_frame,
title="待处理文件",
target_dir=self.input_dir,
is_input=True,
bg='#2d2d2d'
)
self.input_zone.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 箭头
arrow_frame = tk.Frame(file_section, bg='#1e1e1e')
arrow_frame.pack(side=tk.LEFT, padx=10)
tk.Label(
arrow_frame,
text="➡️",
font=('Microsoft YaHei UI', 20),
fg='#ffd54f',
bg='#1e1e1e'
).pack(pady=30)
# 输出文件区域
output_frame = tk.LabelFrame(
file_section,
text=" 📤 输出文件 ",
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#81c784',
bg='#1e1e1e',
relief=tk.GROOVE
)
output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
self.output_zone = DropZone(
output_frame,
title="处理结果",
target_dir=self.output_dir,
is_input=False,
bg='#2d2d2d'
)
self.output_zone.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 意图识别结果区域
self._create_intent_section()
# 执行计划区域Markdown
self._create_plan_section()
# 风险提示区域
self._create_risk_section()
# 按钮区域
self._create_button_section()
def _create_intent_section(self):
"""创建意图识别结果区域"""
section = tk.LabelFrame(
self.frame,
text=" 🎯 意图识别 ",
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#81c784',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.X, padx=10, pady=5)
self.intent_label = tk.Label(
section,
text="",
font=('Microsoft YaHei UI', 10),
fg='#d4d4d4',
bg='#1e1e1e',
wraplength=650,
justify=tk.LEFT
)
self.intent_label.pack(padx=10, pady=8, anchor=tk.W)
def _create_plan_section(self):
"""创建执行计划区域(支持 Markdown"""
section = tk.LabelFrame(
self.frame,
text=" 📄 执行计划 ",
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#ce93d8',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
# 使用 Markdown 渲染的 Text
self.plan_text = MarkdownText(
section,
wrap=tk.WORD,
bg='#2d2d2d',
fg='#d4d4d4',
relief=tk.FLAT,
height=8,
padx=10,
pady=10
)
# 添加滚动条
scrollbar = ttk.Scrollbar(section, orient=tk.VERTICAL, command=self.plan_text.yview)
self.plan_text.configure(yscrollcommand=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.plan_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
def _create_risk_section(self):
"""创建风险提示区域"""
section = tk.LabelFrame(
self.frame,
text=" ⚠️ 安全提示 ",
font=('Microsoft YaHei UI', 11, 'bold'),
fg='#ffb74d',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.X, padx=10, pady=5)
self.risk_label = tk.Label(
section,
text="• 所有操作仅在 workspace 目录内进行\n• 原始文件不会被修改或删除\n• 执行代码已通过安全检查",
font=('Microsoft YaHei UI', 10),
fg='#d4d4d4',
bg='#1e1e1e',
justify=tk.LEFT
)
self.risk_label.pack(padx=10, pady=8, anchor=tk.W)
def _create_button_section(self):
"""创建按钮区域"""
button_frame = tk.Frame(self.frame, bg='#1e1e1e')
button_frame.pack(fill=tk.X, padx=10, pady=15)
# 刷新文件列表按钮
self.refresh_btn = tk.Button(
button_frame,
text="🔄 刷新文件",
font=('Microsoft YaHei UI', 10),
bg='#424242',
fg='white',
activebackground='#616161',
activeforeground='white',
relief=tk.FLAT,
padx=15,
pady=5,
cursor='hand2',
command=self._refresh_all
)
self.refresh_btn.pack(side=tk.LEFT, padx=(0, 10))
# 取消按钮
self.cancel_btn = tk.Button(
button_frame,
text="取消",
font=('Microsoft YaHei UI', 11),
bg='#616161',
fg='white',
activebackground='#757575',
activeforeground='white',
relief=tk.FLAT,
padx=20,
pady=5,
cursor='hand2',
command=self.on_cancel
)
self.cancel_btn.pack(side=tk.RIGHT, padx=(10, 0))
# 执行按钮
self.execute_btn = tk.Button(
button_frame,
text="🚀 开始执行",
font=('Microsoft YaHei UI', 12, 'bold'),
bg='#4caf50',
fg='white',
activebackground='#66bb6a',
activeforeground='white',
relief=tk.FLAT,
padx=30,
pady=8,
cursor='hand2',
command=self._on_execute_clicked
)
self.execute_btn.pack(side=tk.RIGHT)
def _refresh_all(self):
"""刷新所有文件列表"""
self.input_zone._refresh_file_list()
self.output_zone._refresh_file_list()
def _on_execute_clicked(self):
"""执行按钮点击"""
# 刷新文件列表
self.input_zone._refresh_file_list()
# 检查 input 目录是否有文件
files = self.input_zone.get_files()
if not files:
result = messagebox.askyesno(
"确认执行",
"输入文件夹为空,确定要继续执行吗?",
icon='warning'
)
if not result:
return
self.on_execute()
def set_intent_result(self, reason: str, confidence: float):
"""设置意图识别结果"""
self.intent_label.config(
text=f"识别结果: 执行任务 (置信度: {confidence:.0%})\n原因: {reason}"
)
def set_execution_plan(self, plan: str):
"""设置执行计划Markdown 格式)"""
self.plan_text.set_markdown(plan)
def set_risk_info(self, info: str):
"""设置风险提示"""
self.risk_label.config(text=info)
def set_buttons_enabled(self, enabled: bool):
"""设置按钮是否可用"""
state = tk.NORMAL if enabled else tk.DISABLED
self.execute_btn.config(state=state)
self.cancel_btn.config(state=state)
def refresh_output(self):
"""刷新输出文件列表"""
self.output_zone._refresh_file_list()
def show(self):
"""显示视图"""
self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self._refresh_all()
def hide(self):
"""隐藏视图"""
self.frame.pack_forget()
def get_frame(self) -> tk.Frame:
"""获取主框架"""
return self.frame