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

如何阻止点击按钮时多次打开窗口(multiprocessing场景)

解决Tkinter结合multiprocessing点击按钮重复打开窗口的问题

问题现象

点击Run按钮后,程序会多次弹出相同的Tkinter窗口,这是multiprocessing子进程触发重复GUI初始化导致的。

问题根源

在Windows系统中,multiprocessing创建子进程时会重新导入主模块。你的代码里root = Tk()等GUI初始化逻辑写在模块顶层,没有被if __name__ == "__main__"包裹,导致每个子进程启动时都会执行这部分代码,创建新窗口。另外,dostuff函数内的if (__name__ == "__main__")判断无效——该函数在主进程的子线程中执行,__name__始终为__main__,但子进程的模块导入不受这个内部判断影响。

修复方案

  1. 将所有GUI初始化代码放入if __name__ == "__main__"块,确保只有主进程会创建GUI窗口
  2. 子进程仅处理后台任务:禁止子进程操作任何Tkinter元素(Tkinter不是进程安全的)
  3. 分离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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:25:17