|
源代码给你,你说有毒我也没法说了
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, filedialog
from PIL import Image, ImageTk
import os
import sys
import subprocess
import re
import socket
from collections import OrderedDict
import webbrowser
import configparser
import time
# 导入拖放支持库
try:
from tkinterdnd2 import DND_FILES, TkinterDnD
except ImportError:
messagebox.showerror("错误", "需要安装tkinterdnd2库才能支持拖放功能\n请运行: pip install tkinterdnd2")
sys.exit(1)
class ToolPositionEditor:
# 固定程序信息,放在这里简单保护,被修改或删除后重新写入
PROGRAM_INFO = {
'作者': '缘起性空',
'版本': '4.0',
'联系': '32897251@qq.com',
'说明': '本工具仅供技术交流使用'
}
# 用户可自定义的界面设置
USER_CONFIG_KEYS = {
'icon_size': '图标大小',
'window_width': '窗口宽度',
'window_height': '窗口高度',
'tools_per_row': '每行工具数',
'title': '标题'
}
def __init__(self, master):
self.master = master
self.config_file = "config.ini"
self.base_path = self._get_base_path()
# 基础配置(仅包含用户可自定义的项)
self.config = self.load_config()
self.tools = self.load_tools()
# 状态变量
self.edit_mode = False
self.icon_cache = {}
self.selected_button_name = None
self.current_section = None
self.drag_data = {"source": None, "target": None}
self.selected_category = None # 记录选中的分类
# 危险命令关键词列表
self.dangerous_commands = [
"shutdown", "reboot", "restart", "format", "rm -rf", "del /f", "msiexec /x",
"disable driver signature enforcement", "bcdedit", "reg delete", "regedit /s"
]
# 获取本机IP地址
self.ip_address = self.get_ip_address()
# 初始化界面
self.setup_window()
self.setup_ui()
def _get_base_path(self):
"""获取程序基础路径"""
if getattr(sys, 'frozen', False):
return os.path.dirname(sys.executable)
return os.path.dirname(os.path.abspath(__file__))
def get_ip_address(self):
"""获取本机IP地址"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
except Exception:
return "未连接网络"
def load_config(self):
"""加载界面配置,仅加载用户可自定义的项"""
# 默认配置(仅包含用户可自定义的项)
default_config = {
'icon_size': 32,
'window_width': 900,
'window_height': 600,
'tools_per_row': 3,
'title': "我的工具箱"
}
# 加载配置文件中的用户自定义项
if os.path.exists(self.config_file):
try:
parser = configparser.ConfigParser()
parser.read(self.config_file, encoding='utf-8-sig')
if '界面设置' in parser:
for key, ch_name in self.USER_CONFIG_KEYS.items():
if parser.has_option('界面设置', ch_name):
try:
if key in ['icon_size', 'window_width', 'window_height', 'tools_per_row']:
default_config[key] = parser.getint('界面设置', ch_name)
else:
default_config[key] = parser.get('界面设置', ch_name)
except ValueError:
pass
except Exception:
pass
return default_config
def load_tools(self):
"""加载工具数据"""
tools = OrderedDict()
if os.path.exists(self.config_file):
try:
parser = configparser.ConfigParser()
parser.read(self.config_file, encoding='utf-8-sig')
for section in parser.sections():
if section not in ('程序信息', '界面设置'):
tools[section] = []
for name, value in parser.items(section):
parts = [x.strip() for x in value.split('|', 1)]
exe_path = parts[0] if parts and parts[0] else None
icon_path = parts[1] if len(parts) > 1 else None
tools[section].append((name, exe_path, icon_path))
except Exception:
pass
if not tools:
tools["默认分类"] = [("记事本", "notepad.exe", None), ("计算器", "calc.exe", None)]
return tools
def save_config(self):
"""保存配置,包含程序信息、界面设置、工具数据"""
try:
parser = configparser.ConfigParser()
# 先写死程序信息,保证在配置文件顶部
parser.add_section('程序信息')
for key, value in self.PROGRAM_INFO.items():
parser.set('程序信息', key, value)
# 只写用户可自定义的界面设置
parser.add_section('界面设置')
for key, ch_name in self.USER_CONFIG_KEYS.items():
parser.set('界面设置', ch_name, str(self.config[key]))
# 写工具数据
for category in self.tools:
parser.add_section(category)
for name, exe_path, icon_path in self.tools[category]:
value = f"{exe_path if exe_path else ''} | {icon_path if icon_path else ''}"
parser.set(category, name, value.strip(' |'))
# 写入文件
with open(self.config_file, 'w', encoding='utf-8') as f:
parser.write(f)
except Exception as e:
messagebox.showerror("错误", f"保存失败: {str(e)}")
def setup_window(self):
"""设置窗口基本属性"""
self.master.title(self.config['title'])
# 计算最小宽度,确保工具按钮能正常显示
min_width = 250 + self.config['tools_per_row'] * (180 + 15)
self.config['window_width'] = max(self.config['window_width'], min_width)
screen_width = self.master.winfo_screenwidth()
screen_height = self.master.winfo_screenheight()
x = (screen_width - self.config['window_width']) // 2
y = (screen_height - self.config['window_height']) // 2
self.master.geometry(f"{self.config['window_width']}x{self.config['window_height']}+{x}+{y}")
def setup_ui(self):
"""初始化界面"""
# 清空当前界面
for widget in self.master.winfo_children():
widget.destroy()
# 标题栏
title_frame = tk.Frame(self.master, height=50, bg="#2E2E2E")
title_frame.pack(fill=tk.X)
# 标题显示
title_ip_frame = tk.Frame(title_frame, bg="#2E2E2E")
title_ip_frame.pack(side=tk.LEFT, padx=10, pady=5)
tk.Label(title_ip_frame, text=self.config['title'], fg="#FFFFFF", bg="#2E2E2E",
font=("微软雅黑", 16)).pack(anchor="w")
# 编辑模式按钮
self.edit_btn = tk.Button(title_frame, text="进入编辑模式", command=self.toggle_edit_mode,
font=("微软雅黑", 12), bg="#2196F3", fg="white", relief=tk.FLAT, padx=10)
self.edit_btn.pack(side=tk.RIGHT, padx=5, pady=10)
# 关于按钮
about_btn = tk.Button(title_frame, text="关于", command=self.show_about,
font=("微软雅黑", 12), bg="#2E2E2E", fg="white", relief=tk.FLAT, padx=10)
about_btn.pack(side=tk.RIGHT, padx=5, pady=10)
# 设置按钮
settings_btn = tk.Button(title_frame, text="设置", command=self.show_settings,
font=("微软雅黑", 12), bg="#2E2E2E", fg="white", relief=tk.FLAT, padx=10)
settings_btn.pack(side=tk.RIGHT, padx=5, pady=10)
# 主内容区
content_frame = tk.Frame(self.master)
content_frame.pack(fill=tk.BOTH, expand=True)
# 左侧导航
nav_frame = tk.Frame(content_frame, width=150, bg="#F0F0F0")
nav_frame.pack_propagate(False)
nav_frame.pack(side=tk.LEFT, fill=tk.Y)
# 分类列表标题
tk.Label(nav_frame, text="", font=("微软雅黑", 14, "bold"),
bg="#F0F0F0", fg="#333333").pack(fill=tk.X, pady=5, padx=10, anchor="w")
# 分类按钮
self.category_buttons = []
for category in self.tools.keys():
btn = tk.Button(nav_frame, text=category, command=lambda c=category: self.show_category(c),
bg="#F0F0F0", fg="#000000", font=("微软雅黑", 13), relief=tk.FLAT, anchor="w", padx=15, pady=3)
btn.pack(fill=tk.X, ipady=3, pady=1)
btn.bind("<Button-3>", lambda e, c=category: self.show_category_context_menu(e, c)) # 绑定右键
self.category_buttons.append(btn)
# 分类右键菜单
self.category_menu = tk.Menu(self.master, tearoff=0)
self.category_menu.add_command(label="添加分类", command=self._add_category)
self.category_menu.add_command(label="重命名分类", command=self._rename_category)
self.category_menu.add_command(label="删除分类", command=self._delete_category)
# 右侧工具区 + 滚动条
self.canvas = tk.Canvas(content_frame, bg="#FFFFFF", highlightthickness=0)
scrollbar = tk.Scrollbar(content_frame, orient="vertical", command=self.canvas.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.configure(yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# 右键菜单
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._change_tool_category)
self.button_menu.add_command(label="删除", command=self._delete_tool)
self.master.bind("<Button-1>", lambda e: setattr(self, 'selected_button_name', None))
# 拖放设置 - 确保Canvas支持拖放
self.canvas.drop_target_register(DND_FILES)
self.canvas.dnd_bind('<<Drop>>', self.on_drop)
# 底部状态栏
status_frame = tk.Frame(self.master, height=25, bg="#F0F0F0")
status_frame.pack(fill=tk.X, side=tk.BOTTOM)
# IP地址显示(移动到左下角)
self.ip_label = tk.Label(status_frame, text=f"IP: {self.ip_address}", fg="#333333", bg="#F0F0F0",
font=("微软雅黑", 9), padx=10)
self.ip_label.pack(side=tk.LEFT, anchor="sw")
# 编辑模式显示(移动到右下角)
self.mode_label = tk.Label(status_frame, text="浏览模式", fg="#333333", bg="#F0F0F0",
font=("微软雅黑", 9), padx=10)
self.mode_label.pack(side=tk.RIGHT, anchor="se")
# 初始显示工具
if self.tools:
self.show_category(next(iter(self.tools.keys())))
def show_about(self):
"""显示关于对话框"""
about_window = tk.Toplevel(self.master)
about_window.title("关于")
about_window.geometry("300x250")
about_window.transient(self.master)
about_window.grab_set()
# 居中显示
self.master.update_idletasks()
x = self.master.winfo_x() + (self.master.winfo_width() - 300) // 2
y = self.master.winfo_y() + (self.master.winfo_height() - 250) // 2
about_window.geometry(f"+{x}+{y}")
# 标题
tk.Label(about_window, text=self.config['title'], font=("微软雅黑", 16, "bold"),
pady=10).pack()
# 版本信息
tk.Label(about_window, text=f"版本: {self.PROGRAM_INFO['版本']}",
font=("微软雅黑", 10)).pack(pady=5)
# 作者信息
tk.Label(about_window, text=f"作者: {self.PROGRAM_INFO['作者']}",
font=("微软雅黑", 10)).pack(pady=5)
# 联系信息
tk.Label(about_window, text=f"联系: {self.PROGRAM_INFO['联系']}",
font=("微软雅黑", 10)).pack(pady=5)
# 说明信息
tk.Label(about_window, text=self.PROGRAM_INFO['说明'],
font=("微软雅黑", 10), wraplength=250).pack(pady=10)
# 确定按钮
tk.Button(about_window, text="确定", command=about_window.destroy,
width=10).pack(pady=10)
def show_settings(self):
"""显示设置对话框"""
settings_window = tk.Toplevel(self.master)
settings_window.title("设置")
settings_window.geometry("350x300")
settings_window.transient(self.master)
settings_window.grab_set()
# 居中显示
self.master.update_idletasks()
x = self.master.winfo_x() + (self.master.winfo_width() - 350) // 2
y = self.master.winfo_y() + (self.master.winfo_height() - 300) // 2
settings_window.geometry(f"+{x}+{y}")
# 界面设置框架
frame = tk.Frame(settings_window, padx=20, pady=20)
frame.pack(fill=tk.BOTH, expand=True)
# 图标大小
tk.Label(frame, text="图标大小:", font=("微软雅黑", 12)).grid(row=0, column=0, sticky="w", pady=5)
icon_size = tk.StringVar(value=str(self.config['icon_size']))
tk.Spinbox(frame, from_=16, to=64, width=5, textvariable=icon_size).grid(row=0, column=1, sticky="w", pady=5)
# 窗口宽度
tk.Label(frame, text="窗口宽度:", font=("微软雅黑", 12)).grid(row=1, column=0, sticky="w", pady=5)
window_width = tk.StringVar(value=str(self.config['window_width']))
tk.Spinbox(frame, from_=600, to=1920, width=5, textvariable=window_width).grid(row=1, column=1, sticky="w", pady=5)
# 窗口高度
tk.Label(frame, text="窗口高度:", font=("微软雅黑", 12)).grid(row=2, column=0, sticky="w", pady=5)
window_height = tk.StringVar(value=str(self.config['window_height']))
tk.Spinbox(frame, from_=400, to=1080, width=5, textvariable=window_height).grid(row=2, column=1, sticky="w", pady=5)
# 每行工具数
tk.Label(frame, text="每行工具数:", font=("微软雅黑", 12)).grid(row=3, column=0, sticky="w", pady=5)
tools_per_row = tk.StringVar(value=str(self.config['tools_per_row']))
tk.Spinbox(frame, from_=1, to=10, width=5, textvariable=tools_per_row).grid(row=3, column=1, sticky="w", pady=5)
# 标题
tk.Label(frame, text="标题:", font=("微软雅黑", 12)).grid(row=4, column=0, sticky="w", pady=5)
title = tk.StringVar(value=self.config['title'])
tk.Entry(frame, textvariable=title, width=20).grid(row=4, column=1, sticky="w", pady=5)
# 按钮框架
btn_frame = tk.Frame(settings_window)
btn_frame.pack(pady=20)
# 确定按钮
def save_settings():
try:
self.config['icon_size'] = int(icon_size.get())
self.config['window_width'] = int(window_width.get())
self.config['window_height'] = int(window_height.get())
self.config['tools_per_row'] = int(tools_per_row.get())
self.config['title'] = title.get()
self.save_config()
self.setup_ui() # 重新加载界面以应用新设置
settings_window.destroy()
except ValueError:
messagebox.showerror("错误", "请输入有效的数字")
tk.Button(btn_frame, text="确定", command=save_settings, width=10).pack(side=tk.LEFT, padx=10)
tk.Button(btn_frame, text="取消", command=settings_window.destroy, width=10).pack(side=tk.LEFT, padx=10)
def show_category_context_menu(self, event, category=None):
"""显示分类右键菜单"""
self.selected_category = category
self.category_menu.post(event.x_root, event.y_root)
def _add_category(self):
"""添加新分类"""
new_name = simpledialog.askstring("添加分类", "请输入新分类名称")
if new_name and new_name.strip():
new_name = new_name.strip()
# 检查名称是否已存在
if new_name in self.tools:
messagebox.showerror("错误", "分类名称已存在")
return
# 添加新分类
self.tools[new_name] = []
# 更新界面
self.setup_ui()
self.save_config()
def _rename_category(self):
"""重命名分类"""
if not self.selected_category or self.selected_category == "默认分类":
messagebox.showwarning("提示", "不能重命名默认分类")
return
new_name = simpledialog.askstring("重命名分类", "请输入新的分类名称", initialvalue=self.selected_category)
if new_name and new_name != self.selected_category:
# 检查新名称是否已存在
if new_name in self.tools:
messagebox.showerror("错误", "分类名称已存在")
return
# 重命名分类
self.tools[new_name] = self.tools.pop(self.selected_category)
# 更新当前显示的分类
if self.current_category == self.selected_category:
self.current_category = new_name
# 更新界面
self.setup_ui()
self.save_config()
def _delete_category(self):
"""删除分类"""
if not self.selected_category or self.selected_category == "默认分类":
messagebox.showwarning("提示", "不能删除默认分类")
return
if messagebox.askyesno("确认", f"确定要删除分类【{self.selected_category}】吗?\n该分类下的工具将移动到默认分类"):
# 将工具移动到默认分类
if "默认分类" not in self.tools:
self.tools["默认分类"] = []
for tool in self.tools[self.selected_category]:
self.tools["默认分类"].append(tool)
# 删除分类
del self.tools[self.selected_category]
# 如果删除的是当前分类,切换到默认分类
if self.current_category == self.selected_category:
self.current_category = "默认分类"
# 更新界面
self.setup_ui()
self.save_config()
def _normalize_path(self, file_path):
"""规范化路径处理,优先使用相对路径,跨驱动器时使用绝对路径"""
# 如果已经是相对路径且以tools/开头,直接返回
if not os.path.isabs(file_path) and file_path.lower().startswith("tools/"):
return file_path
# 如果是URL,直接返回
if file_path.lower().startswith(('http://', 'https://')):
return file_path
# 尝试创建相对路径
try:
rel_path = os.path.relpath(file_path, self.base_path)
# 检查是否是有效相对路径(避免生成过长的路径)
if not rel_path.startswith('..') and not rel_path.startswith('\\..'):
# 确保相对路径以tools/开头
if not rel_path.lower().replace("\\", "/").startswith("tools/"):
rel_path = os.path.join("tools", rel_path).replace("\\", "/")
return rel_path
else:
# 如果相对路径包含多个..,说明目标文件与程序目录不在同一层级,使用绝对路径
messagebox.showwarning("提示", f"文件与程序不在同一层级,将使用绝对路径: {file_path}")
return file_path
except ValueError:
# 如果是跨驱动器的路径,使用绝对路径
messagebox.showwarning("警告", f"文件位于不同驱动器,将使用绝对路径: {file_path}")
return file_path
def on_drop(self, event):
"""处理文件拖放"""
file_path = self.master.tk.splitlist(event.data)[0]
if not os.path.exists(file_path):
messagebox.showerror("错误", f"文件不存在: {file_path}")
return
tool_name = os.path.splitext(os.path.basename(file_path))[0]
# 改进路径处理逻辑
exe_path = self._normalize_path(file_path)
# 尝试设置图标
icons_dir = os.path.join(self.base_path, "icons")
if not os.path.exists(icons_dir):
os.makedirs(icons_dir)
icon_path = os.path.join("icons", f"{tool_name}.ico")
icon_abs_path = os.path.join(self.base_path, icon_path)
if not os.path.exists(icon_abs_path):
icon_path = None
# 添加工具
if self.current_section not in self.tools:
self.tools[self.current_section] = []
self.tools[self.current_section].append((tool_name, exe_path, icon_path))
self.save_config()
self.draw_tools()
messagebox.showinfo("成功", f"已添加工具: {tool_name}")
def show_category(self, category):
"""显示指定分类的工具"""
self.current_category = category
self.current_section = category
self.draw_tools()
def draw_tools(self):
"""绘制工具按钮"""
self.canvas.delete("all")
self.button_positions = []
tools = self.tools[self.current_category]
for i, (name, exe_path, icon_path) in enumerate(tools):
row = i // self.config['tools_per_row']
col = i % self.config['tools_per_row']
x = 30 + col * (180 + 25)
y = 30 + row * (36 + 25)
# 绘制按钮
btn = self.canvas.create_rectangle(x, y, x + 180, y + 36, fill="#FFFFFF", outline="#4CAF50", width=1, tags=("tool", f"tool_{i}"))
# 绘制图标
icon = self.load_icon(icon_path)
text_x = x + 90
if icon:
icon_x = x + 5
text_x = icon_x + self.config['icon_size'] + 5
self.canvas.create_image(icon_x, y + 18, image=icon, anchor="w", tags=("tool_icon", f"icon_{i}"))
# 绘制文本
self.canvas.create_text(text_x, y + 18, text=name, fill="#000000", font=("微软雅黑", 12), width=180 - (self.config['icon_size'] + 15 if icon else 10), tags=("tool_text", f"text_{i}"), anchor="w" if icon else "center")
self.button_positions.append((x, y, btn))
# 绑定事件
for tag in ["tool", "tool_icon", "tool_text"]:
self.canvas.tag_bind(tag, "<Button-1>", self.on_tool_click)
self.canvas.tag_bind(tag, "<Button-3>", self.show_context_menu)
self.canvas.tag_bind(tag, "<B1-Motion>", self.on_drag)
self.canvas.tag_bind(tag, "<ButtonRelease-1>", self.end_drag)
def load_icon(self, icon_path):
"""加载图标到缓存"""
if not icon_path:
return None
if icon_path in self.icon_cache:
return self.icon_cache[icon_path]
try:
abs_path = os.path.join(self.base_path, icon_path.replace("/", os.sep))
if not os.path.exists(abs_path):
return None
img = Image.open(abs_path)
img = img.resize((self.config['icon_size'], self.config['icon_size']), Image.LANCZOS)
icon = ImageTk.PhotoImage(img)
self.icon_cache[icon_path] = icon
return icon
except Exception:
return None
def toggle_edit_mode(self):
"""切换编辑模式"""
self.edit_mode = not self.edit_mode
if self.edit_mode:
self.edit_btn.config(text="退出编辑模式", bg="#F44336")
self.mode_label.config(text="编辑模式")
else:
self.edit_btn.config(text="进入编辑模式", bg="#2196F3")
self.mode_label.config(text="浏览模式")
self.save_config()
def show_context_menu(self, event):
"""显示右键菜单"""
item = self.canvas.find_closest(event.x, event.y)[0]
tags = self.canvas.gettags(item)
tool_idx = None
for tag in tags:
if tag.startswith("tool_") or tag.startswith("icon_") or tag.startswith("text_"):
idx_part = tag.split("_")[-1]
if idx_part.isdigit():
tool_idx = int(idx_part)
break
if tool_idx is not None:
self.selected_button_name = self.tools[self.current_category][tool_idx][0]
self.button_menu.post(event.x_root, event.y_root)
def on_tool_click(self, event):
"""处理工具点击"""
item = self.canvas.find_closest(event.x, event.y)[0]
tags = self.canvas.gettags(item)
tool_idx = None
for tag in tags:
if tag.startswith("tool_") or tag.startswith("icon_") or tag.startswith("text_"):
idx_part = tag.split("_")[-1]
if idx_part.isdigit():
tool_idx = int(idx_part)
break
if tool_idx is not None:
if not self.edit_mode: # 执行程序
name, exe_path, _ = self.tools[self.current_category][tool_idx]
self.run_program(name, exe_path)
else: # 开始拖拽
self.drag_data["source"] = tool_idx
for _, _, btn in self.button_positions:
self.canvas.itemconfig(btn, outline="#CCCCCC", width=1)
self.canvas.itemconfig(self.button_positions[tool_idx][2], outline="#E0E0E0", width=2)
def run_program(self, name, exe_path):
"""执行程序"""
if not exe_path:
messagebox.showwarning("提示", f"工具【{name}】未配置执行路径")
return
# 检查危险命令
if self.is_dangerous_command(exe_path):
# 修改危险操作提示信息
if not messagebox.askyesno("安全警告", "即将执行高风险操作!\n·系统将重启进入高级启动模式\n·临时禁用驱动签名验证\n确定要继续吗?"):
return
# 处理路径
if exe_path.startswith(('http://', 'https://')):
webbrowser.open(exe_path)
else:
# 如果是绝对路径,直接使用
if os.path.isabs(exe_path):
if not os.path.exists(exe_path):
messagebox.showerror("错误", f"工具不存在: {exe_path}")
return
# 如果是以tools/开头的相对路径,转换为绝对路径
elif exe_path.startswith('tools/'):
exe_path = os.path.join(self.base_path, exe_path)
if not os.path.exists(exe_path):
messagebox.showerror("错误", f"工具不存在: {exe_path}")
return
try:
if sys.platform == 'win32':
subprocess.Popen(exe_path, shell=True, creationflags=subprocess.CREATE_NO_WINDOW)
elif sys.platform == 'darwin':
subprocess.Popen(['open', exe_path])
else:
subprocess.Popen(['xdg-open', exe_path])
except Exception as e:
messagebox.showerror("错误", f"启动程序失败: {str(e)}")
def is_dangerous_command(self, command):
"""检查是否为危险命令"""
cmd_lower = command.lower()
for keyword in self.dangerous_commands:
if keyword in cmd_lower:
return True
return False
def parse_command(self, command_string):
"""解析命令字符串,分离命令和参数"""
pattern = r'"([^"]*)"|\'([^\']*)\'|(\S+)'
parts = []
for match in re.finditer(pattern, command_string):
if match.group(1):
parts.append(match.group(1))
elif match.group(2):
parts.append(match.group(2))
else:
parts.append(match.group(3))
return parts
def is_system_command(self, command):
"""检查是否为系统命令"""
system_commands = ["shutdown", "restart", "reboot", "cmd", "powershell", "msiexec", "regedit", "taskkill", "taskmgr", "services.msc"]
return any(cmd in command.lower() for cmd in system_commands)
def find_in_path(self, executable):
"""在系统PATH中查找可执行文件"""
if os.path.exists(executable):
return True
if sys.platform == 'win32':
extensions = ['.exe', '.com', '.bat', '.cmd']
for ext in extensions:
if os.path.exists(executable + ext):
return True
path_dirs = os.environ['PATH'].split(os.pathsep)
for path_dir in path_dirs:
full_path = os.path.join(path_dir, executable)
if os.path.exists(full_path):
return True
return False
def _rename_tool(self):
"""重命名工具"""
new_name = simpledialog.askstring("重命名", "请输入新的工具名称", initialvalue=self.selected_button_name)
if new_name:
for i, (name, exe_path, icon_path) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (new_name, exe_path, icon_path)
break
self.save_config()
self.draw_tools()
def _set_tool_icon(self):
"""设置工具图标"""
initial_dir = os.path.join(self.base_path, "icons")
if not os.path.exists(initial_dir):
os.makedirs(initial_dir)
icon_path = filedialog.askopenfilename(
title="选择图标文件",
initialdir=initial_dir,
filetypes=[("图标文件", "*.ico *.png *.jpg *.jpeg *.bmp"), ("所有文件", "*.*")]
)
if not icon_path:
return
try:
rel_path = os.path.relpath(icon_path, self.base_path)
if not rel_path.lower().replace("\\", "/").startswith("icons/"):
messagebox.showerror("错误", "图标必须放在程序目录下的icons文件夹内!")
return
rel_path = rel_path.replace("\\", "/")
for i, (name, cmd, _) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (name, cmd, rel_path)
break
self.save_config()
self.draw_tools()
except Exception as e:
messagebox.showerror("错误", f"设置图标失败: {str(e)}")
def _remove_tool_icon(self):
"""删除工具图标"""
for i, (name, exe_path, _) in enumerate(self.tools[self.current_section]):
if name == self.selected_button_name:
self.tools[self.current_section] = (name, exe_path, None)
break
self.save_config()
self.draw_tools()
def _change_tool_category(self):
"""修改工具分类 - 使用下拉菜单选择已有分类"""
if not self.selected_button_name:
return
# 创建一个临时窗口
temp_window = tk.Toplevel(self.master)
temp_window.title("修改分类")
temp_window.geometry("300x150")
temp_window.transient(self.master)
temp_window.grab_set()
# 居中显示窗口
self.master.update_idletasks()
x = self.master.winfo_x() + (self.master.winfo_width() - 300) // 2
y = self.master.winfo_y() + (self.master.winfo_height() - 150) // 2
temp_window.geometry(f"+{x}+{y}")
# 标签
tk.Label(temp_window, text=f"工具: {self.selected_button_name}",
font=("微软雅黑", 12)).pack(pady=10)
# 分类选择
tk.Label(temp_window, text="选择分类:",
font=("微软雅黑", 12)).pack(anchor="w", padx=20)
# 创建下拉菜单
categories = list(self.tools.keys())
selected_category = tk.StringVar(value=self.current_category)
category_menu = ttk.Combobox(temp_window, textvariable=selected_category,
values=categories, state="readonly")
category_menu.pack(fill=tk.X, padx=20, pady=5)
# 按钮框架
btn_frame = tk.Frame(temp_window)
btn_frame.pack(pady=10)
# 确定按钮
def confirm_change():
target_category = selected_category.get()
if target_category == self.current_category:
messagebox.showinfo("提示", "已在该分类中")
temp_window.destroy()
return
# 查找并移动工具
for i, (name, exe_path, icon_path) in enumerate(self.tools[self.current_category]):
if name == self.selected_button_name:
self.tools[target_category].append((name, exe_path, icon_path))
self.tools[self.current_category].pop(i)
break
self.save_config()
self.show_category(target_category)
temp_window.destroy()
tk.Button(btn_frame, text="确定", command=confirm_change,
width=10).pack(side=tk.LEFT, padx=10)
tk.Button(btn_frame, text="取消", command=temp_window.destroy,
width=10).pack(side=tk.LEFT, padx=10)
def _delete_tool(self):
"""删除工具"""
if not self.selected_button_name:
return
if messagebox.askyesno("确认", f"确定要删除工具【{self.selected_button_name}】吗?"):
# 查找并删除工具
for i, (name, _, _) in enumerate(self.tools[self.current_category]):
if name == self.selected_button_name:
self.tools[self.current_category].pop(i)
break
self.save_config()
self.draw_tools()
def _run_selected_tool(self):
"""运行选中的工具"""
if not self.selected_button_name:
return
for name, exe_path, _ in self.tools[self.current_category]:
if name == self.selected_button_name:
self.run_program(name, exe_path)
break
def on_drag(self, event):
"""处理拖拽事件"""
if self.drag_data["source"] is None or not self.edit_mode:
return
# 计算当前鼠标位置对应的工具索引
x, y = event.x, event.y
target_idx = None
for i, (btn_x, btn_y, btn_id) in enumerate(self.button_positions):
if btn_x <= x <= btn_x + 180 and btn_y <= y <= btn_y + 36:
target_idx = i
break
# 如果找到了目标位置且不是源位置
if target_idx is not None and target_idx != self.drag_data["source"]:
# 高亮显示目标位置
for i, (_, _, btn_id) in enumerate(self.button_positions):
if i == target_idx:
self.canvas.itemconfig(btn_id, outline="#90CAF9", width=2)
elif i == self.drag_data["source"]:
self.canvas.itemconfig(btn_id, outline="#E0E0E0", width=2)
else:
self.canvas.itemconfig(btn_id, outline="#CCCCCC", width=1)
self.drag_data["target"] = target_idx
def end_drag(self, event):
"""结束拖拽,交换工具位置"""
if self.drag_data["source"] is not None and self.drag_data["target"] is not None:
# 交换工具位置
source_idx = self.drag_data["source"]
target_idx = self.drag_data["target"]
# 保存原始工具数据
source_tool = self.tools[self.current_category][source_idx]
# 移除源位置的工具
self.tools[self.current_category].pop(source_idx)
# 在目标位置插入工具
if source_idx < target_idx:
self.tools[self.current_category].insert(target_idx, source_tool)
else:
self.tools[self.current_category].insert(target_idx, source_tool)
# 重置拖拽数据
self.drag_data = {"source": None, "target": None}
# 更新界面
self.save_config()
self.draw_tools()
# 主程序入口
if __name__ == "__main__":
# 确保中文显示正常
import locale
locale.setlocale(locale.LC_ALL, '')
# 创建应用程序窗口
root = TkinterDnD.Tk()
root.title("我的工具箱4.0")
# 创建应用程序实例
app = ToolPositionEditor(root)
# 运行主循环
root.mainloop()
|
|