便携工具箱 自动识别文件夹和可执行程序

只需要在同目录建立文件夹放入可执行exe、bat、等程序,运行该程序自动识别分类,无需配置文件。

下载地址:

https://wwvl.lanzout.com/iVLxH2rc7nbi

密码:52pj

便携工具箱   自动识别文件夹和可执行程序

便携工具箱   自动识别文件夹和可执行程序

编译exe用的是:
人懒所以做个python打包exe工具,支持复杂程序打包 1.1
https://bbs.songma.com/thread-2007933-1-1.html
(出处: 论坛)

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 "
".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()

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 共47条

请登录后发表评论