From 15db2f177ab26e1fd2b3c49f8c6c5c405de84a89 Mon Sep 17 00:00:00 2001 From: Pi <123@gmail.com> Date: Fri, 6 Jun 2025 21:04:04 +0800 Subject: [PATCH] init --- .idea/.gitignore | 8 + .idea/vcs.xml | 4 + script_manager.py | 657 +++++++++++++++++++++++++++++ scripts.db | Bin 0 -> 32768 bytes scripts/json数组元素字段拼接.py | 34 ++ scripts/json数组元素字段拼接.ui.py | 111 +++++ scripts/多个字符串拼接为一个.py | 45 ++ scripts/多个字符串拼接为一个.ui.py | 144 +++++++ scripts/时间戳.py | 59 +++ scripts/时间戳.ui.py | 95 +++++ scripts/比较两个字符串的差异.py | 98 +++++ scripts/比较两个字符串的差异.ui.py | 94 +++++ 12 files changed, 1349 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/vcs.xml create mode 100644 script_manager.py create mode 100644 scripts.db create mode 100644 scripts/json数组元素字段拼接.py create mode 100644 scripts/json数组元素字段拼接.ui.py create mode 100644 scripts/多个字符串拼接为一个.py create mode 100644 scripts/多个字符串拼接为一个.ui.py create mode 100644 scripts/时间戳.py create mode 100644 scripts/时间戳.ui.py create mode 100644 scripts/比较两个字符串的差异.py create mode 100644 scripts/比较两个字符串的差异.ui.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/script_manager.py b/script_manager.py new file mode 100644 index 0000000..2a2766b --- /dev/null +++ b/script_manager.py @@ -0,0 +1,657 @@ +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() + # 修改 scripts 表,添加 status 和 type 字段,移除 abbreviation + 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, + usage_count INTEGER DEFAULT 0, + status INTEGER DEFAULT 1, + type TEXT DEFAULT 'complex' + ) + """) + # 创建 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)") + 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, + 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") + 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] +''' + + 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("", 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: + 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("", 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(""" + INSERT INTO scripts (name, path, ui_path, created_at, tags, usage_count, status, type) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + """, ( + script_name, + script_path, + ui_path, + datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M:%S"), + json.dumps(suggested_tags), + 0, + 1, + script_type + )) + script_id = cursor.lastrowid + for tag in suggested_tags: + cursor.execute("INSERT OR IGNORE INTO tags (script_id, tag) VALUES (?, ?)", (script_id, tag)) + conn.commit() + + return { + "id": script_id, + "name": script_name, + "path": script_path, + "ui_path": ui_path, + "suggested_tags": suggested_tags, + "type": script_type + } + + 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() + + def list_scripts(self, tag: str = None, name: str = None) -> List[Dict]: + with sqlite3.connect(self.db_path) as conn: + cursor = conn.cursor() + 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" + params = [] + conditions = [] + + if tag: + query += " AND EXISTS (SELECT 1 FROM tags t WHERE t.script_id = s.id AND t.tag LIKE ?)" + params.append(f'%{tag}%') + if name: + conditions.append("s.name LIKE ?") + params.append(f'%{name}%') + + if conditions: + query += " AND " + " AND ".join(conditions) + + cursor.execute(query, params) + scripts = [] + for row in cursor.fetchall(): + try: + tags = json.loads(row[4]) if row[4] and row[4].strip() else [] + except json.JSONDecodeError: + tags = [] + scripts.append({ + "id": row[0], + "name": row[1], + "path": row[2], + "created_at": row[3], + "tags": tags, + "usage_count": row[5], + "ui_path": row[6], + "type": row[7] + }) + 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() + cursor.execute("SELECT path FROM scripts WHERE id = ? AND status = 1", (script_id,)) + result = cursor.fetchone() + if not result: + raise ValueError("脚本未找到或已删除") + + 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.text_editor_content = "" + 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) + + # 添加脚本选项卡 + self.create_add_tab() + + # 修改脚本选项卡 + self.create_edit_tab() + + # 索引运行脚本选项卡 + self.create_run_tab() + + # 文本编辑选项卡 + self.create_text_editor_tab() + + # 绑定选项卡切换事件 + self.notebook.bind("<>", self.on_tab_changed) + + def create_text_editor_tab(self): + """创建文本编辑选项卡""" + text_editor_frame = ttk.Frame(self.notebook, padding="10") + self.notebook.add(text_editor_frame, text="文本【text】") + text_editor_frame.columnconfigure(0, weight=1) + text_editor_frame.rowconfigure(0, weight=1) + + self.text_editor = tk.Text(text_editor_frame, height=20, width=50) + self.text_editor.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) + text_scroll = ttk.Scrollbar(text_editor_frame, orient=tk.VERTICAL, command=self.text_editor.yview) + text_scroll.grid(row=0, column=1, sticky=(tk.N, tk.S)) + self.text_editor["yscrollcommand"] = text_scroll.set + + # 恢复之前的内容(如果有) + self.text_editor.insert("1.0", self.text_editor_content) + + def on_tab_changed(self, event): + """选项卡切换时保存文本编辑内容""" + selected_tab = self.notebook.select() + tab_name = self.notebook.tab(selected_tab, "text") + + # 如果离开文本编辑选项卡,保存内容 + if hasattr(self, "text_editor") and tab_name != "文本编辑": + self.text_editor_content = self.text_editor.get("1.0", tk.END).strip() + + # 如果切换到文本编辑选项卡,恢复内容 + if tab_name == "文本编辑" and hasattr(self, "text_editor"): + self.text_editor.delete("1.0", tk.END) + self.text_editor.insert("1.0", self.text_editor_content) + + def create_add_tab(self): + add_frame = ttk.Frame(self.notebook, padding="10") + self.notebook.add(add_frame, text="添加【Add】") + 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) + + 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) + self.script_path_var = tk.StringVar() + 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) + + ttk.Label(add_frame, text="标签(逗号分隔,可选):").grid(row=3, column=0, sticky=tk.W, pady=5) + self.tags_var = tk.StringVar() + ttk.Entry(add_frame, textvariable=self.tags_var).grid(row=3, column=1, sticky=(tk.W, tk.E), pady=5) + + ttk.Label(add_frame, text="脚本内容(可选):").grid(row=4, column=0, sticky=tk.W, pady=5) + self.script_content_text = tk.Text(add_frame, height=5) + self.script_content_text.grid(row=4, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) + + ttk.Label(add_frame, text="前端界面内容(复杂脚本可选):").grid(row=5, column=0, sticky=tk.W, pady=5) + self.ui_content_text = tk.Text(add_frame, height=5) + 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("<>", 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): + run_frame = ttk.Frame(self.notebook, padding="10") + self.notebook.add(run_frame, text="运行【Run】") + 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) + + self.tree = ttk.Treeview(left_frame, columns=("ID", "名称", "标签", "使用次数", "创建时间", "类型"), + show="headings") + self.tree.heading("ID", text="ID") + self.tree.heading("名称", text="名称") + self.tree.heading("标签", text="标签") + self.tree.heading("使用次数", text="使用次数") + self.tree.heading("创建时间", text="创建时间") + self.tree.heading("类型", text="类型") + + 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) + + self.tree.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S)) + self.tree.bind("<>", self.on_script_select) + + 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"], + 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"] + )) + + 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()] + script_type = self.script_type_var.get() + + 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, + ui_content or None, script_type) + if tags: + self.manager.update_script(result["id"], script_name, tags) + 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() + self.refresh_edit_list() + 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"]) + 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) + + 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): + result = self.manager.run_script(self.selected_script_id, input_strings) + self.refresh_script_list() # 实时更新使用次数 + return result + 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() \ No newline at end of file diff --git a/scripts.db b/scripts.db new file mode 100644 index 0000000000000000000000000000000000000000..ec53e5c050144570dcde5718a94e50db241b5028 GIT binary patch literal 32768 zcmeI5O>7%Q6vubHYj3>y7}^p{WkFYlDm4j}W5-GA3Y8QLiqsaG1}Rk%SzCJ(mc}2o z*QJ3AZ9-Kw5~UI}Qpxl8Ydips{FTF zclOQOH*bEkv&wkhjCbwarW=|vmd&SCLy2(raXimGsVE%B4biiKo*sJg^jt+xk)F=M zWXl~x+{XPg%`HbcPty$7(%W*SOf~%9$jW*VAf#QrW9bDr(Wl>X~F-OKTaU!&J$rY0bV<8H^7NI%)g=Ox92`#fb?^ zIj0&gItt4w(_&OZOC{(Z;>%k$)bT>KWvjemK^@l;$!sxWD0aP+ls2XoCk!QItLTY} zSB^`LN?V&!>wm#ejiO!7?e;n?d&XpL5s!+!WHz0f&$qm#kxr4)sW0)3PxVlY42i&6S=*WET&$} z=#+pQq%Z3G`ya9D;E~z*nd7#4J-+p={BE;eddjW}wZaTZw$uGPo{kUEm(#6W<+&YZ zQ5&dQaA%{(_qdj^W8?#}jU5iXrD=~BSHM3X<%G5PC8Q{nO`GK4#$H^mBQj1V)8Nh3!=Lr9w7SZL0M8)WW5A%g5d<9Xk;i zbv4tv@Z=^(ZjvACRtuRR00JNY0w4eaAOHd&00JNY0w4eacPoL_E+NP}0RUF4l-TGJ z{Ep1c#{UwzAd)}G4e}HDj(kO~k_!aU`FCrJ!Y>d20T2KI5C8!X009sH0T2Lzdz*l} zLEu(}!(K_?)>`2JUJpy2f9vNjrkdOWx2`<<$uF~)%11t?Gy7hV<(c6F9zo#N(0GE3 z>=HP?9cLi%0@uo74_N%aL@tWtH*%f)NiLG_$=Bo>{r57NCe!yeQq&FvKmY_l00ck) z1V8`;KmY_l00jPF0!<>nmKUvYd?Pu3!YSLXk3&=mmJ&AIiW>3=&WfUW;`H(ln)t1VwP|K7ZrCUJlO2!H?xfB*=900@8p2!O!d zL*Ss`mbrD&zFqugufG!ZY;SWmQW0#-O@>3^Xz8;{q3-fKM?%r^>=~uAI~Lg#>x!;m zi*~ZuVo|3??H@Hf*el9hf1+;-uX+8Y(C&g?O4klK*6V96 z>3TR8c3fX>!+LpzMb%VflS}5}!+qO%)$3oT9`Xe<(qg(G+P MDFJZ_Gu2M~1s$?7oB#j- literal 0 HcmV?d00001 diff --git a/scripts/json数组元素字段拼接.py b/scripts/json数组元素字段拼接.py new file mode 100644 index 0000000..bfe5302 --- /dev/null +++ b/scripts/json数组元素字段拼接.py @@ -0,0 +1,34 @@ +import json +import re + +def process(input_strings: list) -> list: + """解析 JSON 数组,提取用户指定的字段并按模板拼接""" + json_str, template = input_strings[0], input_strings[1] + try: + # 解析 JSON + data = json.loads(json_str) + if not isinstance(data, dict) or "data" not in data or not isinstance(data["data"], list): + return ["错误:JSON 格式无效,缺少 'data' 数组或不是数组"] + + # 提取模板中的字段名(形如 $field$) + fields = re.findall(r'\$([^\$]+)\$', template) + if not fields: + return ["错误:模板中未找到有效字段(格式如 $field$)"] + + # 拼接结果 + result = [] + for item in data["data"]: + # 替换模板中的字段 + formatted = template + for field in fields: + # 获取字段值,缺失时用 "N/A" + value = str(item.get(field, "N/A")) + formatted = formatted.replace(f"${field}$", value) + result.append(formatted) + + # 返回拼接字符串 + return [",".join(result)] if result else ["无有效数据"] + except json.JSONDecodeError: + return ["错误:无效的 JSON 字符串"] + except Exception as e: + return [f"错误:{str(e)}"] \ No newline at end of file diff --git a/scripts/json数组元素字段拼接.ui.py b/scripts/json数组元素字段拼接.ui.py new file mode 100644 index 0000000..15c128a --- /dev/null +++ b/scripts/json数组元素字段拼接.ui.py @@ -0,0 +1,111 @@ +import tkinter as tk +from tkinter import ttk +import pyperclip +from tkinter import messagebox +import json + +def create_ui(parent, run_callback): + """创建前端界面:JSON 输入、字段选择、模板输入、输出显示""" + frame = ttk.Frame(parent) + frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + frame.columnconfigure(1, weight=1) + frame.rowconfigure(1, weight=1) + frame.rowconfigure(4, weight=1) + + # 描述 + ttk.Label(frame, text="解析 JSON 数组,按模板拼接指定字段(如 $field$:OJ:$field2$)").grid( + row=0, column=0, columnspan=3, sticky=tk.W, pady=5 + ) + + # JSON 输入 + ttk.Label(frame, text="输入 JSON:").grid(row=1, column=0, sticky=tk.NW, pady=2) + input_text = tk.Text(frame, height=5, width=50) + input_text.grid(row=1, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) + input_scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=input_text.yview) + input_scroll.grid(row=1, column=3, sticky=(tk.N, tk.S)) + input_text["yscrollcommand"] = input_scroll.set + + # 字段选择 + ttk.Label(frame, text="可用字段:").grid(row=2, column=0, sticky=tk.NW, pady=2) + field_listbox = tk.Listbox(frame, height=5, selectmode=tk.SINGLE) + field_listbox.grid(row=2, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) + field_scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=field_listbox.yview) + field_scroll.grid(row=2, column=2, sticky=(tk.N, tk.S)) + field_listbox["yscrollcommand"] = field_scroll.set + + def detect_fields(): + """检测 JSON 中的字段""" + field_listbox.delete(0, tk.END) + json_str = input_text.get("1.0", tk.END).strip() + try: + data = json.loads(json_str) + if isinstance(data, dict) and "data" in data and isinstance(data["data"], list) and data["data"]: + fields = set(data["data"][0].keys()) + for field in sorted(fields): + field_listbox.insert(tk.END, field) + else: + messagebox.showerror("错误", "JSON 格式无效或缺少 'data' 数组") + except json.JSONDecodeError: + messagebox.showerror("错误", "无效的 JSON 字符串") + except Exception as e: + messagebox.showerror("错误", f"检测失败:{str(e)}") + + def add_to_template(): + """将选中的字段添加到模板""" + selected = field_listbox.curselection() + if not selected: + messagebox.showwarning("警告", "请选择一个字段") + return + field = field_listbox.get(selected[0]) + template_entry.insert(tk.END, f"${field}$") + + ttk.Button(frame, text="检测字段", command=detect_fields).grid(row=3, column=1, sticky=tk.W, pady=2) + ttk.Button(frame, text="添加到模板", command=add_to_template).grid(row=3, column=1, sticky=tk.E, pady=2) + + # 模板输入 + ttk.Label(frame, text="拼接模板:").grid(row=4, column=0, sticky=tk.NW, pady=2) + template_entry = ttk.Entry(frame) + template_entry.grid(row=4, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=2) + + # 输出 + ttk.Label(frame, text="输出结果:").grid(row=5, column=0, sticky=tk.NW, pady=2) + output_text = tk.Text(frame, height=5, width=50, state="disabled") + output_text.grid(row=5, column=1, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) + output_scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=output_text.yview) + output_scroll.grid(row=5, column=3, sticky=(tk.N, tk.S)) + output_text["yscrollcommand"] = output_scroll.set + + def copy_output(event): + output = output_text.get("1.0", tk.END).strip() + if output: + pyperclip.copy(output) + messagebox.showinfo("提示", "输出已复制到剪贴板") + + output_text.bind("", copy_output) + + # 运行按钮 + def run(): + json_str = input_text.get("1.0", tk.END).strip() + template = template_entry.get().strip() + if not json_str or not template: + messagebox.showerror("错误", "请输入 JSON 和模板") + return + result = run_callback([json_str, template]) + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + output_text.insert("1.0", result[0] if result else "无结果") + output_text.configure(state="disabled") + + # 清空按钮 + def clear(): + input_text.delete("1.0", tk.END) + template_entry.delete(0, tk.END) + field_listbox.delete(0, tk.END) + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + output_text.configure(state="disabled") + + ttk.Button(frame, text="运行脚本", command=run).grid(row=6, column=0, pady=5) + ttk.Button(frame, text="清空", command=clear).grid(row=6, column=1, sticky=tk.W, pady=5) + + return frame \ No newline at end of file diff --git a/scripts/多个字符串拼接为一个.py b/scripts/多个字符串拼接为一个.py new file mode 100644 index 0000000..81ea9c2 --- /dev/null +++ b/scripts/多个字符串拼接为一个.py @@ -0,0 +1,45 @@ +def process(input_strings: list) -> list: + """将多个字符串按位置拼接为 str1:str2:str3,...,支持自定义输入和输出分隔符""" + try: + if len(input_strings) < 4: + return ["错误:请至少输入两个字符串、一个输入分隔符和一个输出分隔符"] + + # 提取分隔符 + output_separator = input_strings[-1].strip() # 输出分隔符 + input_separator = input_strings[-2].strip() # 输入分隔符 + if not input_separator or not output_separator: + return ["错误:输入和输出分隔符不能为空"] + # 支持换行符 + if input_separator == "\\n": + input_separator = "\n" + if output_separator == "\\n": + output_separator = "\n" + + # 输入字符串(除去最后两个分隔符) + input_strings = input_strings[:-2] + if len(input_strings) < 2: + return ["错误:请至少输入两个字符串"] + + # 分割每个字符串为列表 + element_lists = [] + for s in input_strings: + if not s.strip(): + return ["错误:输入字符串不能为空"] + elements = [e.strip() for e in s.split(input_separator) if e.strip()] + element_lists.append(elements) + + # 检查元素数量是否一致 + length = len(element_lists[0]) + if not all(len(elements) == length for elements in element_lists): + return ["错误:所有字符串的元素数量必须相同"] + + # 按位置拼接 + result = [] + for i in range(length): + concatenated = ":".join(elements[i] for elements in element_lists) + result.append(concatenated) + + # 使用指定的输出分隔符连接结果 + return [output_separator.join(result)] + except Exception as e: + return [f"错误:{str(e)}"] \ No newline at end of file diff --git a/scripts/多个字符串拼接为一个.ui.py b/scripts/多个字符串拼接为一个.ui.py new file mode 100644 index 0000000..817d81a --- /dev/null +++ b/scripts/多个字符串拼接为一个.ui.py @@ -0,0 +1,144 @@ +import tkinter as tk +from tkinter import ttk +import pyperclip +from tkinter import messagebox + +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(0, weight=1) + frame.rowconfigure(1, weight=1) + + # 描述 + ttk.Label(frame, text="将多个字符串按位置拼接为 str1:str2:str3,...,支持自定义输入分隔符和输出分隔符(如 \\n 表示换行)").grid( + row=0, column=0, columnspan=3, sticky=tk.W, pady=5 + ) + + # 可滚动区域,用于输入框 + canvas = tk.Canvas(frame) + canvas.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S)) + scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview) + scrollbar.grid(row=1, column=3, sticky=(tk.N, tk.S)) + canvas.configure(yscrollcommand=scrollbar.set) + + # 内部框架,包含输入框 + inner_frame = ttk.Frame(canvas) + canvas_frame_id = canvas.create_window((0, 0), window=inner_frame, anchor=tk.NW) + + def update_scrollregion(event=None): + """更新滚动区域""" + canvas.configure(scrollregion=canvas.bbox("all")) + canvas.itemconfig(canvas_frame_id, width=canvas.winfo_width()) + + inner_frame.bind("", update_scrollregion) + + # 存储动态控件 + input_texts = [] + input_labels = [] + input_separator_entry = None + output_separator_entry = None + output_label = ttk.Label(frame, text="输出结果:") + output_text = tk.Text(frame, height=4, width=50, state="disabled") + output_scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=output_text.yview) + add_button = None + run_button = None + clear_button = None + + def add_input_field(): + """添加新的多行输入框,无数量限制""" + nonlocal output_label, output_text, output_scroll, add_button, run_button, clear_button, input_separator_entry, output_separator_entry + row = len(input_texts) + label = ttk.Label(inner_frame, text=f"字符串{row + 1}:") + label.grid(row=row, column=0, sticky=tk.NW, pady=2) + text = tk.Text(inner_frame, height=3, width=50) + text.grid(row=row, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=2) + input_labels.append(label) + input_texts.append(text) + inner_frame.columnconfigure(1, weight=1) + # 更新滚动区域 + update_scrollregion() + + # 初始化两个输入框 + for _ in range(2): + add_input_field() + + # 输入分隔符 + input_separator_label = ttk.Label(frame, text="输入分隔符(\\n 表示换行):") + input_separator_label.grid(row=2, column=0, sticky=tk.W, pady=2) + input_separator_entry = ttk.Entry(frame, width=5) + input_separator_entry.insert(0, ",") # 默认逗号 + input_separator_entry.grid(row=2, column=1, sticky=tk.W, pady=2) + + # 输出分隔符 + output_separator_label = ttk.Label(frame, text="输出分隔符(\\n 表示换行):") + output_separator_label.grid(row=3, column=0, sticky=tk.W, pady=2) + output_separator_entry = ttk.Entry(frame, width=5) + output_separator_entry.insert(0, ",") # 默认逗号 + output_separator_entry.grid(row=3, column=1, sticky=tk.W, pady=2) + + # 输出框配置 + output_label.grid(row=4, column=0, sticky=tk.NW, pady=2) + output_text.grid(row=4, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) + output_scroll.grid(row=4, column=2, sticky=(tk.N, tk.S)) + output_text["yscrollcommand"] = output_scroll.set + + def copy_output(event): + output = output_text.get("1.0", tk.END).strip() + if output: + pyperclip.copy(output) + messagebox.showinfo("提示", "输出已复制到剪贴板") + + output_text.bind("", copy_output) + + # 添加字符串按钮 + add_button = ttk.Button(frame, text="添加字符串", command=add_input_field) + add_button.grid(row=5, column=0, pady=5) + + # 运行按钮 + def run(): + inputs = [text.get("1.0", tk.END).rstrip("\n") for text in input_texts] + input_separator = input_separator_entry.get().strip() + output_separator = output_separator_entry.get().strip() + if len(inputs) < 2 or not all(inputs): + messagebox.showerror("错误", "请至少输入两个非空字符串") + return + if not input_separator or not output_separator: + messagebox.showerror("错误", "请输入输入和输出分隔符") + return + # 传递字符串和分隔符 + result = run_callback(inputs + [input_separator, output_separator]) + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + output_text.insert("1.0", result[0] if result else "无结果") + output_text.configure(state="disabled") + + # 清空按钮 + def clear(): + nonlocal output_label, output_text, output_scroll, add_button, run_button, clear_button, input_separator_entry, output_separator_entry + # 移除多余输入框,保留两个 + while len(input_texts) > 2: + input_texts.pop().destroy() + input_labels.pop().destroy() + # 清空输入和输出 + for text in input_texts: + text.delete("1.0", tk.END) + input_separator_entry.delete(0, tk.END) + input_separator_entry.insert(0, ",") # 重置输入分隔符 + output_separator_entry.delete(0, tk.END) + output_separator_entry.insert(0, ",") # 重置输出分隔符 + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + output_text.configure(state="disabled") + # 重置布局 + for i, (label, text) in enumerate(zip(input_labels, input_texts)): + label.grid(row=i, column=0, sticky=tk.NW, pady=2) + text.grid(row=i, column=1, columnspan=2, sticky=(tk.W, tk.E), pady=2) + update_scrollregion() + + run_button = ttk.Button(frame, text="运行脚本", command=run) + run_button.grid(row=6, column=0, pady=5) + clear_button = ttk.Button(frame, text="清空", command=clear) + clear_button.grid(row=6, column=1, sticky=tk.W, pady=5) + + return frame \ No newline at end of file diff --git a/scripts/时间戳.py b/scripts/时间戳.py new file mode 100644 index 0000000..094bbd5 --- /dev/null +++ b/scripts/时间戳.py @@ -0,0 +1,59 @@ +from datetime import datetime +import pytz +from typing import List + +# 预缓存常用时区 +TIMEZONE_CACHE = { + "Asia/Shanghai": pytz.timezone("Asia/Shanghai"), + "UTC": pytz.timezone("UTC"), + "America/New_York": pytz.timezone("America/New_York"), + "Europe/London": pytz.timezone("Europe/London"), + "Australia/Sydney": pytz.timezone("Australia/Sydney") +} + +def process(input_strings: List[str]) -> List[str]: + """处理时间戳和文本的相互转换""" + try: + if len(input_strings) != 3: + return ["错误:输入格式不正确,期望 [mode, input, timezone]"] + mode, input_str, timezone = input_strings + + # 从缓存获取时区 + tz = TIMEZONE_CACHE.get(timezone) + if not tz: + return ["错误:无效的时区"] + + if mode == "timestamp_to_text": + try: + timestamp = int(input_str) + dt = datetime.fromtimestamp(timestamp, tz=tz) + return [dt.strftime("%Y-%m-%d %H:%M:%S %Z")] + except ValueError: + return ["错误:请输入有效的时间戳"] + + elif mode == "text_to_timestamp": + try: + # 检查是否以 UTC 结尾 + input_str = input_str.strip() + use_utc = input_str.endswith(" UTC") + if use_utc: + input_str = input_str[:-4].strip() # 移除 " UTC" + tz = TIMEZONE_CACHE["UTC"] # 强制使用 UTC 时区 + + # 尝试解析三种格式 + try: + dt = datetime.strptime(input_str, "%Y-%m-%d %H:%M:%S") + except ValueError: + try: + dt = datetime.strptime(input_str, "%Y年%m月%d日 %H:%M:%S") + except ValueError: + # 解析新格式:MMM-DD-YYYY HH:MM:SS AM/PM + dt = datetime.strptime(input_str, "%b-%d-%Y %I:%M:%S %p") + dt = tz.localize(dt) + return [str(int(dt.timestamp()))] + except ValueError: + return ["错误:请输入有效的日期格式(YYYY-MM-DD HH:MM:SS 或 YYYY年MM月DD日 HH:MM:SS 或 MMM-DD-YYYY HH:MM:SS AM/PM)"] + + return ["错误:无效的转换模式"] + except Exception as e: + return [f"错误:{str(e)}"] \ No newline at end of file diff --git a/scripts/时间戳.ui.py b/scripts/时间戳.ui.py new file mode 100644 index 0000000..ec313d8 --- /dev/null +++ b/scripts/时间戳.ui.py @@ -0,0 +1,95 @@ +import tkinter as tk +from tkinter import ttk +import pyperclip +from tkinter import messagebox +from datetime import datetime + +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) + + # 时区选项 + timezones = ["Asia/Shanghai", "UTC", "America/New_York", "Europe/London", "Australia/Sydney"] + + # 上半部分:时间戳转文本 + timestamp_frame = ttk.LabelFrame(frame, text="时间戳转文本", padding=5) + timestamp_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5) + timestamp_frame.columnconfigure(1, weight=1) + + ttk.Label(timestamp_frame, text="时间戳:").grid(row=0, column=0, sticky=tk.W, pady=2) + timestamp_entry = ttk.Entry(timestamp_frame) + timestamp_entry.insert(0, str(int(datetime.now().timestamp()))) # 默认当前时间戳 + timestamp_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2) + + ttk.Label(timestamp_frame, text="时区:").grid(row=1, column=0, sticky=tk.W, pady=2) + timestamp_tz_var = tk.StringVar(value="Asia/Shanghai") + ttk.Combobox(timestamp_frame, textvariable=timestamp_tz_var, values=timezones, state="readonly").grid(row=1, column=1, sticky=tk.W, pady=2) + + ttk.Label(timestamp_frame, text="结果:").grid(row=2, column=0, sticky=tk.W, pady=2) + timestamp_result = ttk.Entry(timestamp_frame, state="readonly") + timestamp_result.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=2) + + def copy_timestamp_result(): + output = timestamp_result.get() + if output: + pyperclip.copy(output) + messagebox.showinfo("提示", "结果已复制到剪贴板") + + def convert_timestamp(): + timestamp = timestamp_entry.get().strip() + if not timestamp: + messagebox.showerror("错误", "请输入时间戳") + return + result = run_callback(["timestamp_to_text", timestamp, timestamp_tz_var.get()]) + timestamp_result.configure(state="normal") + timestamp_result.delete(0, tk.END) + timestamp_result.insert(0, result[0] if result else "无结果") + timestamp_result.configure(state="readonly") + + ttk.Button(timestamp_frame, text="转换", command=convert_timestamp).grid(row=3, column=0, columnspan=2, pady=5) + timestamp_result.bind("", lambda e: copy_timestamp_result()) # 双击复制 + + # 分割线 + ttk.Separator(frame, orient=tk.HORIZONTAL).grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=10) + + # 下半部分:文本转时间戳 + text_frame = ttk.LabelFrame(frame, text="文本转时间戳", padding=5) + text_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=5) + text_frame.columnconfigure(1, weight=1) + + ttk.Label(text_frame, text="日期时间:").grid(row=0, column=0, sticky=tk.W, pady=2) + text_entry = ttk.Entry(text_frame) + text_entry.insert(0, "2025-06-05 16:29:11") # 默认时间 + text_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=2) + + ttk.Label(text_frame, text="时区:").grid(row=1, column=0, sticky=tk.W, pady=2) + text_tz_var = tk.StringVar(value="Asia/Shanghai") + ttk.Combobox(text_frame, textvariable=text_tz_var, values=timezones, state="readonly").grid(row=1, column=1, sticky=tk.W, pady=2) + + ttk.Label(text_frame, text="结果:").grid(row=2, column=0, sticky=tk.W, pady=2) + text_result = ttk.Entry(text_frame, state="readonly") + text_result.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=2) + + def copy_text_result(): + output = text_result.get() + if output: + pyperclip.copy(output) + messagebox.showinfo("提示", "结果已复制到剪贴板") + + def convert_text(): + text = text_entry.get().strip() + if not text: + messagebox.showerror("错误", "请输入日期时间") + return + result = run_callback(["text_to_timestamp", text, text_tz_var.get()]) + text_result.configure(state="normal") + text_result.delete(0, tk.END) + text_result.insert(0, result[0] if result else "无结果") + text_result.configure(state="readonly") + + ttk.Button(text_frame, text="转换", command=convert_text).grid(row=3, column=0, columnspan=2, pady=5) + text_result.bind("", lambda e: copy_text_result()) # 双击复制 + + return frame \ No newline at end of file diff --git a/scripts/比较两个字符串的差异.py b/scripts/比较两个字符串的差异.py new file mode 100644 index 0000000..15f8d18 --- /dev/null +++ b/scripts/比较两个字符串的差异.py @@ -0,0 +1,98 @@ +def process(input_strings: list) -> list: + """比较两个字符串的元素差异,支持自定义分隔符、数值比较和忽略微小差异""" + try: + if len(input_strings) != 5: + return ["错误:输入格式不正确,期望 [str1, str2, separator, numeric_compare, ignore_tiny_diff]"] + str1, str2, separator, numeric_compare, ignore_tiny_diff = input_strings + numeric_compare = numeric_compare.lower() == "true" + ignore_tiny_diff = ignore_tiny_diff.lower() == "true" + + if not str1 or not str2: + return ["错误:请输入两个字符串"] + + if separator == "\\n": + elements1 = [e.strip() for e in str1.splitlines() if e.strip()] + elements2 = [e.strip() for e in str2.splitlines() if e.strip()] + else: + elements1 = [e.strip() for e in str1.split(separator) if e.strip()] + elements2 = [e.strip() for e in str2.split(separator) if e.strip()] + + result = [] + missing_in_1 = [] + missing_in_2 = [] + + if numeric_compare: + try: + nums1 = [(float(e), i + 1) for i, e in enumerate(elements1) if e.strip()] + nums2 = [(float(e), i + 1) for i, e in enumerate(elements2) if e.strip()] + + if ignore_tiny_diff: + TOLERANCE = 1e-6 + set1 = set() + set2 = set() + for val, idx in nums1: + found = False + for existing_val in set1: + if abs(existing_val - val) < TOLERANCE: + found = True + break + if not found: + set1.add(val) + for val, idx in nums2: + found = False + for existing_val in set2: + if abs(existing_val - val) < TOLERANCE: + found = True + break + if not found: + set2.add(val) + for val, idx in nums2: + matched = False + for v1 in set1: + if abs(v1 - val) < TOLERANCE: + matched = True + break + if not matched: + missing_in_1.append((idx, f"{val:.6f} (位置 {idx})")) + for val, idx in nums1: + matched = False + for v2 in set2: + if abs(v2 - val) < TOLERANCE: + matched = True + break + if not matched: + missing_in_2.append((idx, f"{val:.6f} (位置 {idx})")) + else: + set1 = {n[0] for n in nums1} + set2 = {n[0] for n in nums2} + for val, idx in nums2: + if val not in set1: + missing_in_1.append((idx, f"{val:.6f} (位置 {idx})")) + for val, idx in nums1: + if val not in set2: + missing_in_2.append((idx, f"{val:.6f} (位置 {idx})")) + except ValueError: + return ["错误:输入包含无法转换为数值的元素"] + else: + set1, set2 = set(elements1), set(elements2) + for i, e in enumerate(elements2, 1): + if e not in set1: + missing_in_1.append((i, f"{e} (位置 {i})")) + for i, e in enumerate(elements1, 1): + if e not in set2: + missing_in_2.append((i, f"{e} (位置 {i})")) + + if missing_in_1: + result.append("字符串1相对于字符串2缺少:") + result.extend(item[1] for item in sorted(missing_in_1, key=lambda x: x[0])) + else: + result.append("字符串1相对于字符串2没少") + if missing_in_2: + result.append("字符串2相对于字符串1缺少:") + result.extend(item[1] for item in sorted(missing_in_2, key=lambda x: x[0])) + else: + result.append("字符串2相对于字符串1没少") + + return ["\n".join(result)] + except Exception as e: + return [f"错误:{str(e)}"] \ No newline at end of file diff --git a/scripts/比较两个字符串的差异.ui.py b/scripts/比较两个字符串的差异.ui.py new file mode 100644 index 0000000..8f6e936 --- /dev/null +++ b/scripts/比较两个字符串的差异.ui.py @@ -0,0 +1,94 @@ +import tkinter as tk +from tkinter import ttk +from tkinter import messagebox + +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) + frame.rowconfigure(5, weight=1) + + ttk.Label(frame, text="比较两个字符串的元素差异").grid( + row=0, column=0, columnspan=2, sticky=tk.W, pady=5 + ) + + ttk.Label(frame, text="字符串1:").grid(row=1, column=0, sticky=tk.W, pady=2) + input_entry1 = tk.Text(frame, height=4, width=50) + input_entry1.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=2) + + ttk.Label(frame, text="字符串2:").grid(row=2, column=0, sticky=tk.W, pady=2) + input_entry2 = tk.Text(frame, height=4, width=50) + input_entry2.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=2) + + ttk.Label(frame, text="分隔符:").grid(row=3, column=0, sticky=tk.W, pady=2) + separator_var = tk.StringVar(value=",") + separator_menu = ttk.OptionMenu( + frame, + separator_var, + ",", + ",", ";", "\\n", "空格" + ) + separator_menu.grid(row=3, column=1, sticky=tk.W, pady=2) + + numeric_var = tk.BooleanVar(value=False) + ttk.Checkbutton(frame, text="启用数值比较", variable=numeric_var).grid( + row=3, column=1, sticky=tk.E, pady=2 + ) + + ignore_tiny_diff_var = tk.BooleanVar(value=False) + ignore_tiny_diff_check = ttk.Checkbutton( + frame, text="忽略微小差异", variable=ignore_tiny_diff_var, + command=lambda: numeric_var.set(True) if ignore_tiny_diff_var.get() else None + ) + ignore_tiny_diff_check.grid(row=4, column=1, sticky=tk.E, pady=2) + + ttk.Label(frame, text="比较结果:").grid(row=5, column=0, sticky=tk.NW, pady=2) + output_text = tk.Text(frame, height=4, width=50, state="disabled") + output_text.grid(row=5, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), pady=2) + output_scroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=output_text.yview) + output_scroll.grid(row=5, column=2, sticky=(tk.N, tk.S)) + output_text["yscrollcommand"] = output_scroll.set + + def run(): + input1 = input_entry1.get("1.0", tk.END).strip() + input2 = input_entry2.get("1.0", tk.END).strip() + if not input1 or not input2: + messagebox.showerror("错误", "请输入两个字符串") + return + separator = separator_var.get() + if separator == "空格": + separator = " " + try: + input_list = [ + input1, + input2, + separator, + str(numeric_var.get()).lower(), + str(ignore_tiny_diff_var.get()).lower() + ] + result = run_callback(input_list) + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + if result and result[0]: + output_text.insert("1.0", result[0]) + else: + output_text.insert("1.0", "无结果") + output_text.configure(state="disabled") + except Exception as e: + messagebox.showerror("错误", f"运行脚本失败:{str(e)}") + + def clear(): + input_entry1.delete("1.0", tk.END) + input_entry2.delete("1.0", tk.END) + output_text.configure(state="normal") + output_text.delete("1.0", tk.END) + output_text.configure(state="disabled") + numeric_var.set(False) + ignore_tiny_diff_var.set(False) + separator_var.set(",") + + ttk.Button(frame, text="运行脚本", command=run).grid(row=6, column=0, pady=5) + ttk.Button(frame, text="清空", command=clear).grid(row=6, column=1, sticky=tk.W, pady=5) + + return frame \ No newline at end of file