我写了一个程序的小例子,该程序应该在按下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()
编辑:似乎解决方案是在线程启动后继续在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()
答案 0 :(得分:0)
您应该使用non-block thread
。只需thread.start()
就可以了。
在official document中:
其他线程可以调用线程的join()方法。这将阻塞调用线程,直到终止调用join()方法的线程为止。
这意味着只有当函数one_thread
完成工作后,您的代码才会运行。
您的代码可能应该是:
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()
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()
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()
我最小的示例是抓取Web图片。(运行此代码时,完成抓取时,它会显示loading...
。
import tkinter
from PIL import ImageTk,Image
import threading,requests
from io import BytesIO
def scrapImage():
image = ImageTk.PhotoImage(Image.open(BytesIO(requests.get('https://s2.ax1x.com/2020/02/08/1Wl4mT.md.jpg').content)).resize((200,200))) # crawl my image
imagePos.image = image # keep reference.
imagePos['image'] = image
root = tkinter.Tk()
thread = threading.Thread(target=scrapImage)
thread.start()
imagePos = tkinter.Label(text="loading.....")
imagePos.grid()
root.mainloop()