只需要在同目录建立文件夹放入可执行exe、bat、等程序,运行该程序自动识别分类,无需配置文件。
下载地址:
https://wwvl.lanzout.com/iVLxH2rc7nbi
密码:52pj
编译exe用的是:
人懒所以做个python打包exe工具,支持复杂程序打包 1.1
https://www.52pojie.cn/thread-2007933-1-1.html
[Python] 纯文本查看 复制代码
import os
import sys
import tkinter as tk
from tkinter import ttk
import win32ui
import win32gui
from PIL import Image, ImageTk
import subprocess
from win32com.client import Dispatch
def get_current_path():
"""自动获取当前程序路径的通用方法"""
try:
# 打包后的情况
if getattr(sys, 'frozen', False):
return os.path.dirname(sys.executable) # 返回EXE所在目录
# 开发环境
return os.path.dirname(os.path.abspath(__file__)) # 返回脚本所在目录
except Exception as e:
print(f"路径获取失败: {e}")
return os.getcwd() # 退回当前工作目录
def get_exe_icon(exe_path):
"""从exe文件中提取图标"""
try:
# 提取图标
large, small = win32gui.ExtractIconEx(exe_path, 0)
win32gui.DestroyIcon(small[0])
# 创建设备上下文
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, 32, 32)
# 在设备上下文中绘制图标
hdc = hdc.CreateCompatibleDC()
hdc.SelectObject(hbmp)
hdc.DrawIcon((0, 0), large[0])
# 获取位图信息和位图数据
bmpinfo = hbmp.GetInfo()
bmpstr = hbmp.GetBitmapBits(True)
# 创建PIL图像对象
img = Image.frombuffer(
'RGBA',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRA', 0, 1
)
return img
except Exception as e:
print(f"提取图标失败: {e}")
return None
class UniformButton(tk.Frame):
"""统一尺寸的程序按钮组件"""
def __init__(self, parent, text, image, command):
super().__init__(parent, bg="white", highlightthickness=0)
# 固定按钮尺寸
self.button_width = 70
self.button_height = 90
self.max_chars = 10
self.max_lines = 2
self.config(width=self.button_width, height=self.button_height)
self.pack_propagate(False)
# 主容器
self.content_frame = tk.Frame(self, bg="white")
self.content_frame.pack(fill=tk.BOTH, expand=True)
# 图标区域
self.icon_label = tk.Label(self.content_frame,
image=image,
bg="white",
borderwidth=0)
self.icon_label.image = image
self.icon_label.pack(pady=(5, 2), expand=True)
# 文本区域
processed_text = self.process_text(text)
self.text_label = tk.Label(self.content_frame,
text=processed_text,
wraplength=self.button_width-15,
justify="center",
font=("微软雅黑", 8),
bg="white",
borderwidth=0)
self.text_label.pack(pady=(0, 5))
# 事件绑定
self.bind_all_children("<Button-1>", lambda e: command())
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
def bind_all_children(self, event, handler):
for child in self.winfo_children():
child.bind(event, handler)
if isinstance(child, tk.Frame):
for subchild in child.winfo_children():
subchild.bind(event, handler)
def process_text(self, text):
text = text[:20]
words = text.split()
lines = []
current_line = []
for word in words:
if len(word) > self.max_chars:
chunks = [word[i:i+self.max_chars] for i in range(0, len(word), self.max_chars)]
current_line.extend(chunks)
else:
current_line.append(word)
if sum(len(w) for w in current_line) + (len(current_line)-1) > self.max_chars:
lines.append(" ".join(current_line[:-1]))
current_line = [current_line[-1]]
if len(lines) >= self.max_lines:
break
if current_line and len(lines) < self.max_lines:
lines.append(" ".join(current_line))
return "\n".join(lines[:self.max_lines]) + ("..." if len(text) > 20 else "")
def on_enter(self, event):
# pylint: disable=unused-argument
self.config(bg="#e0e0e0")
self.content_frame.config(bg="#e0e0e0")
for child in self.content_frame.winfo_children():
child.config(bg="#e0e0e0")
def on_leave(self, event):
# pylint: disable=unused-argument
self.config(bg="white")
self.content_frame.config(bg="white")
for child in self.content_frame.winfo_children():
child.config(bg="white")
class AppLauncher:
def __init__(self, root):
self.root = root
self.root.title("便携工具箱 by:姬御风")
self.root.geometry("530x400")
self.root.resizable(False, False)
self.icon_size = 32
# 设置窗口图标
self.set_window_icon()
# 主界面
self.main_frame = ttk.Frame(root)
self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# 初始化默认图标
self.default_icon = self.get_default_icon()
# 组件初始化
self.create_category_panel()
self.create_program_panel()
# 数据初始化
self.current_dir = get_current_path() # 使用通用路径获取方法
self.scan_directory()
self.populate_categories()
self.show_category("所有程序")
def set_window_icon(self):
"""设置窗口图标"""
try:
# 使用与编译后的exe相同的图标
exe_path = sys.executable
icon_img = get_exe_icon(exe_path)
if icon_img:
icon_img.save('temp_icon.ico', 'ICO')
self.root.iconbitmap('temp_icon.ico')
os.remove('temp_icon.ico')
else:
print("无法提取exe图标,使用默认图标")
self.root.iconbitmap('icon/app.ico')
except Exception as e:
print(f"设置窗口图标失败: {e}")
def resolve_shortcut(self, path):
"""解析快捷方式"""
try:
shell = Dispatch('WScript.Shell')
shortcut = shell.CreateShortCut(path)
return shortcut.TargetPath
except Exception as _e:
print(f"快捷方式解析失败: {path}")
return None
def get_program_icon(self, path):
"""增强图标获取"""
try:
# 处理快捷方式
if path.lower().endswith('.lnk'):
target = self.resolve_shortcut(path)
if target and os.path.exists(target):
path = target
# 获取图标
large, _ = win32gui.ExtractIconEx(path, 0)
if not large:
raise Exception("No icons found")
hicon = large[0]
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, self.icon_size, self.icon_size)
hdc = hdc.CreateCompatibleDC()
hdc.SelectObject(hbmp)
hdc.DrawIcon((0, 0), hicon)
bmpstr = hbmp.GetBitmapBits(True)
img = Image.frombuffer('RGBA', (self.icon_size, self.icon_size),
bmpstr, 'raw', 'BGRA', 0, 1)
return ImageTk.PhotoImage(img)
except Exception as _e:
print(f"图标加载失败: {os.path.basename(path)}")
return self.default_icon
def get_default_icon(self):
"""系统默认图标"""
try:
# 使用文件夹图标
shell32 = "C:\\Windows\\System32\\shell32.dll"
large, _ = win32gui.ExtractIconEx(shell32, 3)
hicon = large[0]
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, self.icon_size, self.icon_size)
hdc = hdc.CreateCompatibleDC()
hdc.SelectObject(hbmp)
hdc.DrawIcon((0, 0), hicon)
bmpstr = hbmp.GetBitmapBits(True)
img = Image.frombuffer('RGBA', (self.icon_size, self.icon_size),
bmpstr, 'raw', 'BGRA', 0, 1)
return ImageTk.PhotoImage(img)
except Exception as _e:
print(f"默认图标加载失败: {_e}")
return ImageTk.PhotoImage(Image.new('RGBA', (self.icon_size, self.icon_size), (240, 240, 240)))
def create_category_panel(self):
"""分类面板(宽度99)"""
self.category_frame = ttk.Frame(self.main_frame, width=99)
self.category_frame.pack(side=tk.LEFT, fill=tk.Y, padx=3)
self.category_canvas = tk.Canvas(self.category_frame,
width=99,
highlightthickness=0)
scrollbar = ttk.Scrollbar(self.category_frame,
orient="vertical",
command=self.category_canvas.yview)
self.category_container = ttk.Frame(self.category_canvas)
self.category_container.bind("<Configure>",
lambda e: self.category_canvas.configure(
scrollregion=self.category_canvas.bbox("all"),
width=99
))
self.category_canvas.create_window((0,0),
window=self.category_container,
anchor="nw",
width=99)
self.category_canvas.configure(yscrollcommand=scrollbar.set)
self.category_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
ttk.Label(self.category_container,
text="应用分类",
font=("微软雅黑", 9),
padding=3).pack(pady=5)
def create_program_panel(self):
"""程序显示面板"""
self.program_frame = ttk.Frame(self.main_frame)
self.program_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
self.program_canvas = tk.Canvas(self.program_frame,
highlightthickness=0)
scrollbar = ttk.Scrollbar(self.program_frame,
orient="vertical",
command=self.program_canvas.yview)
self.program_container = ttk.Frame(self.program_canvas)
self.program_container.bind("<Configure>",
lambda e: self.program_canvas.configure(
scrollregion=self.program_canvas.bbox("all")
))
self.program_canvas.create_window((0,0),
window=self.program_container,
anchor="nw")
self.program_canvas.configure(yscrollcommand=scrollbar.set)
self.program_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def scan_directory(self):
"""扫描目录"""
self.categories = {"所有程序": []}
executable_ext = ['.exe', '.bat', '.cmd', '.lnk']
for item in os.listdir(self.current_dir):
path = os.path.join(self.current_dir, item)
if os.path.isdir(path):
self.categories[item] = []
for root, _, files in os.walk(path):
for file in files:
if os.path.splitext(file)[1].lower() in executable_ext:
full_path = os.path.join(root, file)
self.categories[item].append({
"name": os.path.splitext(file)[0],
"path": full_path
})
self.categories["所有程序"].append({
"name": os.path.splitext(file)[0],
"path": full_path
})
else:
if os.path.splitext(item)[1].lower() in executable_ext:
self.categories["所有程序"].append({
"name": os.path.splitext(item)[0],
"path": path
})
def populate_categories(self):
"""填充分类"""
for category in self.categories.keys():
btn = ttk.Button(
self.category_container,
text=category,
command=lambda c=category: self.show_category(c),
width=12
)
btn.pack(fill=tk.X, padx=2, pady=2)
def show_category(self, category):
"""显示分类"""
for widget in self.program_container.winfo_children():
widget.destroy()
programs = self.categories.get(category, [])
if not programs:
ttk.Label(self.program_container, text="该分类暂无应用").pack(pady=50)
return
max_columns = 5
row = col = 0
for idx, program in enumerate(programs):
col = idx % max_columns
row = idx // max_columns
button = UniformButton(
parent=self.program_container,
text=program["name"],
image=self.get_program_icon(program["path"]),
command=lambda p=program["path"]: self.launch_program(p)
)
button.grid(row=row, column=col, padx=3, pady=3, sticky="nsew")
self.program_container.grid_columnconfigure(col, weight=1)
self.program_container.grid_rowconfigure(row, weight=1)
def launch_program(self, path):
"""启动程序"""
try:
subprocess.Popen(f'"{path}"', shell=True)
except Exception as _e:
print(f"启动失败: {_e}")
if __name__ == "__main__":
root_window = tk.Tk()
style = ttk.Style()
style.configure("TButton",
padding=3,
font=("微软雅黑", 8),
width=12)
style.map("TButton",
background=[('active', '#f0f0f0')])
AppLauncher(root_window)
root_window.mainloop()