Я написал небольшой пример программы, которая должна запускать несколько пакетов параллельных потоков после нажатия start_button
. Эта процедура может быть запущена только один раз, и после завершения одного пакета потоков предполагается, что пора выходить.
Так, например, он может go, например:
- Пакет 1 (10 потоков) работает, во время нажатия stop_button
. После выполнения пакета 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()
РЕДАКТИРОВАТЬ: Кажется, решение состоит в том, чтобы продолжать вызывать update()
в главном окне Tkinter после запуска потоков и проверять, пока все потоки не закончатся, прежде чем продолжить, для этого необходимы какой-то счетчик и 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()