在将执行工作函数作为单独的进程执行时,您能向我解释一下如何阻止我的python GUI冻结吗?
我编写了一个python GUI,只需按一下按钮,就可以通过多处理模块启动一个进程。我决定进行多处理和线程处理,因为我喜欢选择 启动,暂停,恢复和终止该过程。
不幸的是,当工作进程正在运行时,GUI会冻结并变得无法响应,因此我无法按下“暂停”按钮。按钮。
GUI的冻结问题在stackoverflow上多次报告,但似乎没有单一的此问题来源,也没有统一的解决方案。因此,我的问题不重复。
我目前根本不知道如何解决这个冻结问题。 到目前为止,我对解决方案的唯一猜测是使用一个名为mtTkinter的Tkinter的线程安全包装器,希望它也有助于多处理。但它没有做任何事情。
可能需要在GUI和工作进程之间添加另一层。
欢迎任何建议,提示和解决方案。
最小代码:
import Tkinter as tk
import time
import multiprocessing
import psutil
def test_function():
print 'Test process starting.'
for i in range(11):
print "Test process running, step: ", i
time.sleep(1)
print 'Test process finished.'
class MainScreen(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title("CardZilla 0.1")
self.resizable(0, 0)
self.s_button = tk.Button(self, text="START", command=self.on_start)
self.p_button = tk.Button(self, text="PAUSE", state='disabled', command=self.on_pause)
self.s_button.pack(side=tk.LEFT)
self.p_button.pack(side=tk.LEFT)
def update_all(self): #updates button states
b_list= {self.s_button, self.p_button}
for b in b_list:
b.update()
def on_start(self):
if self.s_button["text"] == "RESUME":
self.s_button["text"] = "START"
self.p_button["text"] = "PAUSE"
self.ps.resume() #resume command for process
else:
str = 'test arg'
#queue = Queue()
p = multiprocessing.Process(None, test_function)
p.start()
self.ps = psutil.Process(p.pid) #getting pid of process, how to move self.ps under def __init__(self): ?
self.s_button["state"] = 'disabled'
self.p_button["state"] = 'normal'
self.update_all()
p.join() #ending process
self.s_button["state"] = 'normal'
self.p_button["state"] = 'disabled'
self.update_all()
def on_pause(self):
if self.p_button["text"] == "PAUSE":
print 'Pause button clicked.'
self.s_button["text"] = "RESUME"
self.s_button["state"] = 'normal'
self.p_button["text"] = "CANCEL"
self.update_all()
self.ps.suspend() #pausing command for process
else:
self.s_button["text"] = "START"
self.s_button["state"] = 'normal'
self.p_button["text"] = "PAUSE"
self.p_button["state"] = "disabled"
self.update_all()
self.ps.terminate() #good to terminate via psutils versus native multiprocessing interface?
if __name__ == '__main__':
ms = MainScreen()
ms.mainloop()
答案 0 :(得分:3)
问题是您正在调用p.join()
等待您的流程退出on_start
功能:
p = multiprocessing.Process(None, test_function)
p.start()
self.ps = psutil.Process(p.pid) #getting pid of process, how to move self.ps under def __init__(self): ?
self.s_button["state"] = 'disabled'
self.p_button["state"] = 'normal'
self.update_all()
p.join() # this will block until `p` is complete
执行此操作可防止控制返回到事件循环,直到进程退出,这意味着直到发生这种情况并且on_start
退出,GUI将无响应。删除对join
的调用,应取消阻止GUI。
现在,这会暴露您设计的其他问题,因为您只想在完成该过程后更新GUI。您可以通过使用after
方法偶尔检查进程是否已完成来执行此操作。这样,事件循环基本上是未阻塞的,我们只是简单地阻止它以查看进程是否已完成。如果有,我们更新GUI按钮。如果没有,我们会安排检查方法在0.5
秒内再次运行。
def check_proc(self):
if not self.p.is_alive():
# Process is done. Update the GUI
self.p.join()
self.s_button["state"] = 'normal'
self.p_button["state"] = 'disabled'
self.update_all()
else:
# Not done yet. Check again later.
self.after(500, self.check_proc)
def on_start(self):
if self.s_button["text"] == "RESUME":
self.s_button["text"] = "START"
self.p_button["text"] = "PAUSE"
self.ps.resume() #resume command for process
else:
str = 'test arg'
#queue = Queue()
self.p = multiprocessing.Process(None, test_function)
self.p.start()
self.ps = psutil.Process(self.p.pid)
self.s_button["state"] = 'disabled'
self.p_button["state"] = 'normal'
self.update_all()
self.after(500, self.check_proc) # Check to see if the process is done in 0.5 seconds
#p.join() #ending process