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:
4
.env.example
Normal file
4
.env.example
Normal file
@@ -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
|
||||||
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@@ -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
|
||||||
|
|
||||||
@@ -46,11 +46,13 @@ class SandboxRunner:
|
|||||||
self.input_dir = self.workspace / "input"
|
self.input_dir = self.workspace / "input"
|
||||||
self.output_dir = self.workspace / "output"
|
self.output_dir = self.workspace / "output"
|
||||||
self.logs_dir = self.workspace / "logs"
|
self.logs_dir = self.workspace / "logs"
|
||||||
|
self.codes_dir = self.workspace / "codes"
|
||||||
|
|
||||||
# 确保目录存在
|
# 确保目录存在
|
||||||
self.input_dir.mkdir(parents=True, exist_ok=True)
|
self.input_dir.mkdir(parents=True, exist_ok=True)
|
||||||
self.output_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.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]:
|
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:
|
if not task_id:
|
||||||
task_id = self._generate_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')
|
code_path.write_text(code, encoding='utf-8')
|
||||||
|
|
||||||
return task_id, code_path
|
return task_id, code_path
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,39 @@ Prompt 模板集合
|
|||||||
所有与 LLM 交互的 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
|
# 意图识别 Prompt
|
||||||
# ========================================
|
# ========================================
|
||||||
@@ -11,7 +44,7 @@ INTENT_CLASSIFICATION_SYSTEM = """你是一个意图分类器。判断用户输
|
|||||||
|
|
||||||
规则:
|
规则:
|
||||||
- chat: 闲聊、问答、知识查询(如天气、新闻、解释概念)
|
- chat: 闲聊、问答、知识查询(如天气、新闻、解释概念)
|
||||||
- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理文件)
|
- execution: 需要操作本地文件的任务(如复制、移动、重命名、整理、转换文件)
|
||||||
|
|
||||||
只输出JSON,格式:
|
只输出JSON,格式:
|
||||||
{"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}"""
|
{"label": "chat或execution", "confidence": 0.0到1.0, "reason": "简短中文理由"}"""
|
||||||
@@ -33,21 +66,20 @@ EXECUTION_PLAN_SYSTEM = """你是一个任务规划助手。根据用户需求
|
|||||||
4. 绝不修改或删除原始文件
|
4. 绝不修改或删除原始文件
|
||||||
5. 不进行任何网络操作
|
5. 不进行任何网络操作
|
||||||
|
|
||||||
输出格式(中文):
|
输出格式(中文,简洁):
|
||||||
## 任务理解
|
## 任务理解
|
||||||
[简述用户想做什么]
|
[一句话简述]
|
||||||
|
|
||||||
## 执行步骤
|
## 执行步骤
|
||||||
1. [步骤1]
|
1. [步骤1]
|
||||||
2. [步骤2]
|
2. [步骤2]
|
||||||
...
|
|
||||||
|
|
||||||
## 输入输出
|
## 输入输出
|
||||||
- 输入目录: workspace/input
|
- 输入: workspace/input
|
||||||
- 输出目录: workspace/output
|
- 输出: workspace/output
|
||||||
|
|
||||||
## 风险提示
|
## 注意事项
|
||||||
[可能失败的情况]"""
|
[可能的问题]"""
|
||||||
|
|
||||||
EXECUTION_PLAN_USER = """用户需求:{user_input}
|
EXECUTION_PLAN_USER = """用户需求:{user_input}
|
||||||
|
|
||||||
@@ -58,22 +90,24 @@ EXECUTION_PLAN_USER = """用户需求:{user_input}
|
|||||||
# 代码生成 Prompt
|
# 代码生成 Prompt
|
||||||
# ========================================
|
# ========================================
|
||||||
|
|
||||||
CODE_GENERATION_SYSTEM = """你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。
|
CODE_GENERATION_SYSTEM = f"""你是一个 Python 代码生成器。根据执行计划生成安全的文件处理代码。
|
||||||
|
|
||||||
硬性约束:
|
【硬性约束 - 必须遵守】
|
||||||
1. 只能操作 workspace/input 和 workspace/output 目录
|
1. 只能操作 workspace/input(读取)和 workspace/output(写入)目录
|
||||||
2. 禁止使用: requests, socket, urllib, subprocess, os.system
|
2. 禁止使用: requests, socket, urllib, subprocess, os.system, eval, exec
|
||||||
3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink
|
3. 禁止删除文件: os.remove, shutil.rmtree, os.unlink
|
||||||
4. 禁止访问 workspace 外的任何路径
|
4. 禁止访问 workspace 外的任何路径
|
||||||
5. 只使用标准库: os, shutil, pathlib, json, csv 等
|
5. 必须处理异常,打印清晰的错误信息
|
||||||
|
|
||||||
代码模板:
|
{ALLOWED_LIBRARIES}
|
||||||
|
|
||||||
|
【代码模板 - 必须按此格式】
|
||||||
```python
|
```python
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# 工作目录
|
# 工作目录(固定,不要修改)
|
||||||
WORKSPACE = Path(__file__).parent
|
WORKSPACE = Path(__file__).parent
|
||||||
INPUT_DIR = WORKSPACE / "input"
|
INPUT_DIR = WORKSPACE / "input"
|
||||||
OUTPUT_DIR = WORKSPACE / "output"
|
OUTPUT_DIR = WORKSPACE / "output"
|
||||||
@@ -82,15 +116,32 @@ def main():
|
|||||||
# 确保输出目录存在
|
# 确保输出目录存在
|
||||||
OUTPUT_DIR.mkdir(exist_ok=True)
|
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__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
```
|
```
|
||||||
|
|
||||||
只输出 Python 代码,不要其他解释。"""
|
只输出 Python 代码块,不要其他解释。"""
|
||||||
|
|
||||||
CODE_GENERATION_USER = """执行计划:
|
CODE_GENERATION_USER = """执行计划:
|
||||||
{execution_plan}
|
{execution_plan}
|
||||||
@@ -107,14 +158,14 @@ CODE_GENERATION_USER = """执行计划:
|
|||||||
SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。
|
SAFETY_REVIEW_SYSTEM = """你是一个代码安全审查员。检查代码是否符合安全规范。
|
||||||
|
|
||||||
检查项:
|
检查项:
|
||||||
1. 是否只操作 workspace 目录
|
1. 是否只操作 workspace/input 和 workspace/output 目录
|
||||||
2. 是否有网络请求代码
|
2. 是否有网络请求代码(requests, socket, urllib)
|
||||||
3. 是否有危险的文件删除操作
|
3. 是否有危险的文件删除操作(os.remove, shutil.rmtree)
|
||||||
4. 是否有执行外部命令的代码
|
4. 是否有执行外部命令的代码(subprocess, os.system)
|
||||||
5. 代码逻辑是否与用户需求一致
|
5. 代码逻辑是否与用户需求一致
|
||||||
|
|
||||||
输出JSON格式:
|
输出JSON格式:
|
||||||
{"pass": true或false, "reason": "中文审查结论"}"""
|
{"pass": true或false, "reason": "中文审查结论,一句话"}"""
|
||||||
|
|
||||||
SAFETY_REVIEW_USER = """用户需求:{user_input}
|
SAFETY_REVIEW_USER = """用户需求:{user_input}
|
||||||
|
|
||||||
@@ -127,4 +178,3 @@ SAFETY_REVIEW_USER = """用户需求:{user_input}
|
|||||||
```
|
```
|
||||||
|
|
||||||
请进行安全审查。"""
|
请进行安全审查。"""
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
# conda activate localagent
|
# conda activate localagent
|
||||||
# pip install -r requirements.txt
|
# pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 核心依赖
|
||||||
python-dotenv>=1.0.0
|
python-dotenv>=1.0.0
|
||||||
requests>=2.31.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 # 文件编码检测
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
任务引导视图组件
|
任务引导视图组件
|
||||||
执行任务的引导式 UI - 支持文件拖拽和 Markdown 渲染
|
执行任务的引导式 UI - 简化版
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
@@ -8,7 +8,6 @@ from tkinter import scrolledtext, messagebox
|
|||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from typing import Callable, Optional, List
|
from typing import Callable, Optional, List
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shutil
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
@@ -22,20 +21,13 @@ class MarkdownText(tk.Text):
|
|||||||
def _setup_tags(self):
|
def _setup_tags(self):
|
||||||
"""设置 Markdown 样式标签"""
|
"""设置 Markdown 样式标签"""
|
||||||
# 标题样式
|
# 标题样式
|
||||||
self.tag_configure('h1', font=('Microsoft YaHei UI', 14, 'bold'), foreground='#ffd54f', spacing1=10, spacing3=5)
|
self.tag_configure('h1', font=('Microsoft YaHei UI', 13, 'bold'), foreground='#ffd54f', spacing1=8, spacing3=4)
|
||||||
self.tag_configure('h2', font=('Microsoft YaHei UI', 12, 'bold'), foreground='#81c784', 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', 11, 'bold'), foreground='#4fc3f7', 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('bullet', foreground='#ce93d8', lmargin1=15, lmargin2=30)
|
||||||
self.tag_configure('numbered', foreground='#ce93d8', lmargin1=20, lmargin2=35)
|
self.tag_configure('numbered', foreground='#ce93d8', lmargin1=15, lmargin2=30)
|
||||||
|
|
||||||
# 代码样式
|
|
||||||
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')
|
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')
|
self.insert(tk.END, stripped[2:] + '\n', 'h1')
|
||||||
# 无序列表
|
# 无序列表
|
||||||
elif stripped.startswith('- ') or stripped.startswith('* '):
|
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):
|
elif re.match(r'^\d+\.\s', stripped):
|
||||||
match = re.match(r'^(\d+\.)\s(.*)$', stripped)
|
match = re.match(r'^(\d+\.)\s(.*)$', stripped)
|
||||||
if match:
|
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:
|
else:
|
||||||
# 处理行内格式
|
self.insert(tk.END, line + '\n', 'normal')
|
||||||
self._render_inline(line + '\n')
|
|
||||||
|
|
||||||
def _render_inline(self, text: str):
|
|
||||||
"""渲染行内 Markdown(粗体、斜体、代码)"""
|
|
||||||
# 简化处理:直接插入普通文本
|
|
||||||
# 完整实现需要更复杂的解析
|
|
||||||
self.insert(tk.END, text, 'normal')
|
|
||||||
|
|
||||||
|
|
||||||
class DropZone(tk.Frame):
|
class FileZone(tk.Frame):
|
||||||
"""文件拖拽区域"""
|
"""简化的文件区域 - 仅显示打开文件夹按钮"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -96,81 +81,35 @@ class DropZone(tk.Frame):
|
|||||||
super().__init__(parent, **kwargs)
|
super().__init__(parent, **kwargs)
|
||||||
self.target_dir = target_dir
|
self.target_dir = target_dir
|
||||||
self.is_input = is_input
|
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.target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
self._create_widgets(title)
|
self._create_widgets(title)
|
||||||
self._setup_drag_drop()
|
|
||||||
self._refresh_file_list()
|
|
||||||
|
|
||||||
def _create_widgets(self, title: str):
|
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(
|
color = '#4fc3f7' if self.is_input else '#81c784'
|
||||||
title_frame,
|
|
||||||
text="📂",
|
self.open_btn = tk.Button(
|
||||||
|
self,
|
||||||
|
text=f"📂 {title}",
|
||||||
font=('Microsoft YaHei UI', 10),
|
font=('Microsoft YaHei UI', 10),
|
||||||
bg='#424242',
|
bg='#424242',
|
||||||
fg='white',
|
fg=color,
|
||||||
|
activebackground='#616161',
|
||||||
|
activeforeground=color,
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
|
padx=15,
|
||||||
|
pady=8,
|
||||||
cursor='hand2',
|
cursor='hand2',
|
||||||
command=self._open_folder
|
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.count_label = tk.Label(
|
||||||
self,
|
self,
|
||||||
text="0 个文件",
|
text="0 个文件",
|
||||||
@@ -180,51 +119,32 @@ class DropZone(tk.Frame):
|
|||||||
)
|
)
|
||||||
self.count_label.pack(pady=(0, 5))
|
self.count_label.pack(pady=(0, 5))
|
||||||
|
|
||||||
def _setup_drag_drop(self):
|
self._refresh_count()
|
||||||
"""设置拖拽功能(Windows 需要 windnd 库,这里用简化方案)"""
|
|
||||||
# 由于 Tkinter 原生不支持文件拖拽,使用点击打开文件夹的方式
|
|
||||||
self.drop_label.bind('<Button-1>', lambda e: self._open_folder())
|
|
||||||
|
|
||||||
def _open_folder(self):
|
def _open_folder(self):
|
||||||
"""打开目标文件夹"""
|
"""打开目标文件夹"""
|
||||||
import os
|
import os
|
||||||
os.startfile(str(self.target_dir))
|
os.startfile(str(self.target_dir))
|
||||||
|
|
||||||
def _refresh_file_list(self):
|
def _refresh_count(self):
|
||||||
"""刷新文件列表"""
|
"""刷新文件计数"""
|
||||||
self.file_listbox.delete(0, tk.END)
|
|
||||||
|
|
||||||
files = list(self.target_dir.glob('*'))
|
files = list(self.target_dir.glob('*'))
|
||||||
files = [f for f in files if f.is_file()]
|
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)} 个文件")
|
self.count_label.config(text=f"{len(files)} 个文件")
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
"""刷新"""
|
||||||
|
self._refresh_count()
|
||||||
|
|
||||||
def get_files(self) -> List[Path]:
|
def get_files(self) -> List[Path]:
|
||||||
"""获取目录中的文件列表"""
|
"""获取目录中的文件列表"""
|
||||||
files = list(self.target_dir.glob('*'))
|
files = list(self.target_dir.glob('*'))
|
||||||
return [f for f in files if f.is_file()]
|
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:
|
class TaskGuideView:
|
||||||
"""
|
"""
|
||||||
任务引导视图
|
任务引导视图 - 简化版
|
||||||
|
|
||||||
小白引导式界面,包含:
|
|
||||||
- 意图识别结果
|
|
||||||
- 文件拖拽区域(输入/输出)
|
|
||||||
- 执行计划展示(Markdown 渲染)
|
|
||||||
- 风险提示
|
|
||||||
- 执行按钮
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -250,73 +170,72 @@ class TaskGuideView:
|
|||||||
|
|
||||||
def _create_widgets(self):
|
def _create_widgets(self):
|
||||||
"""创建 UI 组件"""
|
"""创建 UI 组件"""
|
||||||
# 主框架
|
# 主框架 - 使用 Canvas 实现滚动
|
||||||
self.frame = tk.Frame(self.parent, bg='#1e1e1e')
|
self.frame = tk.Frame(self.parent, bg='#1e1e1e')
|
||||||
|
|
||||||
# 标题
|
# 标题
|
||||||
title_label = tk.Label(
|
title_label = tk.Label(
|
||||||
self.frame,
|
self.frame,
|
||||||
text="执行任务确认",
|
text="执行任务确认",
|
||||||
font=('Microsoft YaHei UI', 16, 'bold'),
|
font=('Microsoft YaHei UI', 14, 'bold'),
|
||||||
fg='#ffd54f',
|
fg='#ffd54f',
|
||||||
bg='#1e1e1e'
|
bg='#1e1e1e'
|
||||||
)
|
)
|
||||||
title_label.pack(pady=(10, 15))
|
title_label.pack(pady=(5, 10))
|
||||||
|
|
||||||
# 上半部分:文件区域
|
# 文件区域(横向排列)
|
||||||
file_section = tk.Frame(self.frame, bg='#1e1e1e')
|
file_section = tk.Frame(self.frame, bg='#1e1e1e')
|
||||||
file_section.pack(fill=tk.X, padx=10, pady=5)
|
file_section.pack(fill=tk.X, padx=10, pady=5)
|
||||||
|
|
||||||
# 输入文件区域
|
# 输入文件区域
|
||||||
input_frame = tk.LabelFrame(
|
input_frame = tk.LabelFrame(
|
||||||
file_section,
|
file_section,
|
||||||
text=" 📥 输入文件 ",
|
text=" 📥 输入 ",
|
||||||
font=('Microsoft YaHei UI', 11, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
fg='#4fc3f7',
|
fg='#4fc3f7',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
relief=tk.GROOVE
|
relief=tk.GROOVE
|
||||||
)
|
)
|
||||||
input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
|
input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
|
||||||
|
|
||||||
self.input_zone = DropZone(
|
self.input_zone = FileZone(
|
||||||
input_frame,
|
input_frame,
|
||||||
title="待处理文件",
|
title="打开输入文件夹",
|
||||||
target_dir=self.input_dir,
|
target_dir=self.input_dir,
|
||||||
is_input=True,
|
is_input=True,
|
||||||
bg='#2d2d2d'
|
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_label = tk.Label(
|
||||||
arrow_frame.pack(side=tk.LEFT, padx=10)
|
file_section,
|
||||||
tk.Label(
|
text="→",
|
||||||
arrow_frame,
|
font=('Microsoft YaHei UI', 16, 'bold'),
|
||||||
text="➡️",
|
|
||||||
font=('Microsoft YaHei UI', 20),
|
|
||||||
fg='#ffd54f',
|
fg='#ffd54f',
|
||||||
bg='#1e1e1e'
|
bg='#1e1e1e'
|
||||||
).pack(pady=30)
|
)
|
||||||
|
arrow_label.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
# 输出文件区域
|
# 输出文件区域
|
||||||
output_frame = tk.LabelFrame(
|
output_frame = tk.LabelFrame(
|
||||||
file_section,
|
file_section,
|
||||||
text=" 📤 输出文件 ",
|
text=" 📤 输出 ",
|
||||||
font=('Microsoft YaHei UI', 11, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
fg='#81c784',
|
fg='#81c784',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
relief=tk.GROOVE
|
relief=tk.GROOVE
|
||||||
)
|
)
|
||||||
output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
|
output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
|
||||||
|
|
||||||
self.output_zone = DropZone(
|
self.output_zone = FileZone(
|
||||||
output_frame,
|
output_frame,
|
||||||
title="处理结果",
|
title="打开输出文件夹",
|
||||||
target_dir=self.output_dir,
|
target_dir=self.output_dir,
|
||||||
is_input=False,
|
is_input=False,
|
||||||
bg='#2d2d2d'
|
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()
|
self._create_intent_section()
|
||||||
@@ -335,142 +254,150 @@ class TaskGuideView:
|
|||||||
section = tk.LabelFrame(
|
section = tk.LabelFrame(
|
||||||
self.frame,
|
self.frame,
|
||||||
text=" 🎯 意图识别 ",
|
text=" 🎯 意图识别 ",
|
||||||
font=('Microsoft YaHei UI', 11, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
fg='#81c784',
|
fg='#81c784',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
relief=tk.GROOVE
|
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(
|
self.intent_label = tk.Label(
|
||||||
section,
|
section,
|
||||||
text="",
|
text="",
|
||||||
font=('Microsoft YaHei UI', 10),
|
font=('Microsoft YaHei UI', 9),
|
||||||
fg='#d4d4d4',
|
fg='#d4d4d4',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
wraplength=650,
|
wraplength=650,
|
||||||
justify=tk.LEFT
|
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):
|
def _create_plan_section(self):
|
||||||
"""创建执行计划区域(支持 Markdown)"""
|
"""创建执行计划区域(支持 Markdown)"""
|
||||||
section = tk.LabelFrame(
|
section = tk.LabelFrame(
|
||||||
self.frame,
|
self.frame,
|
||||||
text=" 📄 执行计划 ",
|
text=" 📄 执行计划 ",
|
||||||
font=('Microsoft YaHei UI', 11, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
fg='#ce93d8',
|
fg='#ce93d8',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
relief=tk.GROOVE
|
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
|
# 使用 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(
|
self.plan_text = MarkdownText(
|
||||||
section,
|
text_frame,
|
||||||
wrap=tk.WORD,
|
wrap=tk.WORD,
|
||||||
bg='#2d2d2d',
|
bg='#2d2d2d',
|
||||||
fg='#d4d4d4',
|
fg='#d4d4d4',
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
height=8,
|
height=6,
|
||||||
padx=10,
|
padx=8,
|
||||||
pady=10
|
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)
|
self.plan_text.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
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):
|
def _create_risk_section(self):
|
||||||
"""创建风险提示区域"""
|
"""创建风险提示区域"""
|
||||||
section = tk.LabelFrame(
|
section = tk.LabelFrame(
|
||||||
self.frame,
|
self.frame,
|
||||||
text=" ⚠️ 安全提示 ",
|
text=" ⚠️ 安全提示 ",
|
||||||
font=('Microsoft YaHei UI', 11, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
fg='#ffb74d',
|
fg='#ffb74d',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
relief=tk.GROOVE
|
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(
|
self.risk_label = tk.Label(
|
||||||
section,
|
section,
|
||||||
text="• 所有操作仅在 workspace 目录内进行\n• 原始文件不会被修改或删除\n• 执行代码已通过安全检查",
|
text="• 所有操作仅在 workspace 目录内进行 • 原始文件不会被修改或删除 • 执行代码已通过安全检查",
|
||||||
font=('Microsoft YaHei UI', 10),
|
font=('Microsoft YaHei UI', 9),
|
||||||
fg='#d4d4d4',
|
fg='#d4d4d4',
|
||||||
bg='#1e1e1e',
|
bg='#1e1e1e',
|
||||||
justify=tk.LEFT
|
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):
|
def _create_button_section(self):
|
||||||
"""创建按钮区域"""
|
"""创建按钮区域"""
|
||||||
button_frame = tk.Frame(self.frame, bg='#1e1e1e')
|
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(
|
self.refresh_btn = tk.Button(
|
||||||
button_frame,
|
button_frame,
|
||||||
text="🔄 刷新文件",
|
text="🔄 刷新",
|
||||||
font=('Microsoft YaHei UI', 10),
|
font=btn_font,
|
||||||
|
width=btn_width,
|
||||||
|
height=btn_height,
|
||||||
bg='#424242',
|
bg='#424242',
|
||||||
fg='white',
|
fg='white',
|
||||||
activebackground='#616161',
|
activebackground='#616161',
|
||||||
activeforeground='white',
|
activeforeground='white',
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
padx=15,
|
|
||||||
pady=5,
|
|
||||||
cursor='hand2',
|
cursor='hand2',
|
||||||
command=self._refresh_all
|
command=self._refresh_all
|
||||||
)
|
)
|
||||||
self.refresh_btn.pack(side=tk.LEFT, padx=(0, 10))
|
self.refresh_btn.pack(side=tk.LEFT)
|
||||||
|
|
||||||
# 取消按钮
|
|
||||||
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(
|
self.execute_btn = tk.Button(
|
||||||
button_frame,
|
button_frame,
|
||||||
text="🚀 开始执行",
|
text="🚀 开始执行",
|
||||||
font=('Microsoft YaHei UI', 12, 'bold'),
|
font=('Microsoft YaHei UI', 10, 'bold'),
|
||||||
|
width=btn_width,
|
||||||
|
height=btn_height,
|
||||||
bg='#4caf50',
|
bg='#4caf50',
|
||||||
fg='white',
|
fg='white',
|
||||||
activebackground='#66bb6a',
|
activebackground='#66bb6a',
|
||||||
activeforeground='white',
|
activeforeground='white',
|
||||||
relief=tk.FLAT,
|
relief=tk.FLAT,
|
||||||
padx=30,
|
|
||||||
pady=8,
|
|
||||||
cursor='hand2',
|
cursor='hand2',
|
||||||
command=self._on_execute_clicked
|
command=self._on_execute_clicked
|
||||||
)
|
)
|
||||||
self.execute_btn.pack(side=tk.RIGHT)
|
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):
|
def _refresh_all(self):
|
||||||
"""刷新所有文件列表"""
|
"""刷新所有文件列表"""
|
||||||
self.input_zone._refresh_file_list()
|
self.input_zone.refresh()
|
||||||
self.output_zone._refresh_file_list()
|
self.output_zone.refresh()
|
||||||
|
|
||||||
def _on_execute_clicked(self):
|
def _on_execute_clicked(self):
|
||||||
"""执行按钮点击"""
|
"""执行按钮点击"""
|
||||||
# 刷新文件列表
|
# 刷新文件列表
|
||||||
self.input_zone._refresh_file_list()
|
self.input_zone.refresh()
|
||||||
|
|
||||||
# 检查 input 目录是否有文件
|
# 检查 input 目录是否有文件
|
||||||
files = self.input_zone.get_files()
|
files = self.input_zone.get_files()
|
||||||
@@ -489,7 +416,7 @@ class TaskGuideView:
|
|||||||
def set_intent_result(self, reason: str, confidence: float):
|
def set_intent_result(self, reason: str, confidence: float):
|
||||||
"""设置意图识别结果"""
|
"""设置意图识别结果"""
|
||||||
self.intent_label.config(
|
self.intent_label.config(
|
||||||
text=f"识别结果: 执行任务 (置信度: {confidence:.0%})\n原因: {reason}"
|
text=f"识别结果: 执行任务 (置信度: {confidence:.0%}) | 原因: {reason}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_execution_plan(self, plan: str):
|
def set_execution_plan(self, plan: str):
|
||||||
@@ -505,14 +432,15 @@ class TaskGuideView:
|
|||||||
state = tk.NORMAL if enabled else tk.DISABLED
|
state = tk.NORMAL if enabled else tk.DISABLED
|
||||||
self.execute_btn.config(state=state)
|
self.execute_btn.config(state=state)
|
||||||
self.cancel_btn.config(state=state)
|
self.cancel_btn.config(state=state)
|
||||||
|
self.refresh_btn.config(state=state)
|
||||||
|
|
||||||
def refresh_output(self):
|
def refresh_output(self):
|
||||||
"""刷新输出文件列表"""
|
"""刷新输出文件列表"""
|
||||||
self.output_zone._refresh_file_list()
|
self.output_zone.refresh()
|
||||||
|
|
||||||
def show(self):
|
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()
|
self._refresh_all()
|
||||||
|
|
||||||
def hide(self):
|
def hide(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user