Как использовать многопоточность с tkinter для обновления метки и одновременного выполнения вычислений в потоке, управляемом событиями кнопок? - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь запустить счетчик, который отображает значение в метке в отдельном окне отображения. Главное окно имеет кнопки START, STOP и DISPLAY. START должен запустить счетчик, STOP должен остановить его, и окно дисплея должно открываться только тогда, когда я нажимаю кнопку DISPLAY.

Вот что у меня есть. Кажется, что кнопки не реагируют, и окно дисплея выскакивает без вмешательства пользователя. Как я могу это исправить?

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 ]

0 голосов
/ 02 мая 2018

Вы можете адаптировать что-то вроде следующего:

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)
0 голосов
/ 02 мая 2018

Я вижу две проблемы с вашим кодом. Во-первых, это просто простые ошибки, например:

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

Я упустил некоторые детали, чтобы упростить пример. Даже с его дополнительными проверками, это все еще далеко не идеально. (Например, он зависает, если вы не «остановитесь» перед закрытием окна и т. Д.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...