代码之家  ›  专栏  ›  技术社区  ›  Shawn

如何在由按钮事件控制的线程中使用多线程和tkinter来更新标签并同时执行计算?

  •  0
  • Shawn  · 技术社区  · 7 年前

    我试图启动一个计数器,在单独的显示窗口上的标签中显示值。 主窗口有一个开始、停止和显示按钮。 启动必须启动计数器,停止必须停止计数器,只有当我单击显示按钮时,显示窗口才能打开。

    这是我到目前为止的情况。按钮似乎没有响应,显示窗口弹出,没有用户干预。我怎样才能解决这个问题?

    import tkinter as tk
    import time
    import threading
    import queue
    
    
    def Run_Device(disp_q,flaq_q):
        temp_q = queue.Queue()
        temp_q.put(0)
        while(flaq_q.empty()):
            #time.sleep(0.2)
            count = temp_q.get()
            count += 1
            temp_q.put(count)
            disp_q.put(count)
        else:
            flaq_q.queue.clear()
    
    
    def P_Window(disp_q):
        pw = tk.Tk()
        value_label = tk.Label(pw, text=disp_q.get(), relief='sunken', bg='lemon chiffon', font='Helvetica 16 bold')
        value_label.pack()
        def update_values():
            value_label.config(text=disp_q.get())  
            value_label.after(1000,update_values)   
        update_values()
    
        pw.mainloop()
    
    def Stop_Dev(flaq_q):
        flaq_q.put("Stop")
    
    
    if __name__ == "__main__":
        disp_q = queue.Queue()
        flaq_q = queue.Queue()
    
        t_device = threading.Thread(target=Run_Device, args=(disp_q, flaq_q), name="Device 1")
        t_disp = threading.Thread(target=P_Window, args=(disp_q, ), name="Display 1")
    
        window = tk.Tk()
    
        start_button = tk.Button(window, text='Start', command=t_device.start(), bg='spring green', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
        start_button.pack()
    
        stop_button = tk.Button(window, text='Stop', command=lambda: Stop_Dev(flaq_q), bg='OrangeRed2', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
        stop_button.pack()
    
        disp_param_button = tk.Button(window, text='Display', command=t_disp.start(), bg='sky blue', font='Helvetica 12 bold', width=20, state='normal', relief='raised')
        disp_param_button.pack()
    
        window.mainloop()
    

    我正在尝试学习如何在tkinter中使用多线程,以便获得任何反馈

    2 回复  |  直到 7 年前
        1
  •  0
  •   cdlane    7 年前

    我发现您的代码有两个问题。第一个只是简单的bug,例如:

    start_button = tk.Button(window, text='Start', command=t_device.start(), ...
    

    应该在这里 command=t_device.start 否则,该命令是 t_device.start()

    第二,你没有处理过各种各样的“如果……怎么办?”场景,例如,如果用户多次按下“开始”或“显示”,该怎么办?

    我已尝试在下面的返工中解决上述问题:

    import tkinter as tk
    from time import sleep
    from queue import Queue, Empty
    from threading import Thread
    
    FONT = 'Helvetica 16 bold'
    
    def run_device():
        count = 0
    
        while flaq_q.empty():
            count += 1
            disp_q.put(count)
            sleep(0.5)
    
        while not flaq_q.empty():  # flaq_q.queue.clear() not documented
            flaq_q.get(False)
    
    def p_window():
        global pw
    
        if pw is None:
    
            pw = tk.Toplevel()
    
            value_label = tk.Label(pw, text=disp_q.get(), width=10, font=FONT)
            value_label.pack()
    
            def update_values():
                if not disp_q.empty():
                    try:
                        value_label.config(text=disp_q.get(False))
                    except Empty:
                        pass
                pw.after(250, update_values)
    
            update_values()
    
        elif pw.state() == 'normal':
            pw.withdraw()
        else:
            pw.deiconify()
    
    def stop_device():
        if flaq_q.empty():
            flaq_q.put("Stop")
    
    def start_device():
        global device
    
        if device and device.is_alive():
            return
    
        while not disp_q.empty():
            disp_q.get(False)
    
        disp_q.put(0)
    
        device = Thread(target=run_device)
        device.start()
    
    if __name__ == "__main__":
        disp_q = Queue()
        flaq_q = Queue()
    
        root = tk.Tk()
    
        pw = None
        device = None
    
        tk.Button(root, text='Start', command=start_device, width=20, font=FONT).pack()
        tk.Button(root, text='Stop', command=stop_device, width=20, font=FONT).pack()
        tk.Button(root, text='Display', command=p_window, width=20, font=FONT).pack()
    
        root.mainloop()
    

    为了简化示例,我省略了一些细节。即使有额外的检查,它仍然远远不够完美。(例如,如果你在关窗前不“停车”,它就会挂起,等等)

        2
  •  0
  •   10SecTom    7 年前

    您可能可以调整以下内容:

    import tkinter as tk
    import threading
    import queue
    import time
    
    
    def run_device():
        for i in range(4):
            print(i)
        pass
    
    
    def process_queue(MyQueue):
        """Check if we got a complete message from our thread.
        Start the next operation if we are ready"""
    
        try:
            # when our thread completes its target function (task),
            # the supplied message is added to the queue
            msg = MyQueue.get(0)
            # check message
            print(msg)
        except queue.Empty:
            # .get failed, come back in 100 and check again
            print('no message')
            threading.Timer(0.001, lambda q=MyQueue: process_queue(q)).start()
    
    
    class ThreadedTask(threading.Thread):
        """threaded task handler"""
        def __init__(self, queue, target, msg):
            threading.Thread.__init__(self)
            # message to add to queue when the task (target function) completes
            self.msg = msg
            # function to run
            self._target = target
            # queue to store completion message
            self.queue = queue
    
        def run(self):
            """called when object is instantiated"""
            # start users task
            try:
                self._target()
            except Exception as e:
                self.queue.put('Thread Fail')
                return
            # we are done, pass the completion message
            self.queue.put(self.msg)
    
    
    if __name__ == '__main__':
        MyQueue = queue.Queue()
        MyThread = ThreadedTask(MyQueue, run_device, 'Thread Task: Run')
        MyThread.start()
        process_queue(MyQueue)