Python Tkinter使用多进程/多线程时程序冻结问题求助
问题根因
你的代码存在4个核心错误,直接导致了进程阻塞、执行顺序异常:
- 多进程启动逻辑未做平台兼容:Windows系统下
multiprocessing模块的进程启动代码必须放在if __name__ == "__main__":判断块内,否则模块导入时会重复执行代码,导致逻辑混乱 multiprocessing.Process传参逻辑完全错误:你定义p2的时候直接执行了os.system(f"{filedialog.askopenfilename(title = 'Choose the execution File')}")这行代码,相当于先同步执行完弹框选文件、运行选中程序直到退出的全流程,才会把os.system返回的整数状态码当成target传给Process,根本没有实现把这部分逻辑放到子进程运行的效果- Tkinter组件跨进程调用违规:Tkinter所有GUI相关操作只能在创建Tk实例的线程/进程中执行,跨进程调用文件选择框会出现未定义行为
- 技术选型冗余:调用独立外部程序不需要额外开进程/线程,直接用
subprocess.Popen就可以实现非阻塞启动,完全不会卡住GUI主线程
修复方案
推荐你完全放弃用多进程启动外部程序的逻辑,把文件选择、启动外部程序的逻辑放到GUI的回调函数里,用非阻塞接口启动即可,修复后的参考代码如下:
import os import subprocess import tkinter.messagebox as msgbox import io, hashlib, queue, sys, time, threading, traceback from tkinter import * import tkinter as tk from tkinter import filedialog class Layout(tk.Frame): def __init__(self, root): tk.Frame.__init__(self, root) self.canvas = tk.Canvas(self, width=200, height=750, background="white") self.xsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview) self.ysb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview) self.canvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set) self.canvas.configure(scrollregion=(0,0,1000,1000)) self.xsb.grid(row=1, column=0, sticky="ew") self.ysb.grid(row=0, column=1, sticky="ns") self.canvas.grid(row=0, column=0, sticky="nsew") self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) self.canvas.fontSize = 12 # 鼠标拖动支持 self.canvas.bind("<ButtonPress-1>", self.move_start) self.canvas.bind("<B1-Motion>", self.move_move) # Linux缩放支持 self.canvas.bind("<Button-4>", self.zoomerP) self.canvas.bind("<Button-5>", self.zoomerM) # Windows缩放支持 self.canvas.bind("<MouseWheel>",self.zoomer) # 拖动方法 def move_start(self, event): self.canvas.scan_mark(event.x, event.y) def move_move(self, event): self.canvas.scan_dragto(event.x, event.y, gain=1) # Windows缩放方法 def zoomer(self, event): if (event.delta > 0): self.canvas.scale("all", event.x, event.y, 1.1, 1.1) self.canvas.fontSize = self.canvas.fontSize * 1.1 elif (event.delta < 0): self.canvas.scale("all", event.x, event.y, 0.9, 0.9) self.canvas.fontSize = self.canvas.fontSize * 0.9 for child_widget in self.canvas.find_withtag("text"): self.canvas.itemconfigure(child_widget, font = ("Helvetica", int(self.canvas.fontSize))) self.canvas.configure(scrollregion = self.canvas.bbox("all")) # Linux放大方法 def zoomerP(self, event): self.canvas.scale("all", event.x, event.y, 1.1, 1.1) self.canvas.fontSize = self.canvas.fontSize * 1.1 for child_widget in self.canvas.find_withtag("text"): self.canvas.itemconfigure(child_widget, font = ("Helvetica", int(self.canvas.fontSize))) self.canvas.configure(scrollregion = self.canvas.bbox("all")) # Linux缩小方法 def zoomerM(self,event): self.canvas.scale("all", event.x, event.y, 0.9, 0.9) self.canvas.fontSize = self.canvas.fontSize * 0.9 for child_widget in self.canvas.find_withtag("text"): self.canvas.itemconfigure(child_widget, font = ("Helvetica", int(self.canvas.fontSize))) self.canvas.configure(scrollregion = self.canvas.bbox("all")) def run_external_program(): # 弹出文件选择框 exe_path = filedialog.askopenfilename(title = 'Choose the execution File') if not exe_path: return # 非阻塞启动外部程序,完全不卡GUI subprocess.Popen(exe_path, shell=True) def main_window(): root = Tk() root.title("Seoul Cell") root.geometry("1300x1000") root.resizable(True, True) monitor1 = Layout(root) monitor1.pack(fill="both", expand=True) # 메뉴바 생성 menu = Menu(root) root.config(menu=menu) menu_file = Menu(menu, tearoff=0) menu_file.add_command(label="Open Cell Information") menu_file.add_command(label="Partitioning") menu_file.add_command(label="Pairing") menu_file.add_command(label="Placement") menu_file.add_command(label="Routing") # 新增启动外部程序的菜单选项 menu_file.add_command(label="Run External Program", command=run_external_program) menu.add_cascade(label="Task", menu = menu_file ) menu_work = Menu(menu, tearoff= 0) menu_work.add_command(label="Open layerinfo file") menu_work.add_command(label="save as layerinfo.txt") menu.add_cascade(label="TechInfo", menu = menu_work) # 第二菜单栏 menu2_frame = Frame(root) menu2_frame.pack(fill = "x") layer_but = Button(menu2_frame, text = "Layers") layer_but.pack(side = "left", padx=3, pady=3) # 脚本区域 script_frame = LabelFrame(root, text = "script") script_frame.pack(fill='x', side="bottom",padx=10, pady=10) script_menu_frame = Frame(script_frame) script_menu_frame.pack(fill='x') # 脚本文本框 scrollbar_script = Scrollbar(script_frame) scrollbar_script.pack(side='right',fill="y") script = Text(script_frame, height=20, yscrollcommand=scrollbar_script.set) script.pack(fill='x', side="bottom", padx=10, pady=10) scrollbar_script.config(command=script.yview) root.mainloop() if __name__ == "__main__": main_window()
修复后你可以点击Task菜单下的Run External Program选项唤起文件选择框,选中程序后会直接非阻塞启动,GUI全程不会卡顿。
内容的提问来源于stack exchange,提问作者onixcurve
相关产品推荐
相关产品推荐

