I wrote a small example of a program that is supposed to start running multiple batches of parallel threads after a start_button
is pressed. This procedure can be launched just once, and after one batch of threads finishes, it is supposed to check if it is time to exit.
So for example, it can go like:
- Batch 1 (10 threads) is running, during it stop_button
is pressed. After batch 1 is done, program should stop without running batch 2 and return to initial state (again having the option to launch this procedure).
But, GUI doesn't seem to be able to register a click or anything at all during this procedure. It just seems frozen. So I'm supposed to somehow separate threads doing their thing from GUI doing its thing, but I don't know how exactly.
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: It seems the solution is to keep calling update()
on main Tkinter window after threads are started and check until all threads are over before proceeding, some kind of counter and threading.Lock()
are needed for this. Here is the solution.
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()