feat: update requirements and enhance task guide UI

- Added core dependencies for file handling (Pillow, openpyxl, python-docx, PyPDF2, chardet) to requirements.txt.
- Modified SandboxRunner to create a dedicated codes directory for task scripts.
- Expanded prompts.py with a list of allowed libraries for code generation.
- Simplified the task guide UI by removing drag-and-drop functionality and enhancing layout and styling for better user experience.
This commit is contained in:
Mimikko-zeus
2026-01-07 00:47:07 +08:00
parent 5fbaa13b38
commit fc11ce8871
12 changed files with 237 additions and 208 deletions

View File

@@ -1,6 +1,6 @@
"""
任务引导视图组件
执行任务的引导式 UI - 支持文件拖拽和 Markdown 渲染
执行任务的引导式 UI - 简化版
"""
import tkinter as tk
@@ -8,7 +8,6 @@ from tkinter import scrolledtext, messagebox
from tkinter import ttk
from typing import Callable, Optional, List
from pathlib import Path
import shutil
import re
@@ -22,20 +21,13 @@ class MarkdownText(tk.Text):
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('h1', font=('Microsoft YaHei UI', 13, 'bold'), foreground='#ffd54f', spacing1=8, spacing3=4)
self.tag_configure('h2', font=('Microsoft YaHei UI', 11, 'bold'), foreground='#81c784', spacing1=6, spacing3=3)
self.tag_configure('h3', font=('Microsoft YaHei UI', 10, 'bold'), foreground='#4fc3f7', spacing1=4, spacing3=2)
# 列表样式
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('bullet', foreground='#ce93d8', lmargin1=15, lmargin2=30)
self.tag_configure('numbered', foreground='#ce93d8', lmargin1=15, lmargin2=30)
# 普通文本
self.tag_configure('normal', font=('Microsoft YaHei UI', 10), foreground='#d4d4d4')
@@ -64,26 +56,19 @@ class MarkdownText(tk.Text):
self.insert(tk.END, stripped[2:] + '\n', 'h1')
# 无序列表
elif stripped.startswith('- ') or stripped.startswith('* '):
self.insert(tk.END, ' ' + stripped[2:] + '\n', 'bullet')
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')
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')
self.insert(tk.END, line + '\n', 'normal')
class DropZone(tk.Frame):
"""文件拖拽区域"""
class FileZone(tk.Frame):
"""简化的文件区域 - 仅显示打开文件夹按钮"""
def __init__(
self,
@@ -96,81 +81,35 @@ class DropZone(tk.Frame):
super().__init__(parent, **kwargs)
self.target_dir = target_dir
self.is_input = is_input
self.configure(bg='#2d2d2d', relief=tk.GROOVE, bd=2)
self.configure(bg='#2d2d2d')
# 确保目录存在
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="📂",
color = '#4fc3f7' if self.is_input else '#81c784'
self.open_btn = tk.Button(
self,
text=f"📂 {title}",
font=('Microsoft YaHei UI', 10),
bg='#424242',
fg='white',
fg=color,
activebackground='#616161',
activeforeground=color,
relief=tk.FLAT,
padx=15,
pady=8,
cursor='hand2',
command=self._open_folder
)
open_btn.pack(side=tk.RIGHT)
self.open_btn.pack(fill=tk.X, padx=5, pady=5)
# 刷新按钮
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 个文件",
@@ -179,52 +118,33 @@ class DropZone(tk.Frame):
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())
self._refresh_count()
def _open_folder(self):
"""打开目标文件夹"""
import os
os.startfile(str(self.target_dir))
def _refresh_file_list(self):
"""刷新文件列表"""
self.file_listbox.delete(0, tk.END)
def _refresh_count(self):
"""刷新文件计数"""
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 refresh(self):
"""刷新"""
self._refresh_count()
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__(
@@ -250,73 +170,72 @@ class TaskGuideView:
def _create_widgets(self):
"""创建 UI 组件"""
# 主框架
# 主框架 - 使用 Canvas 实现滚动
self.frame = tk.Frame(self.parent, bg='#1e1e1e')
# 标题
title_label = tk.Label(
self.frame,
text="执行任务确认",
font=('Microsoft YaHei UI', 16, 'bold'),
font=('Microsoft YaHei UI', 14, 'bold'),
fg='#ffd54f',
bg='#1e1e1e'
)
title_label.pack(pady=(10, 15))
title_label.pack(pady=(5, 10))
# 上半部分:文件区域
# 文件区域(横向排列)
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'),
text=" 📥 输入 ",
font=('Microsoft YaHei UI', 10, '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(
self.input_zone = FileZone(
input_frame,
title="待处理文件",
title="打开输入文件",
target_dir=self.input_dir,
is_input=True,
bg='#2d2d2d'
)
self.input_zone.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.input_zone.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
# 箭头
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),
arrow_label = tk.Label(
file_section,
text="",
font=('Microsoft YaHei UI', 16, 'bold'),
fg='#ffd54f',
bg='#1e1e1e'
).pack(pady=30)
)
arrow_label.pack(side=tk.LEFT, padx=5)
# 输出文件区域
output_frame = tk.LabelFrame(
file_section,
text=" 📤 输出文件 ",
font=('Microsoft YaHei UI', 11, 'bold'),
text=" 📤 输出 ",
font=('Microsoft YaHei UI', 10, '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(
self.output_zone = FileZone(
output_frame,
title="处理结果",
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.output_zone.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
# 意图识别结果区域
self._create_intent_section()
@@ -335,142 +254,150 @@ class TaskGuideView:
section = tk.LabelFrame(
self.frame,
text=" 🎯 意图识别 ",
font=('Microsoft YaHei UI', 11, 'bold'),
font=('Microsoft YaHei UI', 10, 'bold'),
fg='#81c784',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.X, padx=10, pady=5)
section.pack(fill=tk.X, padx=10, pady=3)
self.intent_label = tk.Label(
section,
text="",
font=('Microsoft YaHei UI', 10),
font=('Microsoft YaHei UI', 9),
fg='#d4d4d4',
bg='#1e1e1e',
wraplength=650,
justify=tk.LEFT
)
self.intent_label.pack(padx=10, pady=8, anchor=tk.W)
self.intent_label.pack(padx=8, pady=5, anchor=tk.W)
def _create_plan_section(self):
"""创建执行计划区域(支持 Markdown"""
section = tk.LabelFrame(
self.frame,
text=" 📄 执行计划 ",
font=('Microsoft YaHei UI', 11, 'bold'),
font=('Microsoft YaHei UI', 10, 'bold'),
fg='#ce93d8',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
section.pack(fill=tk.BOTH, expand=True, padx=10, pady=3)
# 使用 Markdown 渲染的 Text
text_frame = tk.Frame(section, bg='#2d2d2d')
text_frame.pack(fill=tk.BOTH, expand=True, padx=3, pady=3)
self.plan_text = MarkdownText(
section,
text_frame,
wrap=tk.WORD,
bg='#2d2d2d',
fg='#d4d4d4',
relief=tk.FLAT,
height=8,
padx=10,
pady=10
height=6,
padx=8,
pady=5
)
# 添加滚动条
scrollbar = ttk.Scrollbar(section, orient=tk.VERTICAL, command=self.plan_text.yview)
scrollbar = ttk.Scrollbar(text_frame, 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)
self.plan_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
def _create_risk_section(self):
"""创建风险提示区域"""
section = tk.LabelFrame(
self.frame,
text=" ⚠️ 安全提示 ",
font=('Microsoft YaHei UI', 11, 'bold'),
font=('Microsoft YaHei UI', 10, 'bold'),
fg='#ffb74d',
bg='#1e1e1e',
relief=tk.GROOVE
)
section.pack(fill=tk.X, padx=10, pady=5)
section.pack(fill=tk.X, padx=10, pady=3)
self.risk_label = tk.Label(
section,
text="• 所有操作仅在 workspace 目录内进行\n• 原始文件不会被修改或删除\n• 执行代码已通过安全检查",
font=('Microsoft YaHei UI', 10),
text="• 所有操作仅在 workspace 目录内进行 • 原始文件不会被修改或删除 • 执行代码已通过安全检查",
font=('Microsoft YaHei UI', 9),
fg='#d4d4d4',
bg='#1e1e1e',
justify=tk.LEFT
)
self.risk_label.pack(padx=10, pady=8, anchor=tk.W)
self.risk_label.pack(padx=8, pady=5, 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)
button_frame.pack(fill=tk.X, padx=10, pady=10)
# 统一按钮样式
btn_font = ('Microsoft YaHei UI', 10)
btn_width = 12
btn_height = 1
# 刷新文件列表按钮
self.refresh_btn = tk.Button(
button_frame,
text="🔄 刷新文件",
font=('Microsoft YaHei UI', 10),
text="🔄 刷新",
font=btn_font,
width=btn_width,
height=btn_height,
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.refresh_btn.pack(side=tk.LEFT)
# 执行按钮
self.execute_btn = tk.Button(
button_frame,
text="🚀 开始执行",
font=('Microsoft YaHei UI', 12, 'bold'),
font=('Microsoft YaHei UI', 10, 'bold'),
width=btn_width,
height=btn_height,
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)
# 取消按钮
self.cancel_btn = tk.Button(
button_frame,
text="取消",
font=btn_font,
width=btn_width,
height=btn_height,
bg='#616161',
fg='white',
activebackground='#757575',
activeforeground='white',
relief=tk.FLAT,
cursor='hand2',
command=self.on_cancel
)
self.cancel_btn.pack(side=tk.RIGHT, padx=(0, 10))
def _refresh_all(self):
"""刷新所有文件列表"""
self.input_zone._refresh_file_list()
self.output_zone._refresh_file_list()
self.input_zone.refresh()
self.output_zone.refresh()
def _on_execute_clicked(self):
"""执行按钮点击"""
# 刷新文件列表
self.input_zone._refresh_file_list()
self.input_zone.refresh()
# 检查 input 目录是否有文件
files = self.input_zone.get_files()
@@ -489,7 +416,7 @@ class TaskGuideView:
def set_intent_result(self, reason: str, confidence: float):
"""设置意图识别结果"""
self.intent_label.config(
text=f"识别结果: 执行任务 (置信度: {confidence:.0%})\n原因: {reason}"
text=f"识别结果: 执行任务 (置信度: {confidence:.0%}) | 原因: {reason}"
)
def set_execution_plan(self, plan: str):
@@ -505,14 +432,15 @@ class TaskGuideView:
state = tk.NORMAL if enabled else tk.DISABLED
self.execute_btn.config(state=state)
self.cancel_btn.config(state=state)
self.refresh_btn.config(state=state)
def refresh_output(self):
"""刷新输出文件列表"""
self.output_zone._refresh_file_list()
self.output_zone.refresh()
def show(self):
"""显示视图"""
self.frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self._refresh_all()
def hide(self):