如何在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
相关产品推荐
相关产品推荐

