You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在tkinter应用运行终端命令时隐藏控制台并实现进度条等需求

实现方案

你需要对现有代码做以下几处核心调整,即可完全满足需求:

  • 替换os.system()为subprocess模块执行命令,通过参数隐藏控制台窗口,同时可以实时捕获命令输出用于进度判断
  • 把打包逻辑放到独立子线程执行,避免阻塞tkinter主线程导致界面假死
  • 解析pyinstaller的运行输出匹配不同执行阶段,同步更新进度条状态
  • 流程执行完成/失败时调用通知模块发送提醒

以下是修改后的完整可运行代码:

from tkinter import Tk, messagebox as mb, filedialog as fd
from tkinter.ttk import Button, Label, Progressbar
import os
import subprocess
import threading
import shutil
from win10toast import ToastNotifier

root = Tk()
root.title("exe生成工具")
root.geometry("500x500")
toaster = ToastNotifier()

# 打包阶段对应的进度值,可根据实际运行情况调整
PROGRESS_MAPPING = {
    "installed pyinstaller": 10,
    "Analyzing": 25,
    "Processing": 50,
    "Writing EXE": 75,
    "Moving files": 90,
    "Done": 100
}

def update_progress(value, text):
    """主线程更新UI的统一方法"""
    status.config(text=text)
    progress_bar['value'] = value

def create_exe_onefile(item):
    try:
        pyinstaller_path = os.path.join(os.path.dirname(os.__file__), "site-packages", "PyInstaller", "__main__.py")
        pyinstaller_path2 = os.path.join(os.environ.get("APPDATA", ""), "Python", "Scripts", "pyinstaller.exe")
        # 检查并安装pyinstaller
        if not (os.path.exists(pyinstaller_path) or os.path.exists(pyinstaller_path2)):
            update_progress(5, "正在安装PyInstaller...")
            # 隐藏窗口执行pip安装
            subprocess.run(
                ["pip", "install", "pyinstaller"],
                creationflags=subprocess.CREATE_NO_WINDOW,
                check=True,
                capture_output=True
            )
            update_progress(PROGRESS_MAPPING["installed pyinstaller"], "PyInstaller安装完成,开始打包...")
        else:
            update_progress(10, "检测到PyInstaller,开始打包...")

        # 生成目标文件夹
        os.makedirs("Apps Created\\Executables", exist_ok=True)
        
        # 执行pyinstaller打包,实时捕获输出
        cmd = ["pyinstaller", "--onefile", "-w", item]
        proc = subprocess.Popen(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            creationflags=subprocess.CREATE_NO_WINDOW,
            universal_newlines=True,
            bufsize=1
        )
        
        # 实时读取输出更新进度
        for line in proc.stdout:
            if "Analyzing" in line:
                root.after(0, update_progress, PROGRESS_MAPPING["Analyzing"], "正在分析依赖模块...")
            elif "Processing" in line:
                root.after(0, update_progress, PROGRESS_MAPPING["Processing"], "正在处理资源文件...")
            elif "Writing EXE" in line:
                root.after(0, update_progress, PROGRESS_MAPPING["Writing EXE"], "正在生成exe文件...")
        
        proc.wait()
        if proc.returncode != 0:
            raise Exception("PyInstaller打包执行失败")
        
        # 选择保存路径
        root.after(0, update_progress, PROGRESS_MAPPING["Moving files"], "正在整理文件...")
        path = fd.askdirectory(title="请选择exe保存位置")
        if path:
            fi = item.replace('.pyw', '')
            os.makedirs(f"Apps Created\\Executables\\{fi}", exist_ok=True)
            
            # 清理临时文件
            if os.path.exists(f"build\\{fi}"):
                shutil.rmtree(f"build\\{fi}")
            if os.path.exists("build") and len(os.listdir("build")) == 0:
                os.rmdir("build")
            if os.path.exists(f"dist\\{fi}.exe"):
                shutil.move(f"dist\\{fi}.exe", f"Apps Created\\Executables\\{fi}\\")
            if os.path.exists("dist"):
                shutil.rmtree("dist")
            if os.path.exists("__pycache__"):
                shutil.rmtree("__pycache__")
            if os.path.exists(f"{fi}.spec"):
                os.remove(f"{fi}.spec")
            
            # 复制到用户选择的路径
            target_exe = f"Apps Created\\Executables\\{fi}\\{fi}.exe"
            shutil.copy2(target_exe, path)
            
            root.after(0, update_progress, PROGRESS_MAPPING["Done"], "打包完成!")
            # 发送系统通知
            toaster.show_toast("exe生成成功", f"文件已保存到{path}", duration=5)
            mb.showinfo("完成", f"文件复制成功。\n你可以在{path}找到生成的exe文件,\n备份文件已保存在{os.getcwd()}\\Apps Created\\Executables")
    
    except Exception as err:
        root.after(0, lambda: mb.showerror("错误", f"生成exe时出现错误!\n{str(err)}"))
        toaster.show_toast("exe生成失败", f"错误信息:{str(err)}", duration=5)

def start_convert(item):
    """点击按钮后启动子线程执行打包"""
    stb.config(state="disabled")
    threading.Thread(target=create_exe_onefile, args=(item,), daemon=True).start()
    # 线程结束后恢复按钮状态
    def check_thread():
        for thread in threading.enumerate():
            if thread.name == "Thread-1":
                root.after(100, check_thread)
                return
        stb.config(state="normal")
    check_thread()


status = Label(root, text="点击按钮开始生成exe文件", font="Calibri 20")
status.pack(pady=50)

# 新增进度条
progress_bar = Progressbar(root, length=400, mode='determinate')
progress_bar.pack(pady=20)

stb = Button(root, text="开始生成", command=lambda: start_convert("main.pyw"))
stb.pack(pady=30)

root.mainloop()

注意事项

  • 使用前先安装依赖:pip install win10toast pyinstaller
  • 进度映射的数值可以根据你实际运行的体验调整,让进度展示更符合实际耗时
  • 所有UI更新操作都通过root.after调度到主线程执行,避免跨线程操作UI报错
  • 子线程设置为daemon守护线程,关闭界面时会自动结束打包进程

内容的提问来源于stack exchange,提问作者IJ_123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:54:02