这个打包工具包含以下功能:
1.主要功能:选择Python脚本文件,设置打包选项(单文件打包、无控制台窗口),自定义程序图标,指定输出目录,实时显示打包日志。
2.自适应布局改进:使用grid布局替代部分pack布局,增强布局控制,为所有容器设置grid_columnconfigure和grid_rowconfigure的weight参数,为Entry组件添加sticky="ew"使其可以水平扩展,日志区域使用nsew粘性设置,配合滚动条实现双向扩展,设置窗口最小尺寸限制minsize(600, 400)。
3.使用说明:选择要打包的Python脚本文件(.py),根据需要设置打包选项(可选)选择程序图标(.ico),(可选)添加版本信息文件选择组件(支持.rc,.txt文件),(可选)指定输出目录,点击"开始打包"按钮启动打包过程。
4.注意事项:电脑上需要提前安装PyInstaller:pip install pyinstaller,输出目录默认使用PyInstaller的默认设置(当前目录下的dist文件夹)。
# -*- coding: utf-8 -*-
# @Author : 小红牛
# 微信公众号:wdPython
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import subprocess
import os
import threading
class PyInstallerPacker:
def __init__(self, root):
self.root = root
root.title("PyInstaller打包exe工具1.0")
root.geometry("720x550")
root.minsize(640, 480)
# 配置根窗口网格布局
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
# 样式配置
self.style = ttk.Style()
self.style.configure("TButton", padding=6)
self.style.configure("TLabel", padding=3)
self.style.configure("TEntry", padding=3)
# 主框架
main_frame = ttk.Frame(root, padding=15)
main_frame.grid(row=0, column=0, sticky="nsew")
main_frame.grid_columnconfigure(1, weight=1)
main_frame.grid_rowconfigure(3, weight=1)
# 脚本选择组件
ttk.Label(main_frame, text="Python脚本路径:").grid(
row=0, column=0, sticky="w", pady=3)
self.script_entry = ttk.Entry(main_frame)
self.script_entry.grid(row=0, column=1, sticky="ew", padx=5)
ttk.Button(main_frame, text="浏览",
command=self.browse_script).grid(row=0, column=2, padx=5)
# 打包选项框架
options_frame = ttk.LabelFrame(main_frame, text="打包选项", padding=10)
options_frame.grid(row=1, column=0, columnspan=3,
pady=10, sticky="ew")
options_frame.grid_columnconfigure(1, weight=1)
# 打包选项内容
self.onefile_var = tk.BooleanVar()
ttk.Checkbutton(options_frame, text="单文件打包",
variable=self.onefile_var).grid(
row=0, column=0, sticky="w", padx=5)
self.noconsole_var = tk.BooleanVar(value=True)
ttk.Checkbutton(options_frame, text="无控制台窗口",
variable=self.noconsole_var).grid(
row=0, column=1, sticky="w", padx=5)
# 图标设置
ttk.Label(options_frame, text="程序图标:").grid(
row=1, column=0, sticky="w", pady=5)
self.icon_entry = ttk.Entry(options_frame)
self.icon_entry.grid(row=1, column=1, sticky="ew", padx=5)
ttk.Button(options_frame, text="选择图标",
command=self.browse_icon, width=10).grid(row=1, column=2)
# 版本信息设置
ttk.Label(options_frame, text="版本信息文件:").grid(
row=2, column=0, sticky="w", pady=5)
self.version_entry = ttk.Entry(options_frame)
self.version_entry.grid(row=2, column=1, sticky="ew", padx=5)
ttk.Button(options_frame, text="选择文件",
command=self.browse_version, width=10).grid(row=2, column=2)
# 输出目录设置
ttk.Label(main_frame, text="输出目录:").grid(
row=2, column=0, sticky="w", pady=3)
self.output_entry = ttk.Entry(main_frame)
self.output_entry.grid(row=2, column=1, sticky="ew", padx=5)
ttk.Button(main_frame, text="选择目录",
command=self.browse_output).grid(row=2, column=2, padx=5)
# 日志区域
log_frame = ttk.LabelFrame(main_frame, text="打包日志", padding=5)
log_frame.grid(row=3, column=0, columnspan=3,
pady=10, sticky="nsew")
log_frame.grid_rowconfigure(0, weight=1)
log_frame.grid_columnconfigure(0, weight=1)
self.log_text = tk.Text(log_frame, wrap=tk.WORD,
font=('Consolas', 10))
scrollbar = ttk.Scrollbar(log_frame, orient="vertical",
command=self.log_text.yview)
self.log_text.configure(yscrollcommand=scrollbar.set)
self.log_text.grid(row=0, column=0, sticky="nsew")
scrollbar.grid(row=0, column=1, sticky="ns")
# 打包按钮
self.pack_btn = ttk.Button(main_frame, text="开始打包",
command=self.start_packing)
self.pack_btn.grid(row=4, column=1, pady=10)
def browse_script(self):
"""选择Python脚本文件"""
path = filedialog.askopenfilename(
filetypes=[("Python文件", "*.py"), ("所有文件", "*.*")])
if path:
self.script_entry.delete(0, tk.END)
self.script_entry.insert(0, os.path.normpath(path))
def browse_icon(self):
"""选择图标文件"""
path = filedialog.askopenfilename(
filetypes=[("图标文件", "*.ico"), ("所有文件", "*.*")])
if path:
self.icon_entry.delete(0, tk.END)
self.icon_entry.insert(0, os.path.normpath(path))
def browse_version(self):
"""选择版本信息文件"""
path = filedialog.askopenfilename(
filetypes=[("资源文件", "*.rc *.txt"), ("所有文件", "*.*")])
if path:
self.version_entry.delete(0, tk.END)
self.version_entry.insert(0, os.path.normpath(path))
def browse_output(self):
"""选择输出目录"""
path = filedialog.askdirectory()
if path:
self.output_entry.delete(0, tk.END)
self.output_entry.insert(0, os.path.normpath(path))
def log_message(self, message):
"""在日志区域显示消息"""
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.root.update_idletasks()
def start_packing(self):
"""启动打包过程"""
script_path = self.script_entry.get()
if not script_path:
messagebox.showerror("错误", "请选择要打包的Python脚本!")
return
if not os.path.exists(script_path):
messagebox.showerror("错误", "指定的Python脚本不存在!")
return
# 构建PyInstaller命令
cmd = ["pyinstaller", "--clean"]
# 添加基本选项
if self.onefile_var.get():
cmd.append("--onefile")
if self.noconsole_var.get():
cmd.append("--onefile")
cmd.append("--noconsole")
# 添加图标
if icon_path := self.icon_entry.get():
if not os.path.exists(icon_path):
messagebox.showerror("错误", "指定的图标文件不存在!")
return
cmd.extend(["--icon", f'"{icon_path}"'])
# 添加版本信息
if version_path := self.version_entry.get():
if not os.path.exists(version_path):
messagebox.showerror("错误", "版本信息文件不存在!")
return
cmd.extend(["--version-file", f'"{version_path}"'])
# 添加输出目录
if output_dir := self.output_entry.get():
cmd.extend(["--distpath", f'"{output_dir}"'])
cmd.append(f'"{script_path}"')
# 初始化界面状态
self.pack_btn.config(state=tk.DISABLED)
self.log_text.delete(1.0, tk.END)
self.log_message(">>> 开始打包命令: " + " ".join(cmd))
# 启动后台线程执行打包命令
threading.Thread(
target=self.run_pack_command,
args=(cmd,),
daemon=True
).start()
def run_pack_command(self, cmd):
"""执行打包命令"""
try:
process = subprocess.Popen(
" ".join(cmd),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding="utf-8",
errors="replace"
)
# 实时读取输出
while True:
output = process.stdout.readline()
if output == "" and process.poll() is not None:
break
if output:
self.root.after(0, self.log_message, output.strip())
# 处理返回结果
return_code = process.poll()
if return_code == 0:
self.root.after(0, self.log_message, ">>> 打包成功完成!")
self.root.after(0, messagebox.showinfo,
"完成", "打包操作成功完成!")
else:
error_msg = f">>> 打包失败,错误码:{return_code}"
self.root.after(0, self.log_message, error_msg)
self.root.after(0, messagebox.showerror,
"错误", f"打包失败,错误码:{return_code}")
except Exception as e:
error_msg = f">>> 发生异常错误:{str(e)}"
self.root.after(0, self.log_message, error_msg)
self.root.after(0, messagebox.showerror,
"异常错误", f"发生未预期错误:{str(e)}")
finally:
self.root.after(0, self.pack_btn.config, {"state": tk.NORMAL})
if __name__ == "__main__":
root = tk.Tk()
app = PyInstallerPacker(root)
root.mainloop()
完毕!!感谢您的收看
----------★★跳转到历史博文集合★★----------
我的零基础Python教程,Python入门篇 进阶篇 视频教程 Py安装py项目 Python模块 Python爬虫 Json Xpath 正则表达式 Selenium Etree CssGui程序开发 Tkinter Pyqt5 列表元组字典数据可视化 matplotlib 词云图 Pyecharts 海龟画图 Pandas Bug处理 电脑小知识office自动化办公 编程工具 NumPy Pygame