每次想给 Python 脚本打包成 exe 做个顺手的工具,总卡在界面这关呀
想做得好看点吧,学前端得啃 HTML/CSS/JS,可能还得搭框架,对 Python 选手不够友好
用 Qt 吧,功能虽强,但随便加个窗口就多几十兆体积,打包出来太大了;tkinter 倒是很轻量,可调个按钮样式都要查半天文档,最终效果还总带着股vb风,实在拿不出手,关键跨平台不友好
直到有次偶尔间在github,发现了了 Gradio—— 这东西简直是为懒人量身定做的:嘿嘿不用学前端,纯 Python 代码就能拼出带交互的网页界面,页面不算太丑,下面直接上代码演示下


单击这个会下载文件
会自动生成一个api调用的

完整代码如下:
import os
import re
import shutil
import subprocess
import tempfile
import threading
import gradio as gr
class Py2ExeConverter:
def __init__(self):
self.convert_thread = None
self.stop_flag = False
self.progress_log = ""
self.lock = threading.Lock()
def convert_to_exe(self, python_file, onefile, noconsole, icon_file, app_name):
if not python_file:
return "请先选择 Python 文件!", None
self.stop_flag = False
self.progress_log = ""
temp_dir = tempfile.mkdtemp()
python_filename = os.path.basename(python_file)
python_path = os.path.join(temp_dir, python_filename)
shutil.copy(python_file, python_path)
icon_path = None
if icon_file:
icon_filename = os.path.basename(icon_file)
icon_path = os.path.join(temp_dir, icon_filename)
shutil.copy(icon_file, icon_path)
if not app_name:
app_name = os.path.splitext(python_filename)[0]
if not re.match(
r"^[a-zA-Z0-9_\-.\u4e00-\u9fa5][a-zA-Z0-9_\-.\u4e00-\u9fa5 ]*$", app_name
):
return"程序名称包含非法字符或格式不正确!", None
self.convert_thread = threading.Thread(
target=self._convert_thread,
args=(python_path, onefile, noconsole, icon_path, app_name, temp_dir),
daemon=True,
)
self.convert_thread.start()
return "转换已开始,请稍候...", None
def _convert_thread(
self, python_path, onefile, noconsole, icon_path, app_name, temp_dir
):
dist_dir = os.path.join(temp_dir, "dist")
build_dir = os.path.join(temp_dir, "build")
command = ["pyinstaller"]
if onefile:
command.append("--onefile")
else:
command.append("--onedir")
if noconsole:
command.extend(["--noconsole", "--windowed"])
command.extend(
[
"--clean",
"--distpath",
dist_dir,
"--workpath",
build_dir,
"--specpath",
temp_dir,
]
)
if icon_path and os.path.exists(icon_path):
command.extend(["--icon", icon_path])
command.extend(["--name", app_name, python_path])
full_command = " ".join(command)
with self.lock:
self.progress_log += f"执行命令: {full_command}\n\n"
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
)
for line in process.stdout:
if self.stop_flag:
process.terminate()
with self.lock:
self.progress_log += "\n已停止转换\n"
return
with self.lock:
self.progress_log += line
process.wait()
if process.returncode == 0:
exe_name = f"{app_name}.exe"
exe_path = os.path.join(dist_dir, exe_name)
if not os.path.exists(exe_path):
with self.lock:
self.progress_log += f"\n未找到输出文件: {exe_path}\n"
self.conversion_result = (False, "输出文件未找到", None)
return
safe_path = os.path.join(tempfile.gettempdir(), exe_name)
shutil.copy(exe_path, safe_path)
with self.lock:
self.progress_log += f"\n转换成功!输出文件: {safe_path}\n"
self.conversion_result = (True, "转换完成", safe_path)
else:
with self.lock:
self.progress_log += f"\n转换失败,返回码 {process.returncode}\n"
self.conversion_result = (False, "转换失败", None)
def check_conversion_status(self):
with self.lock:
log = self.progress_log
if hasattr(self, "conversion_result"):
result = self.conversion_result
if result[0]:
return log, result[2]
else:
return log, None
return log, None
def stop_conversion(self):
self.stop_flag = True
if self.convert_thread and self.convert_thread.is_alive():
return "已请求停止转换"
return "没有正在进行的转换"
converter = Py2ExeConverter()
with gr.Blocks(title="Python 打包转 EXE 工具", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🐍 Python 打包转 EXE 工具")
with gr.Row():
with gr.Column():
python_file = gr.File(label="选择 Python 文件", file_types=[".py"])
onefile = gr.Checkbox(label="生成单个文件 (--onefile)", value=True)
noconsole = gr.Checkbox(label="隐藏控制台 (--noconsole)", value=True)
icon_file = gr.File(label="图标文件 (.ico)", file_types=[".ico"])
app_name = gr.Textbox(label="程序名称", placeholder="输出的 EXE 名称")
with gr.Row():
convert_btn = gr.Button("开始转换", variant="primary")
stop_btn = gr.Button("停止转换", variant="stop")
status = gr.Textbox(label="日志输出", lines=20, interactive=False)
download_link = gr.File(label="下载生成的文件", visible=False)
with gr.Column():
gr.Markdown("### 使用说明")
gr.Markdown(
"""
1. 选择 `.py` 文件
2. 配置选项(单文件/隐藏控制台/图标)
3. 点击开始转换
4. 查看实时日志
5. 转换成功后可下载生成的 `.exe` 文件
需提前安装:
```
pip install pyinstaller
```
"""
)
convert_btn.click(
converter.convert_to_exe,
inputs=[python_file, onefile, noconsole, icon_file, app_name],
outputs=[status, download_link],
)
stop_btn.click(converter.stop_conversion, outputs=status)
def refresh():
log, file_path = converter.check_conversion_status()
if file_path:
return log, gr.File(value=file_path, visible=True)
return log, gr.File(visible=False)
gr.Timer().tick(refresh, outputs=[status, download_link])
if __name__ == "__main__":
demo.launch(server_name="127.0.0.1", server_port=7860, inbrowser=True)
快学试一试吧
参考文档:https://www.gradio.app/