【问题标题】:Tkinter: How use multithreading to keep a monitoring function active in the backgroundTkinter:如何使用多线程在后台保持监控功能处于活动状态
【发布时间】:2020-12-11 16:59:21
【问题描述】:

我正在制作一个用于在超级计算机上可视化 SLURM 队列的 GUI。我想通过定期通过 ssh(使用 paramiko)发送命令来获取队列、分析它并更新一些与 tk 标签相关的 tk 变量,从而持续监控队列中正在运行和待处理的作业的数量。

到目前为止,我一直在使用self.after() 语法来执行此操作,并确保不要过于频繁地进行后台 SSH 调用(以免导致界面中令人讨厌的冻结)。但是,我正在重写我的代码,并且我还想监控其他几个方面,但这太慢了界面。

我认为这可以通过使用threading 模块来解决,但我不确定如何正确设置它。对于这个问题,请考虑以下 MWE:

import tkinter as tk


class GUI(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)

        Home(self)


class Home(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.counter = tk.IntVar()
        self.counter2 = tk.IntVar()
        self.grid(row=0, column=0)

        tk.Label(self, text='Counter 1:').grid(row=0, column=0)
        tk.Label(self, textvar=self.counter).grid(row=0, column=1)

        tk.Label(self, text='Counter 2:').grid(row=1, column=0)
        tk.Label(self, textvar=self.counter2).grid(row=1, column=1)

        self.monitor_counter()
        self.monitor_counter2()

    def monitor_counter(self, end=100, delay=1000):
        time.sleep(0.2)
        if self.counter.get() < 0:
            self.parent.destroy()
        self.counter.set(end)
        self.after(int(delay), lambda: self.monitor_counter(end-1))

    def monitor_counter2(self, start=0, delay=1000):
        time.sleep(0.2)
        self.counter2.set(start)
        self.after(int(delay), lambda: self.monitor_counter2(start+1))


if __name__ == '__main__':
    GUI().mainloop()

它只是跟踪连接到某些标签的不断变化的IntVars,并使用 sleep 语句来模拟 SSH 调用。按照现在的设置方式,尽管计数器正在更新,但 GUI 不会显示。

如何修改上面的程序以将监控功能生成为它们自己的线程?假设监控应该始终处于活动状态,并且用户无法在不退出程序的情况下停用它们。另外,当退出程序时调用root.destroy(),当然应该终止线程。

【问题讨论】:

    标签: python multithreading tkinter python-multithreading


    【解决方案1】:

    以下是基于您使用线程的更新代码:

    import tkinter as tk
    import time
    import threading
    
    class GUI(tk.Tk):
        def __init__(self):
            tk.Tk.__init__(self)
            Home(self)
    
    class Home(tk.Frame):
        def __init__(self, parent):
            tk.Frame.__init__(self, parent)
            self.parent = parent
            self.counter = tk.IntVar()
            self.counter2 = tk.IntVar()
            self.grid(row=0, column=0)
    
            tk.Label(self, text='Counter 1:').grid(row=0, column=0)
            tk.Label(self, textvar=self.counter).grid(row=0, column=1)
    
            tk.Label(self, text='Counter 2:').grid(row=1, column=0)
            tk.Label(self, textvar=self.counter2).grid(row=1, column=1)
    
            # start the monitor tasks
            threading.Thread(target=self.monitor_counter, daemon=True).start()
            threading.Thread(target=self.monitor_counter2, daemon=True).start()
    
        def monitor_counter(self, end=100, delay=1):
            while True:
                time.sleep(0.2) # simulate SSH call
                if self.counter.get() < 0:
                    self.parent.destroy()
                end -= 1
                self.counter.set(end)
                time.sleep(delay)
    
        def monitor_counter2(self, start=0, delay=1):
            while True:
                time.sleep(0.2) # simulate SSH call
                self.counter2.set(start)
                start += 1
                time.sleep(delay)
    
    if __name__ == '__main__':
        GUI().mainloop()
    

    【讨论】:

    • 酷。我有类似的事情发生,但 GUI 的关闭并不漂亮。 'daemon=True' 使 GUI 漂亮地关闭。
    猜你喜欢
    • 2016-03-15
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-12
    • 2014-08-19
    • 2011-09-07
    相关资源
    最近更新 更多