|
0608修改 优化代码
本帖最后由 piaomusic 于 2025-6-8 22:51 编辑
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import configparser
import os
import sys
import ctypes
import socket
import logging
import webbrowser
import subprocess
from threading import Thread, Lock
from ttkbootstrap import Style
from PIL import Image, ImageTk
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='tool.log',
encoding='utf-8'
)
logger = logging.getLogger(__name__)
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except Exception:
return False
def get_local_ip():
try:
hostname = socket.gethostname()
ip_list = socket.gethostbyname_ex(hostname)[2]
valid_ips = [ip for ip in ip_list if not ip.startswith('127.') and not ip.startswith('169.254.')]
return valid_ips[0] if valid_ips else (ip_list[0] if ip_list else "127.0.0.1")
except Exception as e:
logger.error(f"获取内网IP失败: {str(e)}")
return "127.0.0.1"
def center_window(window, width, height):
window.update_idletasks()
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
window.geometry(f'{width}x{height}+{x}+{y}')
class ToolboxApp:
DEFAULT_CONFIG = {
'title_font': 'Microsoft YaHei', 'nav_font': 'Microsoft YaHei', 'button_font': 'Microsoft YaHei',
'header_bg': '#2c3e50', 'header_fg': '#ecf0f1', 'nav_bg': '#f8f9fa', 'content_bg': '#ffffff',
'button_bg': '#ecf0f1', 'button_fg': '#000000', 'active_button_bg': '#d1d8e0',
'icon_text_padding': 5, 'icon_size': 32, 'window_width': 800, 'window_height': 600,
'tools_per_row': 3, 'title': '高级工具箱', 'title_font_size': 14, 'nav_font_size': 12,
'button_font_size': 10, 'ip_font_size': 10, 'title_bar_height': 50, 'nav_bar_width': 150,
'nav_button_spacing': 5, 'nav_button_top_margin': 10, 'button_width': 20,
'button_padx': 5, 'button_pady': 5
}
CONFIG_MAPPING = {
'window_width': '窗口宽度', 'window_height': '窗口高度', 'tools_per_row': '每行工具数',
'title': '窗口标题', 'title_font_size': '主标题字号', 'nav_font_size': '导航栏字号',
'button_font_size': '按钮字号', 'ip_font_size': 'IP字号', 'title_bar_height': '标题栏高度',
'nav_bar_width': '导航栏宽度', 'nav_button_spacing': '导航按钮间距',
'nav_button_top_margin': '导航栏顶部间距', 'icon_size': '图标大小', 'button_width': '按钮宽度',
'button_padx': '水平间距', 'button_pady': '垂直间距'
}
PROTECTED_METADATA = {
'作者': '缘起性空', '版本': '3.0', '联系': '32897251@qq.com', '说明': '本工具仅供技术交流使用'
}
def __init__(self, master):
self.master = master
self.base_path = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
self.config = self.DEFAULT_CONFIG.copy()
self.tools = {}
self.icons = {}
self.running_tools = {}
self.tool_lock = Lock()
self.current_section = None
self.selected_button = None
self.selected_button_name = None
self._last_config_mtime = None
self._initialize_app()
def _initialize_app(self):
try:
self.master.withdraw()
self._ensure_metadata()
self._load_config()
self._setup_ui()
self.master.after(100, self._finalize_ui)
except Exception as e:
logger.critical(f"应用程序初始化失败: {str(e)}")
messagebox.showerror("错误", f"程序初始化失败: {str(e)}")
sys.exit(1)
def _ensure_metadata(self):
config_path = os.path.join(self.base_path, 'config.ini')
if not os.path.exists(config_path):
self._write_protected_metadata(config_path)
return
parser = configparser.ConfigParser()
parser.read(config_path, encoding='utf-8')
needs_update = any(
not parser.has_option('程序信息', key) or parser.get('程序信息', key) != value
for key, value in self.PROTECTED_METADATA.items()
)
if needs_update:
self._write_protected_metadata(config_path, parser)
def _write_protected_metadata(self, config_path, old_parser=None):
try:
parser = configparser.ConfigParser()
if old_parser:
for section in old_parser.sections():
if section != '程序信息':
parser.add_section(section)
for key, value in old_parser.items(section):
parser.set(section, key, value)
with open(config_path, 'w', encoding='utf-8') as f:
f.write("[程序信息]\n")
for key, value in self.PROTECTED_METADATA.items():
f.write(f"{key} = {value}\n")
f.write("\n")
if '界面设置' in parser:
f.write("[界面设置]\n")
for key, value in parser.items('界面设置'):
f.write(f"{key} = {value}\n")
f.write("\n")
for section in parser.sections():
if section not in {'程序信息', '界面设置'}:
f.write(f"[{section}]\n")
for key, value in parser.items(section):
f.write(f"{key} = {value}\n")
f.write("\n")
logger.info("配置文件已更新")
except Exception as e:
logger.error(f"写入配置文件失败: {str(e)}")
raise RuntimeError(f"无法写入配置文件: {str(e)}")
def _finalize_ui(self):
center_window(self.master, self.config['window_width'], self.config['window_height'])
self._update_ip_display()
self.master.deiconify()
if self.tools:
self.master.after(50, lambda: self.show_tools(next(iter(self.tools)), force=True))
def _load_config(self):
config_path = os.path.join(self.base_path, 'config.ini')
if not os.path.exists(config_path):
logger.warning(f"配置文件不存在: {config_path}, 使用默认配置")
return
current_mtime = os.path.getmtime(config_path)
if self._last_config_mtime == current_mtime:
return
self._last_config_mtime = current_mtime
parser = configparser.ConfigParser()
parser.read(config_path, encoding='utf-8')
if '界面设置' in parser:
for internal_key, chinese_key in self.CONFIG_MAPPING.items():
if parser.has_option('界面设置', chinese_key):
try:
value = parser.get('界面设置', chinese_key)
if internal_key in ['window_width', 'window_height', 'tools_per_row',
'title_font_size', 'nav_font_size', 'button_font_size',
'ip_font_size', 'title_bar_height', 'nav_bar_width',
'nav_button_spacing', 'nav_button_top_margin',
'icon_size', 'button_width',
'button_padx', 'button_pady']:
self.config[internal_key] = int(value)
else:
self.config[internal_key] = value
except ValueError:
logger.warning(f"配置项 {chinese_key} 格式错误, 使用默认值")
self._load_tools_config(parser)
def _load_tools_config(self, parser):
self.tools.clear()
for section in parser.sections():
if section in {'程序信息', '界面设置'}:
continue
self.tools[section] = []
for name, cmd in parser.items(section):
parts = cmd.split('|')
tool_config = (
name.strip(),
parts[0].strip(),
parts[1].strip() if len(parts) > 1 else "运行",
parts[2].strip() if len(parts) > 2 and parts[2].strip() else None
)
self.tools[section].append(tool_config)
if not self.tools:
logger.warning("配置文件中没有定义任何工具分类")
def _save_config(self):
config_path = os.path.join(self.base_path, 'config.ini')
try:
old_parser = configparser.ConfigParser()
if os.path.exists(config_path):
old_parser.read(config_path, encoding='utf-8')
with open(config_path, 'w', encoding='utf-8') as f:
f.write("[程序信息]\n")
for key, value in self.PROTECTED_METADATA.items():
f.write(f"{key} = {value}\n")
f.write("\n[界面设置]\n")
for internal_key, chinese_key in self.CONFIG_MAPPING.items():
if internal_key in self.config:
f.write(f"{chinese_key} = {self.config[internal_key]}\n")
f.write("\n")
for section, tools in self.tools.items():
f.write(f"[{section}]\n")
for name, cmd, btn_text, icon_path in tools:
value = f"{cmd}|{btn_text}"
if icon_path:
value += f"|{icon_path}"
f.write(f"{name} = {value}\n")
f.write("\n")
self._last_config_mtime = os.path.getmtime(config_path)
logger.info("配置文件已保存")
except Exception as e:
logger.error(f"保存配置文件失败: {str(e)}")
messagebox.showerror("错误", f"保存配置文件失败: {str(e)}")
def _setup_ui(self):
self.style = Style(theme="minty")
self._configure_styles()
self.master.title(self.config['title'])
main_frame = ttk.Frame(self.master)
main_frame.pack(fill=tk.BOTH, expand=True)
self._setup_header(main_frame)
self._setup_body(main_frame)
self._setup_button_context_menu()
if sys.platform == 'win32':
self.master.tk.call('tk', 'scaling', 1.5)
self.master.option_add('*tearOff', False)
def _setup_header(self, parent):
header_frame = ttk.Frame(parent, height=self.config['title_bar_height'], style='Header.TFrame')
header_frame.pack(fill=tk.X)
header_frame.pack_propagate(False)
ttk.Label(header_frame, text=self.config['title'], style='Header.TLabel').pack(side=tk.LEFT, padx=20)
info_frame = ttk.Frame(header_frame, style='Header.TFrame')
info_frame.pack(side=tk.RIGHT, padx=10)
self.ip_label = ttk.Label(info_frame, text="IP: 获取中...", style='IP.TLabel')
self.ip_label.pack(side=tk.LEFT, padx=5)
ttk.Button(info_frame, text="⚙", command=self._show_toolbox_manager, style='NoHover.TButton').pack(side=tk.LEFT, padx=5)
ttk.Button(info_frame, text="⭐", command=self.show_about, style='NoHover.TButton').pack(side=tk.LEFT, padx=5)
def _show_toolbox_manager(self):
ToolboxManager(self.master, os.path.join(self.base_path, 'config.ini'), self._reload_config)
def _reload_config(self):
current_section = self.current_section
self._load_config()
for widget in self.nav_frame.winfo_children():
widget.destroy()
ttk.Frame(self.nav_frame, height=self.config['nav_button_top_margin'], style='Nav.TFrame').pack()
for section in self.tools.keys():
btn = ttk.Button(self.nav_frame, text=section, command=lambda s=section: self.show_tools(s, force=True), style='Nav.TButton')
btn.pack(fill=tk.X, padx=5, pady=self.config['nav_button_spacing'])
if current_section in self.tools:
self.show_tools(current_section, force=True)
elif self.tools:
self.show_tools(next(iter(self.tools)), force=True)
else:
self._clear_content_frame()
self.current_section = None
def _setup_body(self, parent):
body_frame = ttk.Frame(parent)
body_frame.pack(fill=tk.BOTH, expand=True)
self._setup_navigation(body_frame)
self.content_frame = ttk.Frame(body_frame, style='Content.TFrame')
self.content_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)
def _setup_navigation(self, parent):
self.nav_frame = ttk.Frame(parent, width=self.config['nav_bar_width'], style='Nav.TFrame')
self.nav_frame.pack(side=tk.LEFT, fill=tk.Y)
self.nav_frame.pack_propagate(False)
ttk.Frame(self.nav_frame, height=self.config['nav_button_top_margin'], style='Nav.TFrame').pack()
for section in self.tools.keys():
btn = ttk.Button(self.nav_frame, text=section, command=lambda s=section: self.show_tools(s, force=True), style='Nav.TButton')
btn.pack(fill=tk.X, padx=5, pady=self.config['nav_button_spacing'])
def _setup_button_context_menu(self):
self.button_menu = tk.Menu(self.master, tearoff=0)
self.button_menu.add_command(label="运行", command=self._run_selected_tool)
self.button_menu.add_command(label="重命名", command=self._rename_tool)
self.button_menu.add_command(label="设置图标", command=self._set_tool_icon)
self.button_menu.add_command(label="删除图标", command=self._remove_tool_icon)
self.button_menu.add_separator()
self.button_menu.add_command(label="删除", command=self._delete_tool)
self.master.bind("<Button-1>", self._clear_selection)
def _configure_styles(self):
self.style.configure('Header.TFrame', background=self.config['header_bg'])
self.style.configure('Header.TLabel', background=self.config['header_bg'],
foreground=self.config['header_fg'],
font=(self.config['title_font'], self.config['title_font_size'], 'bold'))
self.style.configure('IP.TLabel', background=self.config['header_bg'],
foreground=self.config['header_fg'],
font=(self.config['button_font'], self.config['ip_font_size']))
self.style.configure('Nav.TFrame', background=self.config['nav_bg'])
self.style.configure('Content.TFrame', background=self.config['content_bg'])
self.style.configure('Tool.TFrame', background=self.config['content_bg'])
self.style.configure('Nav.TButton', font=(self.config['nav_font'], self.config['nav_font_size']),
background=self.config['nav_bg'], foreground='#333333',
borderwidth=0, padding=6)
self.style.configure('Primary.TButton', font=(self.config['button_font'], self.config['button_font_size']),
background=self.config['button_bg'], foreground=self.config['button_fg'],
borderwidth=1, relief="solid",
padding=(self.config['icon_text_padding'], self.config['button_pady']),
anchor='w', width=self.config['button_width'])
self.style.map('Primary.TButton',
background=[('active', self.config['active_button_bg'])],
foreground=[('active', '#000000')])
self.style.configure('NoHover.TButton', font=(self.config['button_font'], self.config['button_font_size']),
foreground=self.config['header_fg'], background=self.config['header_bg'],
borderwidth=0, padding=2)
self.style.map('NoHover.TButton', foreground=[], background=[])
def show_tools(self, section, force=False):
if not force and self.current_section == section:
return
self._clear_content_frame()
self.current_section = section
if section not in self.tools:
return
grid_frame = ttk.Frame(self.content_frame, style='Content.TFrame')
grid_frame.pack(fill=tk.BOTH, expand=True)
self._preload_icons(section)
row, col = 0, 0
for name, cmd, btn_text, icon_path in self.tools[section]:
if col == 0:
row_frame = ttk.Frame(grid_frame, style='Tool.TFrame')
row_frame.grid(row=row, column=0, sticky="ew", pady=self.config['button_pady'])
btn = ttk.Button(row_frame, text=f" {name}", command=lambda c=cmd, n=name: self.run_tool(c, n), style='Primary.TButton')
if icon_path:
try:
icon = self._load_icon(icon_path)
btn.config(image=icon, compound=tk.LEFT, padding=(self.config['icon_text_padding'], 0, 0, 0))
btn.image = icon
except Exception as e:
logger.error(f"设置按钮图标失败: {name} - {str(e)}")
btn.grid(row=0, column=col, padx=self.config['button_padx'], pady=self.config['button_pady'], sticky="nsew")
btn.bind("<Button-3>", lambda e, b=btn, n=name: self._show_button_context_menu(e, b, n))
col += 1
if col >= self.config['tools_per_row']:
col = 0
row += 1
for i in range(self.config['tools_per_row']):
grid_frame.columnconfigure(i, weight=1)
def _preload_icons(self, section):
for _, _, _, icon_path in self.tools[section]:
if icon_path:
try:
self._load_icon(icon_path)
except:
pass
def _clear_content_frame(self):
for widget in self.content_frame.winfo_children():
if isinstance(widget, ttk.Button) and hasattr(widget, 'image'):
widget.image = None
widget.destroy()
self.master.update_idletasks()
def _load_icon(self, icon_path):
if icon_path in self.icons:
return self.icons[icon_path]
try:
if not os.path.isabs(icon_path):
icon_path = os.path.join(self.base_path, icon_path)
img = Image.open(icon_path).convert('RGBA')
img = img.resize((self.config['icon_size'], self.config['icon_size']), Image.LANCZOS)
photo = ImageTk.PhotoImage(img)
self.icons[icon_path] = photo
return photo
except Exception as e:
logger.error(f"加载图标失败: {os.path.abspath(icon_path)} - {str(e)}")
empty_img = Image.new('RGBA', (self.config['icon_size'], self.config['icon_size']), (0, 0, 0, 0))
empty_photo = ImageTk.PhotoImage(empty_img)
self.icons[icon_path] = empty_photo
return empty_photo
def run_tool(self, command, name):
if "禁用驱动签名" in name or "启用驱动签名" in name:
if not self._show_driver_signature_warning(name):
return
if command.startswith(('http://', 'https://')):
webbrowser.open(command)
else:
if command.startswith('tools/'):
command = os.path.join(self.base_path, command)
if not os.path.exists(command):
logger.error(f"工具不存在: {command}")
return
Thread(target=self._execute_command, args=(command, name), daemon=True).start()
def _execute_command(self, command, name):
try:
with self.tool_lock:
if name in self.running_tools:
logger.warning(f"工具已在运行: {name}")
return
self.running_tools[name] = True
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = subprocess.SW_HIDE
process = subprocess.Popen(command, shell=True, creationflags=subprocess.CREATE_NEW_CONSOLE, startupinfo=startupinfo)
def monitor_process():
try:
process.wait(timeout=30)
except subprocess.TimeoutExpired:
logger.warning(f"工具执行超时:{name}")
process.kill()
finally:
with self.tool_lock:
self.running_tools.pop(name, None)
Thread(target=monitor_process, daemon=True).start()
except Exception as e:
logger.error(f"执行失败: {name} - {str(e)}")
with self.tool_lock:
self.running_tools.pop(name, None)
def _show_button_context_menu(self, event, button, name):
self.selected_button = button
self.selected_button_name = name
try:
self.button_menu.tk_popup(event.x_root, event.y_root)
finally:
self.button_menu.grab_release()
def _clear_selection(self, event):
if not isinstance(event.widget, ttk.Button):
self.selected_button = None
self.selected_button_name = None
def _run_selected_tool(self):
if not self.selected_button_name or not self.current_section:
return
for name, cmd, _, _ in self.tools[self.current_section]:
if name == self.selected_button_name:
self.run_tool(cmd, name)
break
def _rename_tool(self):
if not self.selected_button_name or not self.current_section:
return
new_name = simpledialog.askstring("重命名工具", "输入新名称:", initialvalue=self.selected_button_name, parent=self.master)
if not new_name or new_name == self.selected_button_name:
return
for i, (name, cmd, btn_text, icon_path) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (new_name, cmd, btn_text, icon_path)
break
self._save_config()
self.show_tools(self.current_section, force=True)
def _set_tool_icon(self):
if not self.selected_button_name or not self.current_section:
return
icon_path = filedialog.askopenfilename(
title="选择图标文件",
filetypes=[("图标文件", "*.ico *.png *.jpg *.jpeg *.bmp"), ("所有文件", "*.*")]
)
if not icon_path:
return
try:
self._load_icon(icon_path)
for i, (name, cmd, btn_text, _) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (name, cmd, btn_text, icon_path)
break
self._save_config()
self.show_tools(self.current_section, force=True)
except Exception as e:
logger.error(f"设置图标失败: {str(e)}")
def _remove_tool_icon(self):
if not self.selected_button_name or not self.current_section:
return
for i, (name, cmd, btn_text, _) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (name, cmd, btn_text, None)
break
self._save_config()
self.show_tools(self.current_section, force=True)
def _delete_tool(self):
if not self.selected_button_name or not self.current_section:
return
if not messagebox.askyesno("确认删除", f"确定要删除工具 '{self.selected_button_name}' 吗?"):
return
self.tools[self.current_section] = [
(name, cmd, btn_text, icon_path)
for name, cmd, btn_text, icon_path in self.tools[self.current_section]
if name != self.selected_button_name
]
self._save_config()
self.show_tools(self.current_section, force=True)
def _show_driver_signature_warning(self, operation_name):
warning_dialog = tk.Toplevel(self.master)
warning_dialog.title("安全警告")
warning_dialog.resizable(False, False)
warning_dialog.transient(self.master)
warning_dialog.grab_set()
warning_dialog.configure(bg='#f8f9fa')
dialog_width, dialog_height = 400, 250
warning_dialog.geometry(f"{dialog_width}x{dialog_height}")
warning_dialog.update_idletasks()
screen_width = warning_dialog.winfo_screenwidth()
screen_height = warning_dialog.winfo_screenheight()
x = (screen_width - dialog_width) // 2
y = (screen_height - dialog_height) // 2
warning_dialog.geometry(f"+{x}+{y}")
content_frame = tk.Frame(warning_dialog, bg='#f8f9fa', padx=15, pady=15)
content_frame.pack(fill=tk.BOTH, expand=True)
title_frame = tk.Frame(content_frame, bg='#f8f9fa')
title_frame.pack(fill=tk.X, pady=(0, 10))
tk.Label(title_frame, text="⚠", font=('Arial', 20), fg='orange', bg='#f8f9fa').pack(side=tk.LEFT, padx=(0, 10))
tk.Label(title_frame, text="即将执行高风险操作!", font=(self.config['title_font'], self.config['title_font_size'], 'bold'),
justify=tk.LEFT, bg='#f8f9fa').pack(side=tk.LEFT)
text_frame = tk.Frame(content_frame, bg='#f8f9fa')
text_frame.pack(fill=tk.X, padx=(30, 0), pady=(0, 15))
for point in ["系统将重启进入高级启动模式", "临时禁用驱动签名验证", "确定要继续吗?"]:
point_frame = tk.Frame(text_frame, bg='#f8f9fa')
point_frame.pack(fill=tk.X, pady=2)
tk.Label(point_frame, text="•", font=('Arial', 10), bg='#f8f9fa').pack(side=tk.LEFT, padx=(0, 5))
tk.Label(point_frame, text=point, font=(self.config['button_font'], self.config['button_font_size']),
bg='#f8f9fa').pack(side=tk.LEFT)
result = [False]
button_frame = tk.Frame(content_frame, bg='#f8f9fa')
button_frame.pack(fill=tk.X, padx=10, pady=10)
ttk.Button(button_frame, text="是(Y)", command=lambda: [result.__setitem__(0, True), warning_dialog.destroy()],
style='Primary.TButton').pack(side=tk.RIGHT, padx=5)
ttk.Button(button_frame, text="否(N)", command=lambda: [result.__setitem__(0, False), warning_dialog.destroy()],
style='Primary.TButton').pack(side=tk.RIGHT, padx=5)
self.master.wait_window(warning_dialog)
return result[0]
def show_about(self):
about_text = f"""{self.config['title']}
版本: {self.PROTECTED_METADATA['版本']}
作者: {self.PROTECTED_METADATA['作者']}
联系: {self.PROTECTED_METADATA['联系']}
{self.PROTECTED_METADATA['说明']}"""
messagebox.showinfo("关于", about_text)
def _update_ip_display(self):
def update_ip():
try:
ip = get_local_ip()
self.ip_label.config(text=f"IP: {ip}")
except Exception as e:
logger.error(f"获取IP失败: {str(e)}")
self.ip_label.config(text="IP: 获取失败")
Thread(target=update_ip, daemon=True).start()
class ToolboxManager:
def __init__(self, master, config_path='config.ini', refresh_callback=None):
self.master = master
self.config_path = config_path
self.refresh_callback = refresh_callback
self.tools = {}
self._load_config()
self.window = tk.Toplevel(master)
self.window.title("工具箱管理")
self.window.geometry("450x300")
self.window.resizable(False, False)
self.window.protocol("WM_DELETE_WINDOW", self._on_close)
self._center_window()
self._create_ui()
self._refresh_data()
def _on_close(self):
if self.refresh_callback:
self.refresh_callback()
self.window.destroy()
def _center_window(self):
self.window.update_idletasks()
width, height = self.window.winfo_width(), self.window.winfo_height()
x = (self.window.winfo_screenwidth() // 2) - (width // 2)
y = (self.window.winfo_screenheight() // 2) - (height // 2)
self.window.geometry(f'+{x}+{y}')
def _load_config(self):
self.config = configparser.ConfigParser()
if os.path.exists(self.config_path):
self.config.read(self.config_path, encoding='utf-8')
for section in self.config.sections():
if section in {'程序信息', '界面设置'}:
continue
self.tools[section] = []
for name, value in self.config.items(section):
parts = value.split('|')
self.tools[section].append({
'name': name,
'command': parts[0].strip(),
'btn_text': parts[1].strip() if len(parts) > 1 else "运行",
'icon_path': parts[2].strip() if len(parts) > 2 and parts[2].strip() else None
})
def _save_config(self):
try:
with open(self.config_path, 'w', encoding='utf-8') as f:
self.config.write(f)
if self.refresh_callback:
self.refresh_callback()
except Exception as e:
messagebox.showerror("错误", f"保存配置文件失败: {str(e)}", parent=self.window)
def _create_ui(self):
top_frame = ttk.Frame(self.window, padding=10)
top_frame.pack(fill=tk.X)
top_frame.columnconfigure(0, weight=1)
top_frame.columnconfigure(1, weight=1)
ttk.Button(top_frame, text="添加工具", command=self._show_add_tool_panel, style='primary.TButton').grid(row=0, column=0, padx=5, sticky='ew')
ttk.Button(top_frame, text="管理分类", command=self._show_manage_category_panel, style='primary.TButton').grid(row=0, column=1, padx=5, sticky='ew')
self.main_panel = ttk.Frame(self.window, padding=(10, 10, 10, 10))
self.main_panel.pack(fill=tk.BOTH, expand=True)
self._show_add_tool_panel()
def _show_add_tool_panel(self):
self._clear_main_panel()
main_frame = ttk.Frame(self.main_panel)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
ttk.Label(main_frame, text="选择分类:").grid(row=0, column=0, sticky='w', pady=5)
categories = [cat for cat in self.tools.keys() if cat not in ['程序信息', '界面设置']]
self.cat_combobox = ttk.Combobox(main_frame, values=categories, state="readonly")
self.cat_combobox.grid(row=0, column=1, padx=5, pady=5, sticky='ew')
if categories:
self.cat_combobox.current(0)
ttk.Label(main_frame, text="工具名称:").grid(row=1, column=0, sticky='w', pady=5)
self.tool_name_entry = ttk.Entry(main_frame)
self.tool_name_entry.grid(row=1, column=1, padx=5, pady=5, sticky='ew')
ttk.Label(main_frame, text="命令/URL:").grid(row=2, column=0, sticky='w', pady=5)
self.cmd_entry = ttk.Entry(main_frame)
self.cmd_entry.grid(row=2, column=1, padx=5, pady=5, sticky='ew')
btn_frame = ttk.Frame(main_frame)
btn_frame.grid(row=3, column=0, columnspan=2, pady=15, sticky='ew')
btn_frame.columnconfigure(0, weight=1)
btn_frame.columnconfigure(1, weight=1)
ttk.Button(btn_frame, text="选择文件", command=self._browse_file).grid(row=0, column=0, padx=5, sticky='ew')
ttk.Button(btn_frame, text="确认", command=self._add_tool, style='danger.TButton').grid(row=0, column=1, padx=5, sticky='ew')
main_frame.columnconfigure(1, weight=1)
def _show_manage_category_panel(self):
self._clear_main_panel()
main_frame = ttk.Frame(self.main_panel)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
add_frame = ttk.LabelFrame(main_frame, text="添加新分类", padding=10)
add_frame.grid(row=0, column=0, padx=5, pady=5, sticky='ew')
ttk.Label(add_frame, text="新分类名称:").pack(side=tk.LEFT)
self.new_cat_entry = ttk.Entry(add_frame)
self.new_cat_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
ttk.Button(add_frame, text="添加分类", command=self._add_category, style='success.TButton').pack(side=tk.LEFT, padx=5)
del_frame = ttk.LabelFrame(main_frame, text="删除分类", padding=10)
del_frame.grid(row=1, column=0, padx=5, pady=5, sticky='ew')
ttk.Label(del_frame, text="选择分类:").pack(side=tk.LEFT)
categories = [cat for cat in self.tools.keys() if cat not in ['程序信息', '界面设置']]
self.del_category_combobox = ttk.Combobox(del_frame, values=categories, state="readonly")
self.del_category_combobox.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
if categories:
self.del_category_combobox.current(0)
ttk.Button(del_frame, text="删除分类", command=self._delete_category, style='danger.TButton').pack(side=tk.LEFT, padx=5)
main_frame.columnconfigure(0, weight=1)
def _clear_main_panel(self):
for widget in self.main_panel.winfo_children():
widget.destroy()
def _refresh_data(self):
categories = [cat for cat in self.tools.keys() if cat not in ['程序信息', '界面设置']]
if hasattr(self, 'cat_combobox') and self.cat_combobox.winfo_exists():
self.cat_combobox['values'] = categories
if categories:
self.cat_combobox.current(0)
if hasattr(self, 'del_category_combobox') and self.del_category_combobox.winfo_exists():
self.del_category_combobox['values'] = categories
if categories:
self.del_category_combobox.current(0)
def _browse_file(self):
self.window.attributes('-disabled', True)
file_path = filedialog.askopenfilename(
title="选择可执行文件",
filetypes=[("可执行文件", "*.exe"), ("批处理文件", "*.bat *.cmd"), ("所有文件", "*.*")]
)
self.window.attributes('-disabled', False)
self.window.focus_force()
self.window.lift()
if file_path:
self.cmd_entry.delete(0, tk.END)
self.cmd_entry.insert(0, file_path)
file_name = os.path.basename(file_path)
tool_name = os.path.splitext(file_name)[0]
self.tool_name_entry.delete(0, tk.END)
self.tool_name_entry.insert(0, tool_name)
def _add_tool(self):
category = self.cat_combobox.get()
name = self.tool_name_entry.get().strip()
cmd = self.cmd_entry.get().strip()
if not category or not name or not cmd:
messagebox.showwarning("警告", "请填写完整信息", parent=self.window)
return
if category not in self.tools:
self.tools[category] = []
self.tools[category].append({'name': name, 'command': cmd, 'btn_text': "运行", 'icon_path': None})
if not self.config.has_section(category):
self.config.add_section(category)
self.config.set(category, name, f"{cmd}|运行")
self._save_config()
self.tool_name_entry.delete(0, tk.END)
self.cmd_entry.delete(0, tk.END)
messagebox.showinfo("成功", "工具添加成功", parent=self.window)
def _add_category(self):
new_cat = self.new_cat_entry.get().strip()
if not new_cat or new_cat in ['程序信息', '界面设置']:
messagebox.showwarning("警告", "无效的分类名称", parent=self.window)
return
if new_cat in self.tools:
messagebox.showwarning("警告", "分类已存在", parent=self.window)
return
self.tools[new_cat] = []
self.config.add_section(new_cat)
self._save_config()
self.new_cat_entry.delete(0, tk.END)
self._refresh_data()
messagebox.showinfo("成功", "分类添加成功", parent=self.window)
def _delete_category(self):
category = self.del_category_combobox.get()
if not category or category in ['程序信息', '界面设置']:
messagebox.showwarning("警告", "无效的分类", parent=self.window)
return
if not messagebox.askyesno("确认删除", f"确定要删除分类 '{category}' 及其所有工具吗?", parent=self.window):
return
if category in self.tools:
del self.tools[category]
if self.config.has_section(category):
self.config.remove_section(category)
self._save_config()
self._clear_main_panel()
self._show_manage_category_panel()
messagebox.showinfo("成功", "分类删除成功", parent=self.window)
def main():
if not is_admin():
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
sys.exit()
root = tk.Tk()
if sys.platform == 'win32':
ctypes.windll.shcore.SetProcessDpiAwareness(1)
try:
root.attributes('-alpha', 0.0)
app = ToolboxApp(root)
root.after(100, lambda: root.attributes('-alpha', 1.0))
root.mainloop()
except Exception as e:
logger.critical(f"程序崩溃: {str(e)}")
if __name__ == "__main__":
main()
|
|