当前位置: 首页 > 知识库问答 >
问题:

Tkinter GUI在使用多处理时冻结。过程

尤祖鹤
2023-03-14

你能解释一下,当作为一个单独的进程执行辅助函数时,我是如何防止python GUI冻结的吗?

我编写了一个python GUI,点击一个按钮,就可以通过多处理模块启动一个进程。我决定使用多处理而不是线程,因为我喜欢选择启动、暂停、恢复和终止进程。

不幸的是,当辅助进程运行时,GUI会冻结并失去响应,因此我无法按下“暂停”按钮。

图形用户界面的冻结问题在stackoverflow上报告过几次,但是这个问题似乎没有一个单一的来源,也没有一个统一的解决方案。因此我的问题不是重复的。

我目前完全不知道如何解决这个冰冻问题。到目前为止,我对解决方案的唯一猜测是为Tkinter使用一个名为mtTkinter的线程安全包装器,希望它也能帮助进行多处理。但它什么也没做。

也许需要在GUI和工作进程之间html" target="_blank">添加另一层。

欢迎提供任何建议、提示和解决方案。

最小代码:

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()

共有1个答案

蔚元明
2023-03-14

问题是,您正在调用p.join()以等待进程在启动时退出函数:

           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。您可以使用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

 类似资料:
  • 问题内容: 我正在尝试并行化脚本,但是由于未知的原因,内核只是冻结而没有引发任何错误。 最小的工作示例: 有趣的是,如果我在另一个文件中定义函数然后将其导入,则一切都可以正常工作。如何使它工作而无需另一个文件? 我使用spyder(anaconda),如果从Windows命令行运行代码,则结果相同。 问题答案: 发生这种情况是因为在子进程导入时,您没有保护代码的“过程”部分免于重新执行。 它们需要

  • TLDR:我有一个pyspark作业,当我在具有16个vcpus的ec2实例中运行它时,它会在10分钟内完成,但如果我使用具有超过20个vcpus的实例,它会冻结(它不会失败,只是永远不会完成)。我已经尝试了我能想到的一切,我只是不知道为什么会发生这种情况。 完整故事: 我有大约200个小型pyspark作业,出于成本和灵活性的考虑,我使用aws batch与spark dockers而不是EMR

  • 我已经在处理中编写了一个算法来执行以下操作: 由于某种原因,这个算法会立即冻结。我在里面放了打印语句,显示它甚至在试图加载图像之前就冻结了。考虑到我已经编写了另一个非常相似的算法,并且执行起来没有并发症,这让我特别困惑。另一种算法读取图像,对指定大小的每块瓷砖的颜色取平均值,然后在用平均颜色取平均值的区域上打印矩形,有效地使图像像素化。两种算法都加载图像并检查其每个像素。这个算法的主要区别在于它没

  • 我最近在我的项目中添加了一个很棒的UI库'antd'。https://ant.design/docs/react/introve

  • 问题内容: 我有一个程序在其中加载文件,同时显示一个窗口以通知用户正在加载文件。我决定制作一个FileLoader类,它是一个实际上处理了文件加载的SwingWorker类,以及一个实现PropertyChangeListener的ProgressWindow,用于通知用户传递给它的SwingWorker的状态。 我的代码当前如下所示: 问题是,每当我调用loader.get()时,它都会冻结GU

  • 问题内容: 我有一个JSON,我需要对其进行一些处理。它使用我需要以某种方式引用的切片,以便在函数末尾修改Room-struct。如何通过按引用类型同时使用此结构? http://play.golang.org/p/wRhd1sDqtb 问题答案: 您的逻辑中有两个不同的问题:第一个是切片本身的操作方式,第二个是实际的并发问题。 对于分片操作,仅按值传递分片作为参数将意味着您将无法以必须在增加分片