Joh*_*hon 2 python user-interface tkinter freeze multiprocessing
你能向我解释一下,当将工作函数作为单独的进程执行时,如何防止 python GUI 冻结?
我编写了一个 python GUI,单击按钮即可通过多处理模块启动一个进程。我决定使用多处理而不是线程,因为我喜欢选择启动、暂停、恢复和终止进程。
不幸的是,当工作进程运行时,GUI 冻结并变得无响应,因此我无法按“暂停”按钮。
stackoverflow 上多次报告了 GUI 的冻结问题,但似乎没有该问题的单一来源,也没有统一的解决方案。因此我的问题不是重复的。
我目前完全不知道如何解决这个冻结问题。到目前为止,我对解决方案的唯一猜测是使用名为 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()
Run Code Online (Sandbox Code Playgroud)
问题是您正在调用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
Run Code Online (Sandbox Code Playgroud)
这样做可以防止控制权返回到事件循环,直到进程退出,这意味着在这种情况发生并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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4632 次 |
| 最近记录: |