2025-06-05 16:04:09 +08:00
|
|
|
|
import tkinter as tk
|
|
|
|
|
from tkinter import ttk, messagebox, filedialog
|
|
|
|
|
import sqlite3
|
|
|
|
|
import os
|
|
|
|
|
import datetime
|
|
|
|
|
import json
|
|
|
|
|
import importlib.util
|
|
|
|
|
from typing import List, Dict
|
|
|
|
|
import pyperclip
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScriptManager:
|
|
|
|
|
def __init__(self, db_path="scripts.db", scripts_dir="scripts"):
|
|
|
|
|
self.db_path = db_path
|
|
|
|
|
self.scripts_dir = scripts_dir
|
|
|
|
|
self._init_db()
|
|
|
|
|
self._init_scripts_dir()
|
|
|
|
|
self.tag_library = {
|
|
|
|
|
"文本": ["string", "text", "format", "generate"],
|
|
|
|
|
"转换": ["convert", "transform", "format"],
|
|
|
|
|
"处理": ["process", "handle", "manipulate"],
|
|
|
|
|
"生成": ["create", "generate", "make"],
|
|
|
|
|
"解析": ["parse", "extract", "analyze"]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def _init_db(self):
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
# 修改 scripts 表,添加 status 和 type 字段,移除 abbreviation
|
2025-06-05 16:04:09 +08:00
|
|
|
|
cursor.execute("""
|
|
|
|
|
CREATE TABLE IF NOT EXISTS scripts (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
path TEXT NOT NULL,
|
|
|
|
|
ui_path TEXT NOT NULL,
|
|
|
|
|
created_at TEXT NOT NULL,
|
|
|
|
|
tags TEXT,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
usage_count INTEGER DEFAULT 0,
|
|
|
|
|
status INTEGER DEFAULT 1,
|
|
|
|
|
type TEXT DEFAULT 'complex'
|
2025-06-05 16:04:09 +08:00
|
|
|
|
)
|
|
|
|
|
""")
|
2025-06-05 16:25:50 +08:00
|
|
|
|
# 创建 tags 表
|
|
|
|
|
cursor.execute("""
|
|
|
|
|
CREATE TABLE IF NOT EXISTS tags (
|
|
|
|
|
script_id INTEGER,
|
|
|
|
|
tag TEXT,
|
|
|
|
|
PRIMARY KEY (script_id, tag),
|
|
|
|
|
FOREIGN KEY (script_id) REFERENCES scripts(id)
|
|
|
|
|
)
|
|
|
|
|
""")
|
|
|
|
|
# 为 tags 字段创建索引
|
|
|
|
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_scripts_tags ON scripts(tags)")
|
|
|
|
|
cursor.execute("CREATE INDEX IF NOT EXISTS idx_tags_tag ON tags(tag)")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
def _init_scripts_dir(self):
|
|
|
|
|
os.makedirs(self.scripts_dir, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
def _suggest_tags(self, script_name: str) -> List[str]:
|
|
|
|
|
name = script_name.lower()
|
|
|
|
|
suggested_tags = []
|
|
|
|
|
for category, keywords in self.tag_library.items():
|
|
|
|
|
if any(keyword in name for keyword in keywords):
|
|
|
|
|
suggested_tags.append(category)
|
|
|
|
|
return suggested_tags
|
|
|
|
|
|
|
|
|
|
def add_script(self, script_name: str, script_content: str = None, script_path: str = None,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ui_content: str = None, script_type: str = "complex") -> Dict:
|
|
|
|
|
script_name_clean = script_name.replace(" ", "_").replace("/", "_").replace("\\", "_")
|
|
|
|
|
script_path = os.path.join(self.scripts_dir, f"{script_name_clean}.py")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
ui_path = os.path.join(self.scripts_dir, f"{script_name_clean}.ui.py")
|
|
|
|
|
|
|
|
|
|
if not script_content:
|
|
|
|
|
script_content = '''def process(input_strings: list) -> list:
|
|
|
|
|
"""处理输入字符串并返回结果"""
|
|
|
|
|
return [s.upper() for s in input_strings]
|
|
|
|
|
'''
|
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
if not ui_content and script_type == "simple":
|
|
|
|
|
ui_content = '''import tkinter as tk
|
|
|
|
|
from tkinter import ttk
|
|
|
|
|
import pyperclip
|
|
|
|
|
|
|
|
|
|
def create_ui(parent, run_callback):
|
|
|
|
|
"""创建简单脚本前端界面:单一输入输出"""
|
|
|
|
|
frame = ttk.Frame(parent)
|
|
|
|
|
frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
frame.columnconfigure(1, weight=1)
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text="输入字符串:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
input_entry = ttk.Entry(frame)
|
|
|
|
|
input_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text="输出结果:").grid(row=1, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
output_entry = ttk.Entry(frame, state="readonly")
|
|
|
|
|
output_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
def copy_output(event):
|
|
|
|
|
output = output_entry.get()
|
|
|
|
|
if output:
|
|
|
|
|
pyperclip.copy(output)
|
|
|
|
|
tk.messagebox.showinfo("提示", "输出已复制到剪贴板")
|
|
|
|
|
|
|
|
|
|
output_entry.bind("<Button-1>", copy_output)
|
|
|
|
|
|
|
|
|
|
def run():
|
|
|
|
|
input_text = input_entry.get().strip()
|
|
|
|
|
if not input_text:
|
|
|
|
|
tk.messagebox.showerror("错误", "请输入字符串")
|
|
|
|
|
return
|
|
|
|
|
result = run_callback([input_text])
|
|
|
|
|
output_entry.configure(state="normal")
|
|
|
|
|
output_entry.delete(0, tk.END)
|
|
|
|
|
output_entry.insert(0, result[0] if result else "")
|
|
|
|
|
output_entry.configure(state="readonly")
|
|
|
|
|
|
|
|
|
|
ttk.Button(frame, text="运行脚本", command=run).grid(row=2, column=0, columnspan=2, pady=5)
|
|
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
'''
|
|
|
|
|
elif not ui_content:
|
2025-06-05 16:04:09 +08:00
|
|
|
|
ui_content = '''import tkinter as tk
|
|
|
|
|
from tkinter import ttk
|
|
|
|
|
import pyperclip
|
|
|
|
|
|
|
|
|
|
def create_ui(parent, run_callback):
|
|
|
|
|
"""创建前端界面"""
|
|
|
|
|
frame = ttk.Frame(parent)
|
|
|
|
|
frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
frame.columnconfigure(1, weight=1)
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text="输入字符串 1:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
input_entry = ttk.Entry(frame)
|
|
|
|
|
input_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
ttk.Label(frame, text="输出结果 1:").grid(row=1, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
output_entry = ttk.Entry(frame, state="readonly")
|
|
|
|
|
output_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
def copy_output(event):
|
|
|
|
|
output = output_entry.get()
|
|
|
|
|
if output:
|
|
|
|
|
pyperclip.copy(output)
|
|
|
|
|
tk.messagebox.showinfo("提示", "输出已复制到剪贴板")
|
|
|
|
|
|
|
|
|
|
output_entry.bind("<Button-1>", copy_output)
|
|
|
|
|
|
|
|
|
|
def run():
|
|
|
|
|
input_text = input_entry.get().strip()
|
|
|
|
|
if not input_text:
|
|
|
|
|
tk.messagebox.showerror("错误", "请输入字符串")
|
|
|
|
|
return
|
|
|
|
|
result = run_callback([input_text])
|
|
|
|
|
output_entry.configure(state="normal")
|
|
|
|
|
output_entry.delete(0, tk.END)
|
|
|
|
|
output_entry.insert(0, result[0] if result else "")
|
|
|
|
|
output_entry.configure(state="readonly")
|
|
|
|
|
|
|
|
|
|
ttk.Button(frame, text="运行脚本", command=run).grid(row=2, column=0, columnspan=2, pady=5)
|
|
|
|
|
|
|
|
|
|
return frame
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
with open(script_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(script_content)
|
|
|
|
|
|
|
|
|
|
with open(ui_path, 'w', encoding='utf-8') as f:
|
|
|
|
|
f.write(ui_content)
|
|
|
|
|
|
|
|
|
|
suggested_tags = self._suggest_tags(script_name)
|
|
|
|
|
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("""
|
2025-06-05 16:25:50 +08:00
|
|
|
|
INSERT INTO scripts (name, path, ui_path, created_at, tags, usage_count, status, type)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
""", (
|
|
|
|
|
script_name,
|
|
|
|
|
script_path,
|
|
|
|
|
ui_path,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M:%S"),
|
2025-06-05 16:04:09 +08:00
|
|
|
|
json.dumps(suggested_tags),
|
2025-06-05 16:25:50 +08:00
|
|
|
|
0,
|
|
|
|
|
1,
|
|
|
|
|
script_type
|
2025-06-05 16:04:09 +08:00
|
|
|
|
))
|
2025-06-05 16:25:50 +08:00
|
|
|
|
script_id = cursor.lastrowid
|
|
|
|
|
for tag in suggested_tags:
|
|
|
|
|
cursor.execute("INSERT OR IGNORE INTO tags (script_id, tag) VALUES (?, ?)", (script_id, tag))
|
2025-06-05 16:04:09 +08:00
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
return {
|
2025-06-05 16:25:50 +08:00
|
|
|
|
"id": script_id,
|
2025-06-05 16:04:09 +08:00
|
|
|
|
"name": script_name,
|
|
|
|
|
"path": script_path,
|
|
|
|
|
"ui_path": ui_path,
|
|
|
|
|
"suggested_tags": suggested_tags,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
"type": script_type
|
2025-06-05 16:04:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
def update_script(self, script_id: int, new_name: str, new_tags: List[str]) -> None:
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("SELECT name, path, ui_path FROM scripts WHERE id = ? AND status = 1", (script_id,))
|
|
|
|
|
result = cursor.fetchone()
|
|
|
|
|
if not result:
|
|
|
|
|
raise ValueError("脚本未找到或已删除")
|
|
|
|
|
|
|
|
|
|
old_name, old_path, old_ui_path = result
|
|
|
|
|
new_name_clean = new_name.replace(" ", "_").replace("/", "_").replace("\\", "_")
|
|
|
|
|
new_path = os.path.join(self.scripts_dir, f"{new_name_clean}.py")
|
|
|
|
|
new_ui_path = os.path.join(self.scripts_dir, f"{new_name_clean}.ui.py")
|
|
|
|
|
|
|
|
|
|
# 重命名文件
|
|
|
|
|
if old_name != new_name:
|
|
|
|
|
os.rename(old_path, new_path)
|
|
|
|
|
os.rename(old_ui_path, new_ui_path)
|
|
|
|
|
|
|
|
|
|
# 更新 scripts 表
|
|
|
|
|
cursor.execute("""
|
|
|
|
|
UPDATE scripts SET name = ?, path = ?, ui_path = ?, tags = ?
|
|
|
|
|
WHERE id = ? AND status = 1
|
|
|
|
|
""", (new_name, new_path, new_ui_path, json.dumps(new_tags), script_id))
|
|
|
|
|
|
|
|
|
|
# 更新 tags 表
|
|
|
|
|
cursor.execute("DELETE FROM tags WHERE script_id = ?", (script_id,))
|
|
|
|
|
for tag in new_tags:
|
|
|
|
|
cursor.execute("INSERT OR IGNORE INTO tags (script_id, tag) VALUES (?, ?)", (script_id, tag))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
def delete_script(self, script_id: int) -> None:
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute("UPDATE scripts SET status = 0 WHERE id = ? AND status = 1", (script_id,))
|
|
|
|
|
if cursor.rowcount == 0:
|
|
|
|
|
raise ValueError("脚本未找到或已删除")
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
2025-06-05 16:04:09 +08:00
|
|
|
|
def list_scripts(self, tag: str = None, name: str = None) -> List[Dict]:
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
query = "SELECT s.id, s.name, s.path, s.created_at, s.tags, s.usage_count, s.ui_path, s.type " \
|
|
|
|
|
"FROM scripts s WHERE s.status = 1"
|
2025-06-05 16:04:09 +08:00
|
|
|
|
params = []
|
|
|
|
|
conditions = []
|
|
|
|
|
|
|
|
|
|
if tag:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
query += " AND EXISTS (SELECT 1 FROM tags t WHERE t.script_id = s.id AND t.tag LIKE ?)"
|
2025-06-05 16:04:09 +08:00
|
|
|
|
params.append(f'%{tag}%')
|
|
|
|
|
if name:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
conditions.append("s.name LIKE ?")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
params.append(f'%{name}%')
|
|
|
|
|
|
|
|
|
|
if conditions:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
query += " AND " + " AND ".join(conditions)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
|
|
|
|
cursor.execute(query, params)
|
|
|
|
|
scripts = []
|
|
|
|
|
for row in cursor.fetchall():
|
|
|
|
|
try:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
tags = json.loads(row[4]) if row[4] and row[4].strip() else []
|
2025-06-05 16:04:09 +08:00
|
|
|
|
except json.JSONDecodeError:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
tags = []
|
2025-06-05 16:04:09 +08:00
|
|
|
|
scripts.append({
|
|
|
|
|
"id": row[0],
|
|
|
|
|
"name": row[1],
|
|
|
|
|
"path": row[2],
|
|
|
|
|
"created_at": row[3],
|
|
|
|
|
"tags": tags,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
"usage_count": row[5],
|
|
|
|
|
"ui_path": row[6],
|
|
|
|
|
"type": row[7]
|
2025-06-05 16:04:09 +08:00
|
|
|
|
})
|
|
|
|
|
return scripts
|
|
|
|
|
|
|
|
|
|
def run_script(self, script_id: int, input_strings: List[str]) -> List[str]:
|
|
|
|
|
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
|
cursor = conn.cursor()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
cursor.execute("SELECT path FROM scripts WHERE id = ? AND status = 1", (script_id,))
|
2025-06-05 16:04:09 +08:00
|
|
|
|
result = cursor.fetchone()
|
|
|
|
|
if not result:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
raise ValueError("脚本未找到或已删除")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
|
|
|
|
script_path = result[0]
|
|
|
|
|
cursor.execute("UPDATE scripts SET usage_count = usage_count + 1 WHERE id = ?", (script_id,))
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
|
|
|
|
spec = importlib.util.spec_from_file_location("script_module", script_path)
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
return module.process(input_strings)
|
|
|
|
|
|
|
|
|
|
def load_ui_module(self, ui_path: str):
|
|
|
|
|
if not ui_path or not os.path.isfile(ui_path):
|
|
|
|
|
raise ValueError(f"前端界面文件不存在或路径无效:{ui_path}")
|
|
|
|
|
try:
|
|
|
|
|
spec = importlib.util.spec_from_file_location("ui_module", ui_path)
|
|
|
|
|
if spec is None:
|
|
|
|
|
raise ValueError(f"无法加载前端界面模块:{ui_path} (模块规格无效)")
|
|
|
|
|
module = importlib.util.module_from_spec(spec)
|
|
|
|
|
spec.loader.exec_module(module)
|
|
|
|
|
return module
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise ValueError(f"加载前端界面失败:{str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScriptManagerGUI:
|
|
|
|
|
def __init__(self, root):
|
|
|
|
|
self.root = root
|
|
|
|
|
self.root.title("脚本管理器")
|
|
|
|
|
self.manager = ScriptManager()
|
|
|
|
|
self.selected_script_id = None
|
|
|
|
|
self.current_ui_frame = None
|
|
|
|
|
self.create_widgets()
|
|
|
|
|
self.root.columnconfigure(0, weight=1)
|
|
|
|
|
self.root.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
def create_widgets(self):
|
|
|
|
|
self.notebook = ttk.Notebook(self.root)
|
|
|
|
|
self.notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=10, pady=5)
|
|
|
|
|
self.notebook.columnconfigure(0, weight=1)
|
|
|
|
|
self.notebook.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
# 添加脚本选项卡
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.create_add_tab()
|
|
|
|
|
|
|
|
|
|
# 修改脚本选项卡
|
|
|
|
|
self.create_edit_tab()
|
|
|
|
|
|
|
|
|
|
# 索引运行脚本选项卡
|
|
|
|
|
self.create_run_tab()
|
|
|
|
|
|
|
|
|
|
def create_add_tab(self):
|
2025-06-05 16:04:09 +08:00
|
|
|
|
add_frame = ttk.Frame(self.notebook, padding="10")
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.notebook.add(add_frame, text="添加【Add】")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
add_frame.columnconfigure(1, weight=1)
|
|
|
|
|
add_frame.rowconfigure(3, weight=1)
|
|
|
|
|
|
|
|
|
|
ttk.Label(add_frame, text="脚本名称:").grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
self.script_name_var = tk.StringVar()
|
|
|
|
|
ttk.Entry(add_frame, textvariable=self.script_name_var).grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Label(add_frame, text="脚本类型:").grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
self.script_type_var = tk.StringVar(value="simple")
|
|
|
|
|
ttk.OptionMenu(add_frame, self.script_type_var, "simple", "simple", "complex").grid(row=1, column=1, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
ttk.Label(add_frame, text="脚本路径(可选):").grid(row=2, column=0, sticky=tk.W, pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
self.script_path_var = tk.StringVar()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Entry(add_frame, textvariable=self.script_path_var).grid(row=2, column=1, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
ttk.Button(add_frame, text="选择文件", command=self.browse_file).grid(row=2, column=2, padx=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Label(add_frame, text="标签(逗号分隔,可选):").grid(row=3, column=0, sticky=tk.W, pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
self.tags_var = tk.StringVar()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Entry(add_frame, textvariable=self.tags_var).grid(row=3, column=1, sticky=(tk.W, tk.E), pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Label(add_frame, text="脚本内容(可选):").grid(row=4, column=0, sticky=tk.W, pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
self.script_content_text = tk.Text(add_frame, height=5)
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.script_content_text.grid(row=4, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ttk.Label(add_frame, text="前端界面内容(复杂脚本可选):").grid(row=5, column=0, sticky=tk.W, pady=5)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
self.ui_content_text = tk.Text(add_frame, height=5)
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.ui_content_text.grid(row=5, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
|
|
|
|
|
|
|
|
|
|
ttk.Button(add_frame, text="添加脚本", command=self.add_script).grid(row=6, column=0, columnspan=3, pady=5)
|
|
|
|
|
|
|
|
|
|
def create_edit_tab(self):
|
|
|
|
|
edit_frame = ttk.Frame(self.notebook, padding="10")
|
|
|
|
|
self.notebook.add(edit_frame, text="修改【Modify】")
|
|
|
|
|
edit_frame.columnconfigure(0, weight=1)
|
|
|
|
|
edit_frame.rowconfigure(1, weight=1)
|
|
|
|
|
|
|
|
|
|
# 搜索和脚本列表
|
|
|
|
|
ttk.Label(edit_frame, text="搜索(名称或标签):").grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
self.edit_search_var = tk.StringVar()
|
|
|
|
|
ttk.Entry(edit_frame, textvariable=self.edit_search_var).grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
ttk.Button(edit_frame, text="搜索", command=self.refresh_edit_list).grid(row=0, column=2, padx=5)
|
|
|
|
|
|
|
|
|
|
self.edit_tree = ttk.Treeview(edit_frame, columns=("ID", "名称", "标签", "使用次数", "创建时间", "类型"),
|
|
|
|
|
show="headings")
|
|
|
|
|
self.edit_tree.heading("ID", text="ID")
|
|
|
|
|
self.edit_tree.heading("名称", text="名称")
|
|
|
|
|
self.edit_tree.heading("标签", text="标签")
|
|
|
|
|
self.edit_tree.heading("使用次数", text="使用次数")
|
|
|
|
|
self.edit_tree.heading("创建时间", text="创建时间")
|
|
|
|
|
self.edit_tree.heading("类型", text="类型")
|
|
|
|
|
|
|
|
|
|
self.edit_tree.column("ID", width=25, minwidth=25, stretch=False)
|
|
|
|
|
self.edit_tree.column("名称", width=150, minwidth=100, stretch=True)
|
|
|
|
|
self.edit_tree.column("标签", width=100, minwidth=80, stretch=True)
|
|
|
|
|
self.edit_tree.column("使用次数", width=80, minwidth=60, stretch=False)
|
|
|
|
|
self.edit_tree.column("创建时间", width=120, minwidth=100, stretch=True)
|
|
|
|
|
self.edit_tree.column("类型", width=80, minwidth=60, stretch=False)
|
|
|
|
|
|
|
|
|
|
self.edit_tree.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
self.edit_tree.bind("<<TreeviewSelect>>", self.on_edit_script_select)
|
|
|
|
|
|
|
|
|
|
# 修改区域
|
|
|
|
|
edit_input_frame = ttk.Frame(edit_frame)
|
|
|
|
|
edit_input_frame.grid(row=2, column=0, columnspan=3, pady=5, sticky=(tk.W, tk.E))
|
|
|
|
|
edit_input_frame.columnconfigure(1, weight=1)
|
|
|
|
|
|
|
|
|
|
ttk.Label(edit_input_frame, text="脚本名称:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
self.edit_name_var = tk.StringVar()
|
|
|
|
|
ttk.Entry(edit_input_frame, textvariable=self.edit_name_var).grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
ttk.Label(edit_input_frame, text="标签(逗号分隔):").grid(row=1, column=0, sticky=tk.W, pady=2)
|
|
|
|
|
self.edit_tags_var = tk.StringVar()
|
|
|
|
|
ttk.Entry(edit_input_frame, textvariable=self.edit_tags_var).grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2)
|
|
|
|
|
|
|
|
|
|
ttk.Button(edit_input_frame, text="修改脚本", command=self.update_script).grid(row=2, column=0, pady=5)
|
|
|
|
|
ttk.Button(edit_input_frame, text="删除脚本", command=self.delete_script).grid(row=2, column=1, sticky=tk.W, pady=5)
|
|
|
|
|
|
|
|
|
|
self.refresh_edit_list()
|
|
|
|
|
|
|
|
|
|
def create_run_tab(self):
|
2025-06-05 16:04:09 +08:00
|
|
|
|
run_frame = ttk.Frame(self.notebook, padding="10")
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.notebook.add(run_frame, text="运行【Run】")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
run_frame.columnconfigure(0, weight=1)
|
|
|
|
|
run_frame.columnconfigure(2, weight=1)
|
|
|
|
|
run_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
left_frame = ttk.Frame(run_frame)
|
|
|
|
|
left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5))
|
|
|
|
|
left_frame.columnconfigure(0, weight=1)
|
|
|
|
|
left_frame.rowconfigure(1, weight=1)
|
|
|
|
|
|
|
|
|
|
ttk.Label(left_frame, text="搜索(名称或标签):").grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
|
|
|
self.search_var = tk.StringVar()
|
|
|
|
|
ttk.Entry(left_frame, textvariable=self.search_var).grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5)
|
|
|
|
|
ttk.Button(left_frame, text="搜索", command=self.search_scripts).grid(row=0, column=2, padx=5)
|
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.tree = ttk.Treeview(left_frame, columns=("ID", "名称", "标签", "使用次数", "创建时间", "类型"),
|
2025-06-05 16:04:09 +08:00
|
|
|
|
show="headings")
|
|
|
|
|
self.tree.heading("ID", text="ID")
|
|
|
|
|
self.tree.heading("名称", text="名称")
|
|
|
|
|
self.tree.heading("标签", text="标签")
|
|
|
|
|
self.tree.heading("使用次数", text="使用次数")
|
|
|
|
|
self.tree.heading("创建时间", text="创建时间")
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.tree.heading("类型", text="类型")
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.tree.column("ID", width=25, minwidth=25, stretch=False)
|
|
|
|
|
self.tree.column("名称", width=150, minwidth=100, stretch=True)
|
|
|
|
|
self.tree.column("标签", width=100, minwidth=80, stretch=True)
|
|
|
|
|
self.tree.column("使用次数", width=80, minwidth=60, stretch=False)
|
|
|
|
|
self.tree.column("创建时间", width=120, minwidth=100, stretch=True)
|
|
|
|
|
self.tree.column("类型", width=80, minwidth=60, stretch=False)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
|
|
|
|
self.tree.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
self.tree.bind("<<TreeviewSelect>>", self.on_script_select)
|
2025-06-05 16:25:50 +08:00
|
|
|
|
|
2025-06-05 16:04:09 +08:00
|
|
|
|
ttk.Separator(run_frame, orient=tk.VERTICAL).grid(row=0, column=1, sticky=(tk.N, tk.S), padx=5)
|
|
|
|
|
|
|
|
|
|
self.io_frame = ttk.Frame(run_frame)
|
|
|
|
|
self.io_frame.grid(row=0, column=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(5, 0))
|
|
|
|
|
self.io_frame.columnconfigure(0, weight=1)
|
|
|
|
|
self.io_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
|
|
self.refresh_script_list()
|
|
|
|
|
|
|
|
|
|
def browse_file(self):
|
|
|
|
|
file_path = filedialog.askopenfilename(filetypes=[("Python files", "*.py")])
|
|
|
|
|
if file_path:
|
|
|
|
|
self.script_path_var.set(file_path)
|
|
|
|
|
|
|
|
|
|
def refresh_script_list(self, search_term: str = None):
|
|
|
|
|
for item in self.tree.get_children():
|
|
|
|
|
self.tree.delete(item)
|
|
|
|
|
scripts = self.manager.list_scripts(tag=search_term, name=search_term)
|
|
|
|
|
for script in scripts:
|
|
|
|
|
self.tree.insert("", tk.END, values=(
|
|
|
|
|
script["id"],
|
|
|
|
|
script["name"],
|
|
|
|
|
", ".join(script["tags"]),
|
|
|
|
|
script["usage_count"],
|
2025-06-05 16:25:50 +08:00
|
|
|
|
script["created_at"],
|
|
|
|
|
script["type"]
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def refresh_edit_list(self, search_term: str = None):
|
|
|
|
|
for item in self.edit_tree.get_children():
|
|
|
|
|
self.edit_tree.delete(item)
|
|
|
|
|
scripts = self.manager.list_scripts(tag=search_term, name=search_term)
|
|
|
|
|
for script in scripts:
|
|
|
|
|
self.edit_tree.insert("", tk.END, values=(
|
|
|
|
|
script["id"],
|
|
|
|
|
script["name"],
|
|
|
|
|
", ".join(script["tags"]),
|
|
|
|
|
script["usage_count"],
|
|
|
|
|
script["created_at"],
|
|
|
|
|
script["type"]
|
2025-06-05 16:04:09 +08:00
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def add_script(self):
|
|
|
|
|
script_name = self.script_name_var.get().strip()
|
|
|
|
|
script_path = self.script_path_var.get().strip()
|
|
|
|
|
script_content = self.script_content_text.get("1.0", tk.END).strip()
|
|
|
|
|
ui_content = self.ui_content_text.get("1.0", tk.END).strip()
|
|
|
|
|
tags = [t.strip() for t in self.tags_var.get().split(",") if t.strip()]
|
2025-06-05 16:25:50 +08:00
|
|
|
|
script_type = self.script_type_var.get()
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
|
|
|
|
if not script_name and not script_path:
|
|
|
|
|
messagebox.showerror("错误", "脚本名称和脚本路径不能同时为空")
|
|
|
|
|
return
|
|
|
|
|
if not script_path and not script_content:
|
|
|
|
|
messagebox.showerror("错误", "脚本路径和脚本内容不能同时为空")
|
|
|
|
|
return
|
|
|
|
|
if not script_name and script_path:
|
|
|
|
|
script_name = os.path.splitext(os.path.basename(script_path))[0]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
result = self.manager.add_script(script_name, script_content or None, script_path or None,
|
2025-06-05 16:25:50 +08:00
|
|
|
|
ui_content or None, script_type)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
if tags:
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.manager.update_script(result["id"], script_name, tags)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
messagebox.showinfo("成功", f"脚本 '{script_name}' 已添加!\n建议标签:{', '.join(result['suggested_tags'])}")
|
|
|
|
|
self.script_name_var.set("")
|
|
|
|
|
self.script_path_var.set("")
|
|
|
|
|
self.script_content_text.delete("1.0", tk.END)
|
|
|
|
|
self.ui_content_text.delete("1.0", tk.END)
|
|
|
|
|
self.tags_var.set(", ".join(result["suggested_tags"]))
|
|
|
|
|
self.refresh_script_list()
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.refresh_edit_list()
|
2025-06-05 16:04:09 +08:00
|
|
|
|
except Exception as e:
|
|
|
|
|
messagebox.showerror("错误", f"添加脚本失败:{str(e)}")
|
|
|
|
|
|
|
|
|
|
def on_script_select(self, event):
|
|
|
|
|
selection = self.tree.selection()
|
|
|
|
|
if selection:
|
|
|
|
|
item = self.tree.item(selection[0])
|
|
|
|
|
self.selected_script_id = int(item["values"][0])
|
|
|
|
|
script = next(s for s in self.manager.list_scripts() if s["id"] == self.selected_script_id)
|
|
|
|
|
self.refresh_io_frame(script["ui_path"], script["path"])
|
2025-06-05 16:25:50 +08:00
|
|
|
|
self.refresh_script_list() # 实时更新使用次数
|
|
|
|
|
|
|
|
|
|
def on_edit_script_select(self, event):
|
|
|
|
|
selection = self.edit_tree.selection()
|
|
|
|
|
if selection:
|
|
|
|
|
item = self.edit_tree.item(selection[0])
|
|
|
|
|
self.selected_script_id = int(item["values"][0])
|
|
|
|
|
script = next(s for s in self.manager.list_scripts() if s["id"] == self.selected_script_id)
|
|
|
|
|
self.edit_name_var.set(script["name"])
|
|
|
|
|
self.edit_tags_var.set(", ".join(script["tags"]))
|
|
|
|
|
|
|
|
|
|
def update_script(self):
|
|
|
|
|
if not self.selected_script_id:
|
|
|
|
|
messagebox.showerror("错误", "请先选择一个脚本")
|
|
|
|
|
return
|
|
|
|
|
new_name = self.edit_name_var.get().strip()
|
|
|
|
|
new_tags = [t.strip() for t in self.edit_tags_var.get().split(",") if t.strip()]
|
|
|
|
|
if not new_name:
|
|
|
|
|
messagebox.showerror("错误", "脚本名称不能为空")
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
self.manager.update_script(self.selected_script_id, new_name, new_tags)
|
|
|
|
|
messagebox.showinfo("成功", f"脚本 '{new_name}' 已更新")
|
|
|
|
|
self.edit_name_var.set("")
|
|
|
|
|
self.edit_tags_var.set("")
|
|
|
|
|
self.refresh_edit_list()
|
|
|
|
|
self.refresh_script_list()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
messagebox.showerror("错误", f"更新脚本失败:{str(e)}")
|
|
|
|
|
|
|
|
|
|
def delete_script(self):
|
|
|
|
|
if not self.selected_script_id:
|
|
|
|
|
messagebox.showerror("错误", "请先选择一个脚本")
|
|
|
|
|
return
|
|
|
|
|
if messagebox.askyesno("确认", "确定要删除此脚本吗?"):
|
|
|
|
|
try:
|
|
|
|
|
self.manager.delete_script(self.selected_script_id)
|
|
|
|
|
messagebox.showinfo("成功", "脚本已删除")
|
|
|
|
|
self.edit_name_var.set("")
|
|
|
|
|
self.edit_tags_var.set("")
|
|
|
|
|
self.refresh_edit_list()
|
|
|
|
|
self.refresh_script_list()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
messagebox.showerror("错误", f"删除脚本失败:{str(e)}")
|
|
|
|
|
|
|
|
|
|
def search_scripts(self):
|
|
|
|
|
search_term = self.search_var.get().strip()
|
|
|
|
|
self.refresh_script_list(search_term)
|
2025-06-05 16:04:09 +08:00
|
|
|
|
|
|
|
|
|
def refresh_io_frame(self, ui_path: str = None, script_path: str = None):
|
|
|
|
|
if self.current_ui_frame:
|
|
|
|
|
self.current_ui_frame.destroy()
|
|
|
|
|
|
|
|
|
|
if not ui_path or not script_path:
|
|
|
|
|
self.current_ui_frame = ttk.Label(
|
|
|
|
|
self.io_frame,
|
|
|
|
|
text="请选择一个脚本",
|
|
|
|
|
wraplength=int(self.io_frame.winfo_width() * 0.9) or 300
|
|
|
|
|
)
|
|
|
|
|
self.current_ui_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ui_module = self.manager.load_ui_module(ui_path)
|
|
|
|
|
def run_callback(input_strings):
|
2025-06-05 16:25:50 +08:00
|
|
|
|
result = self.manager.run_script(self.selected_script_id, input_strings)
|
|
|
|
|
self.refresh_script_list() # 实时更新使用次数
|
|
|
|
|
return result
|
2025-06-05 16:04:09 +08:00
|
|
|
|
self.current_ui_frame = ui_module.create_ui(self.io_frame, run_callback)
|
|
|
|
|
self.current_ui_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
except Exception as e:
|
|
|
|
|
frame_width = self.io_frame.winfo_width() or 300
|
|
|
|
|
self.current_ui_frame = ttk.Label(
|
|
|
|
|
self.io_frame,
|
|
|
|
|
text=f"加载前端界面失败:{str(e)}",
|
|
|
|
|
wraplength=int(frame_width * 0.9),
|
|
|
|
|
padding=(5, 5),
|
|
|
|
|
foreground="red"
|
|
|
|
|
)
|
|
|
|
|
self.current_ui_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
root = tk.Tk()
|
|
|
|
|
root.geometry("800x600")
|
|
|
|
|
app = ScriptManagerGUI(root)
|
|
|
|
|
root.mainloop()
|