如何阻止点击按钮时多次打开窗口(multiprocessing场景)
解决Tkinter结合multiprocessing点击按钮重复打开窗口的问题
问题现象
点击Run按钮后,程序会多次弹出相同的Tkinter窗口,这是multiprocessing子进程触发重复GUI初始化导致的。
问题根源
在Windows系统中,multiprocessing创建子进程时会重新导入主模块。你的代码里root = Tk()等GUI初始化逻辑写在模块顶层,没有被if __name__ == "__main__"包裹,导致每个子进程启动时都会执行这部分代码,创建新窗口。另外,dostuff函数内的if (__name__ == "__main__")判断无效——该函数在主进程的子线程中执行,__name__始终为__main__,但子进程的模块导入不受这个内部判断影响。
修复方案
- 将所有GUI初始化代码放入
if __name__ == "__main__"块,确保只有主进程会创建GUI窗口 - 子进程仅处理后台任务:禁止子进程操作任何Tkinter元素(Tkinter不是进程安全的)
- 分离GUI与业务逻辑:后台处理(PDF解析)和GUI更新完全拆分,GUI操作仅在主进程执行
修改后的完整代码
import os import time import datetime import multiprocessing from functools import partial from tkinter import Tk, StringVar, Label, Entry, Button, messagebox, DISABLED, NORMAL from tkinter import ttk # 辅助函数:仅处理业务逻辑,不涉及GUI操作 def get_listof_files(folder, ext): # 实现你的文件列表获取逻辑 pass def get_total_txt(failed, pdf_path): # 实现PDF处理逻辑,仅处理数据 try: # 此处编写PDF解析代码 pass except Exception as e: failed.append(pdf_path) def disable_event(): # 实现窗口关闭禁用逻辑 pass No_grid_number = [] # 全局统计变量 def dostuff(root, run_btn, folderPath): try: folder = folderPath.get() if folder != "": if os.path.exists(folder): run_btn['state'] = DISABLED msg = "Please wait... Process being done.\n Dont close the app." label = Label(root, text=msg, foreground="red") label.grid(row=5, column=0, padx=20) label.update() root.protocol("WM_DELETE_WINDOW", disable_event) pdfs_list = get_listof_files(folder, "pdf") start_time = datetime.now() with multiprocessing.Manager() as manager: failed = manager.list() if pdfs_list: # 进程池仅处理PDF业务,不碰GUI with multiprocessing.Pool(5) as p: p.map(partial(get_total_txt, failed), pdfs_list) # 所有GUI更新操作由主进程完成 label.destroy() suss_files_count = len(pdfs_list)-len(failed) print(f"Out of {len(pdfs_list)} files {suss_files_count} Succeeded and {len(failed)} files found without data {len(No_grid_number)} Files Without Grid Numbers and {round(suss_files_count/(len(pdfs_list))*100, 2)}% loaded.") msg = f"\nProcess has been Completed Sucessfully..!\n Out of {len(pdfs_list)} files {suss_files_count} Succeeded and {len(failed)} files found without data.\n Please check Log for More info." label = Label(root, text=msg, foreground="green") label.grid(row=5, column=0, padx=20) label.update() time.sleep(5) label.destroy() end_time = datetime.now() print(f'Duration: {(end_time - start_time).total_seconds()/60} Minutes') run_btn['state'] = NORMAL root.protocol("WM_DELETE_WINDOW", root.destroy) # 恢复正常关闭逻辑 else: messagebox.showerror("Path Not Found", f"Selected path or directory doesn't exists: {folder}") else: messagebox.showwarning("Warning", "Please Select path") except AttributeError as attbe: messagebox.showerror("AttributeError", str(attbe)) run_btn['state'] = NORMAL except FileNotFoundError as fne: messagebox.showerror("FileNotFoundError", str(fne)) run_btn['state'] = NORMAL except TypeError as TError: messagebox.showerror("TypeError", str(TError)) run_btn['state'] = NORMAL def getFolderPath(): # 实现文件夹选择逻辑 pass if __name__ == "__main__": # 所有GUI初始化逻辑仅由主进程执行 root = Tk() root.title("PDF Reader") root.geometry("500x300+500+300") root.maxsize(width=None, height=None) root.resizable(False, False) folderPath = StringVar() a = Label(root, text="\nChoose Path:", font=('Arial')) a.grid(row=0, column=0) e = Entry(root, textvariable=folderPath, width=50) e.grid(row=3, column=0, padx=35) btn_browse = ttk.Button(root, text="Browse", command=getFolderPath, padding=5) btn_browse.grid(row=3, column=1) run_btn = Button(root, text="Run", command=lambda: multiprocessing.Process(target=dostuff, args=(root, run_btn, folderPath)).start(), width=30, height=2, padx=10) run_btn.grid(row=4, column=0) root.lift() root.mainloop()
关键改动说明
- 把
root = Tk()及所有GUI元素创建代码移到if __name__ == "__main__"块,避免子进程重复初始化GUI - 调整
dostuff启动方式:用multiprocessing.Process托管后台任务,确保业务逻辑与GUI完全隔离 - 子进程执行的
get_total_txt仅处理PDF数据,不涉及任何Tkinter操作 - 所有GUI状态更新(按钮状态、提示标签)均在主进程完成
内容的提问来源于stack exchange,提问作者Vignesh Vangala
相关产品推荐
相关产品推荐

