首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >用gradio开发漂亮的python打包转exe工具

用gradio开发漂亮的python打包转exe工具

作者头像
小田测测看
发布2026-06-17 17:57:47
发布2026-06-17 17:57:47
440
举报

每次想给 Python 脚本打包成 exe 做个顺手的工具,总卡在界面这关呀

想做得好看点吧,学前端得啃 HTML/CSS/JS,可能还得搭框架,对 Python 选手不够友好

用 Qt 吧,功能虽强,但随便加个窗口就多几十兆体积,打包出来太大了;tkinter 倒是很轻量,可调个按钮样式都要查半天文档,最终效果还总带着股vb风,实在拿不出手,关键跨平台不友好

直到有次偶尔间在github,发现了了 Gradio—— 这东西简直是为懒人量身定做的:嘿嘿不用学前端,纯 Python 代码就能拼出带交互的网页界面,页面不算太丑,下面直接上代码演示下

单击这个会下载文件

会自动生成一个api调用的

完整代码如下:

代码语言:javascript
复制
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/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程拾光 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档