diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7b4d2ff --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +LLM_API_URL=https://api.siliconflow.cn/v1/chat/completions +LLM_API_KEY=sk-fxsxbgatrjjhsnjpkdfgfngukqoqqgitjpxfqfxifcipaqpc +INTENT_MODEL_NAME=Qwen/Qwen2.5-7B-Instruct +GENERATION_MODEL_NAME=Qwen/Qwen2.5-72B-Instruct \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bf6fe5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Python 编译缓存 +__pycache__/ +*.py[cod] +*$py.class +*.pyo +*.pyd + +# 虚拟环境 +.env +.venv/ +env/ +venv/ +ENV/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# 工作区(运行时生成的文件) +workspace/ + +# 系统文件 +.DS_Store +Thumbs.db +desktop.ini + +# 日志 +*.log + +# 打包 +dist/ +build/ +*.egg-info/ +*.spec + diff --git a/executor/sandbox_runner.py b/executor/sandbox_runner.py index 920178c..89b0110 100644 --- a/executor/sandbox_runner.py +++ b/executor/sandbox_runner.py @@ -46,11 +46,13 @@ class SandboxRunner: self.input_dir = self.workspace / "input" self.output_dir = self.workspace / "output" self.logs_dir = self.workspace / "logs" + self.codes_dir = self.workspace / "codes" # 确保目录存在 self.input_dir.mkdir(parents=True, exist_ok=True) self.output_dir.mkdir(parents=True, exist_ok=True) self.logs_dir.mkdir(parents=True, exist_ok=True) + self.codes_dir.mkdir(parents=True, exist_ok=True) def save_task_code(self, code: str, task_id: Optional[str] = None) -> tuple[str, Path]: """ @@ -66,7 +68,7 @@ class SandboxRunner: if not task_id: task_id = self._generate_task_id() - code_path = self.workspace / f"task_{task_id}.py" + code_path = self.codes_dir / f"task_{task_id}.py" code_path.write_text(code, encoding='utf-8') return task_id, code_path diff --git a/llm/__pycache__/prompts.cpython-310.pyc b/llm/__pycache__/prompts.cpython-310.pyc index 6386c57..c2f75db 100644 Binary files a/llm/__pycache__/prompts.cpython-310.pyc and b/llm/__pycache__/prompts.cpython-310.pyc differ diff --git a/llm/__pycache__/prompts.cpython-313.pyc b/llm/__pycache__/prompts.cpython-313.pyc index adcaa0c..3a5c4aa 100644 Binary files a/llm/__pycache__/prompts.cpython-313.pyc and b/llm/__pycache__/prompts.cpython-313.pyc differ diff --git a/llm/prompts.py b/llm/prompts.py index c425833..91ca255 100644 --- a/llm/prompts.py +++ b/llm/prompts.py @@ -3,6 +3,39 @@ Prompt 模板集合 所有与 LLM 交互的 Prompt 统一在此管理 """ +# ======================================== +# 可用库列表(用于代码生成约束) +# ======================================== + +ALLOWED_LIBRARIES = """ +可用的 Python 库(只能使用以下库): + +标准库: +- os, sys, pathlib - 路径和系统操作 +- shutil - 文件复制移动 +- json, csv - 数据格式处理 +- re - 正则表达式 +- datetime - 日期时间 +- collections - 集合工具 +- itertools - 迭代工具 +- hashlib - 哈希计算 +- base64 - 编码解码 +- zipfile, tarfile - 压缩解压 +- glob - 文件匹配 +- fnmatch - 文件名匹配 +- tempfile - 临时文件 +- io - IO操作 +- struct - 二进制数据 +- math - 数学运算 + +第三方库: +- PIL/Pillow - 图片处理(from PIL import Image) +- openpyxl - Excel 处理 +- docx - Word 文档处理(from docx import Document) +- PyPDF2 - PDF 处理 +- chardet - 文件编码检测 +""" + # ======================================== # 意图识别 Prompt # ======================================== @@ -11,7 +44,7 @@ INTENT_CLASSIFICATION_SYSTEM = """你是一个意图分类器。判断用户输 规则: - chat: 闲聊、问答、知识查询(如天气、新闻、解释概念) -- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理文件) +- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理、转换文件) 只输出JSON,格式: {"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}""" @@ -33,21 +66,20 @@ EXECUTION_PLAN_SYSTEM = """你是一个任务规划助手。根据用户需求 4. 绝不修改或删除原始文件 5. 不进行任何网络操作 -输出格式(中文): +输出格式(中文,简洁): ## 任务理解 -[简述用户想做什么] +[一句话简述] ## 执行步骤 1. [步骤1] 2. [步骤2] -... ## 输入输出 -- 输入目录: workspace/input -- 输出目录: workspace/output +- 输入: workspace/input +- 输出: workspace/output -## 风险提示 -[可能失败的情况]""" +## 注意事项 +[可能的问题]""" EXECUTION_PLAN_USER = """用户需求:{user_input} @@ -58,22 +90,24 @@ EXECUTION_PLAN_USER = """用户需求:{user_input} # 代码生成 Prompt # ======================================== -CODE_GENERATION_SYSTEM = """你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。 +CODE_GENERATION_SYSTEM = f"""你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。 -硬性约束: -1. 只能操作 workspace/input 和 workspace/output 目录 -2. 禁止使用: requests, socket, urllib, subprocess, os.system +【硬性约束 - 必须遵守】 +1. 只能操作 workspace/input(读取)和 workspace/output(写入)目录 +2. 禁止使用: requests, socket, urllib, subprocess, os.system, eval, exec 3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink 4. 禁止访问 workspace 外的任何路径 -5. 只使用标准库: os, shutil, pathlib, json, csv 等 +5. 必须处理异常,打印清晰的错误信息 -代码模板: +{ALLOWED_LIBRARIES} + +【代码模板 - 必须按此格式】 ```python import os import shutil from pathlib import Path -# 工作目录 +# 工作目录(固定,不要修改) WORKSPACE = Path(__file__).parent INPUT_DIR = WORKSPACE / "input" OUTPUT_DIR = WORKSPACE / "output" @@ -82,15 +116,32 @@ def main(): # 确保输出目录存在 OUTPUT_DIR.mkdir(exist_ok=True) - # TODO: 实现具体逻辑 + # 获取输入文件 + input_files = list(INPUT_DIR.glob("*")) + if not input_files: + print("输入目录为空") + return - print("任务完成") + success_count = 0 + fail_count = 0 + + for file_path in input_files: + if file_path.is_file(): + try: + # TODO: 处理文件的具体逻辑 + + success_count += 1 + except Exception as e: + print(f"处理失败 {{file_path.name}}: {{e}}") + fail_count += 1 + + print(f"处理完成: 成功 {{success_count}} 个, 失败 {{fail_count}} 个") if __name__ == "__main__": main() ``` -只输出 Python 代码,不要其他解释。""" +只输出 Python 代码块,不要其他解释。""" CODE_GENERATION_USER = """执行计划: {execution_plan} @@ -107,14 +158,14 @@ CODE_GENERATION_USER = """执行计划: SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。 检查项: -1. 是否只操作 workspace 目录 -2. 是否有网络请求代码 -3. 是否有危险的文件删除操作 -4. 是否有执行外部命令的代码 +1. 是否只操作 workspace/input 和 workspace/output 目录 +2. 是否有网络请求代码(requests, socket, urllib) +3. 是否有危险的文件删除操作(os.remove, shutil.rmtree) +4. 是否有执行外部命令的代码(subprocess, os.system) 5. 代码逻辑是否与用户需求一致 输出JSON格式: -{"pass": true或false, "reason": "中文审查结论"}""" +{"pass": true或false, "reason": "中文审查结论,一句话"}""" SAFETY_REVIEW_USER = """用户需求:{user_input} @@ -127,4 +178,3 @@ SAFETY_REVIEW_USER = """用户需求:{user_input} ``` 请进行安全审查。""" - diff --git a/requirements.txt b/requirements.txt index 08157af..9c2eec5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,13 @@ # conda activate localagent # pip install -r requirements.txt +# 核心依赖 python-dotenv>=1.0.0 requests>=2.31.0 +# 文件处理库(代码生成可用) +Pillow>=10.0.0 # 图片处理 +openpyxl>=3.1.0 # Excel 处理 +python-docx>=1.0.0 # Word 文档处理 +PyPDF2>=3.0.0 # PDF 处理 +chardet>=5.0.0 # 文件编码检测 diff --git a/ui/__pycache__/chat_view.cpython-310.pyc b/ui/__pycache__/chat_view.cpython-310.pyc index ad4db0a..a78f507 100644 Binary files a/ui/__pycache__/chat_view.cpython-310.pyc and b/ui/__pycache__/chat_view.cpython-310.pyc differ diff --git a/ui/__pycache__/chat_view.cpython-313.pyc b/ui/__pycache__/chat_view.cpython-313.pyc index dcec184..3d207fd 100644 Binary files a/ui/__pycache__/chat_view.cpython-313.pyc and b/ui/__pycache__/chat_view.cpython-313.pyc differ diff --git a/ui/__pycache__/task_guide_view.cpython-310.pyc b/ui/__pycache__/task_guide_view.cpython-310.pyc index e5ae1e3..de2d1d8 100644 Binary files a/ui/__pycache__/task_guide_view.cpython-310.pyc and b/ui/__pycache__/task_guide_view.cpython-310.pyc differ diff --git a/ui/__pycache__/task_guide_view.cpython-313.pyc b/ui/__pycache__/task_guide_view.cpython-313.pyc index 98063b7..af7422e 100644 Binary files a/ui/__pycache__/task_guide_view.cpython-313.pyc and b/ui/__pycache__/task_guide_view.cpython-313.pyc differ diff --git a/ui/task_guide_view.py b/ui/task_guide_view.py index b69544d..50bcfa3 100644 --- a/ui/task_guide_view.py +++ b/ui/task_guide_view.py @@ -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('', 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):