Tkinter GUI在启动线程后无响应

问题描述 投票:1回答:1

我写了一个程序的小例子,应该在按下start_button之后开始运行多批并行线程。此过程只能启动一次,并且在一批线程完成后,应该检查是否该退出。因此,例如,它可以像这样:-在按下stop_button的过程中,批处理1(10个线程)正在运行。批处理1完成后,程序应在不运行批处理2的情况下停止运行,并返回初始状态(同样可以选择启动此过程)。

但是,在此过程中,GUI似乎根本无法注册点击或任何操作。好像冻结了。因此,我应该以某种方式将执行其任务的线程与执行其任务的GUI分开,但我不知道具体如何。

import threading
import tkinter as tk
import time
import random


class Blocking():
    def __init__(self):
        self.master = tk.Tk()
        self.master.geometry("400x400")

        self.start_button = tk.Button(self.master, command=self.long_task, text='press me to start', state='normal')
        self.start_button.pack()

        self.stop_button = tk.Button(self.master, command=self.stop_func, text='press me to stop', state='normal')
        self.stop_button.pack()
        self.long_task_was_stopped = False

        self.master.mainloop()

    def one_thread(self, thread_index):
        time.sleep(random.randint(5, 10))

    def long_task(self): # will run many batches of parallel one_thread functions on press of start_button
        self.start_button["state"] = 'disabled'
        # first batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        for thread in threads:
            thread.join()

        print("First batch over!")
        # batch over, check if it was stopped
        print("Stop variable value:", self.long_task_was_stopped)
        if self.long_task_was_stopped == True:
            # reset states, quit function
            self.long_task_was_stopped = False
            self.start_button["state"] = 'normal'
            print("Stopped, exiting!")
            return

        # second batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        for thread in threads:
            thread.join()

        print("Second batch over!")
        self.long_task_was_stopped = False
        self.start_button["state"] = 'normal'
        print("Done.")
        return

    def stop_func(self):
        print("Trying to stop...")
        self.long_task_was_stopped = True



if __name__ == '__main__':
    block = Blocking()  

EDIT:

似乎解决方案是在线程启动后继续在Tkinter主窗口上调用update(),并检查直到所有线程都结束后再进行操作,为此需要某种计数器和threading.Lock()。这是解决方案。
import threading
import tkinter as tk
import time
import random


class Blocking():
    def __init__(self):
        self.master = tk.Tk()
        self.master.geometry("400x400")

        self.start_button = tk.Button(self.master, command=self.long_task, text='press me to start', state='normal')
        self.start_button.pack()

        self.stop_button = tk.Button(self.master, command=self.stop_func, text='press me to stop', state='normal')
        self.stop_button.pack()
        self.long_task_was_stopped = False

        self.LOCK = threading.Lock()
        self.count_of_done_threads = 0
        self.master.mainloop()


    def one_thread(self, thread_index):
        time.sleep(random.randint(5, 10))
        with self.LOCK:
            print("Thread", thread_index, "done.")
            self.count_of_done_threads = self.count_of_done_threads +1

    def long_task(self): # will run many batches of parallel one_thread functions on press of start_button
        self.start_button["state"] = 'disabled'
        self.long_task_was_stopped = False

        # first batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        # wait until threads are done
        while 1:
            self.master.update()
            if self.count_of_done_threads == 10: # 10 here is size of batch
                break
        self.count_of_done_threads = 0

        print("First batch over!")
        # batch over, check if it was stopped
        print("Stop variable value:", self.long_task_was_stopped)
        if self.long_task_was_stopped == True:
            # reset states, quit function
            self.long_task_was_stopped = False
            self.start_button["state"] = 'normal'
            print("Stopped, exiting!")
            return

        # second batch of threads
        threads = []
        for thread_number in range(0,10):
            thread = threading.Thread(target=self.one_thread, args=(thread_number,))
            threads.append(thread)
            thread.start()

        # wait until threads are done
        while 1:
            self.master.update()
            if self.count_of_done_threads == 10:
                break
        self.count_of_done_threads = 0

        print("Second batch over!")
        self.long_task_was_stopped = False
        self.start_button["state"] = 'normal'
        print("Done.")
        return

    def stop_func(self):
        print("Trying to stop...")
        self.long_task_was_stopped = True

if __name__ == '__main__':
    block = Blocking()  

我写了一个程序的小例子,应该在按下start_button后开始运行多批并行线程。此过程只能启动一次,一个之后就可以启动...

python-3.x multithreading tkinter python-multithreading
1个回答
0
投票

您应该使用non-block thread。只需thread.start()可以。在official document

© www.soinside.com 2019 - 2024. All rights reserved.