Вот кое-что, чтобы продемонстрировать, как делать то, что вы хотите.Это работает, потому что код, работающий в потоках, отличных от основного, не вызывает никаких вызовов tkinter
.Чтобы заставить потоки запускаться последовательно один за другим, он использует FIFO Queue
записей, представляющих каждую из них, и запускает новые, запущенные после завершения последней.
Чтобы «запланировать» последовательность шагов, выполняемых в определенном порядке, как это делается в функции start_all()
, все, что нужно сделать, - это put()
информация о каждом из них в заказе.они должны выполняться в этом job_queue
.
. Все это достигается путем многократного использования универсального метода after()
для периодического запуска функции "опроса" (названной poll
), котораясреди прочего, проверяет, выполняется ли в данный момент другой поток или нет, и реагирует соответствующим образом.
В коде обработка, выполняемая каждым потоком, называется «шагом» или «заданием».
from queue import Queue, Empty
import random
import tkinter as tk
from tkinter import ttk
import tkinter.messagebox as tkMessageBox
from threading import Thread
from time import sleep
random.seed(42) # Generate repeatable sequence for testing.
ITERATIONS = 100
POLLING_RATE = 100 # millisecs.
# Global variables
cur_thread = None # Current running thread.
cur_button = None
cur_name = None
job_queue = Queue() # FIFO queue.
def sample_function():
for i in range(1, ITERATIONS):
print(i)
sleep(0.01) # Simulate slow I/O.
def start_thread(name, func, btn_variable):
global cur_thread, cur_button
if cur_thread is not None:
tkMessageBox.showerror('Error', "You can't start a step when there"
" are some already running.")
return
cur_thread = Thread(target=func)
cur_button = btn_variable
btn_variable.configure(style='blue.TButton')
cur_thread.start()
def poll(window, processing_bar):
global cur_thread, cur_button
if cur_thread is not None:
if cur_thread.is_alive():
processing_bar.step()
else:
cur_thread.join() # Should be immediate.
cur_thread = None
processing_bar.stop()
cur_button.configure(style='green.TButton')
window.update()
elif not job_queue.empty(): # More to do?
try:
job_info = job_queue.get_nowait() # Non-blocking.
start_thread(*job_info)
processing_bar.start()
window.update()
except Empty: # Just in case (shouldn't happen).
pass
window.after(POLLING_RATE, poll, window, processing_bar)
# Button commands.
def prepare_clicked():
start_thread('prepare', sample_function, prepare_btn)
def social_clicked():
start_thread('social', sample_function, social_btn)
def anomaly_clicked():
start_thread('anomaly', sample_function, anomaly_btn)
def scoring_clicked():
start_thread('scoring', sample_function, scoring_btn)
def dashboard_clicked():
start_thread('dashboard', sample_function, dashboard_btn)
def start_all():
global job_queue
# Put info for each step in the job queue to be run.
for job_info in (('prepare', sample_function, prepare_btn),
('social', sample_function, social_btn),
('anomaly', sample_function, anomaly_btn),
('scoring', sample_function, scoring_btn),
('dashboard', sample_function, dashboard_btn)):
job_queue.put(job_info)
# Start the polling.
window.after(POLLING_RATE, poll, window, processing_bar)
####
window = tk.Tk()
#window = tk.Toplevel()
topFrame = tk.Frame(window)
bottomFrame = tk.Frame(window)
# Tell the Frame to fill the whole window
topFrame.pack(fill=tk.BOTH, expand=1)
bottomFrame.pack(fill=tk.BOTH, expand=1)
# Make the Frame grid contents expand & contract with the window
topFrame.columnconfigure(0, weight=1)
for i in range(5):
topFrame.rowconfigure(i, weight=1)
bottomFrame.rowconfigure(0, weight=1)
for i in range(3):
bottomFrame.columnconfigure(i, weight=1)
ttk.Style().configure('blue.TButton', foreground='blue')
ttk.Style().configure('green.TButton', foreground='green')
ttk.Style().configure('red.TButton', foreground='red')
prepare_btn = ttk.Button(topFrame, command=prepare_clicked, text='Button 1', style='red.TButton')
anomaly_btn = ttk.Button(topFrame, command=anomaly_clicked, text='Button 2', style='red.TButton')
social_btn = ttk.Button(topFrame, command=social_clicked, text='Button 3', style='red.TButton')
scoring_btn = ttk.Button(topFrame, command=scoring_clicked, text='Button 4', style='red.TButton')
dashboard_btn = ttk.Button(topFrame, command=dashboard_clicked, text='Button 5', style='red.TButton')
commentary = ttk.Button(bottomFrame, text='START', width=10, command=start_all)
commentarylabel = ttk.Label(bottomFrame, text=' Commentary ', width=25)
processing_bar = ttk.Progressbar(bottomFrame, orient='horizontal', mode='indeterminate')
prepare_btn.grid (row=0, column=0, columnspan=1, sticky='EWNS')
anomaly_btn.grid (row=1, column=0, columnspan=1, sticky='EWNS')
social_btn.grid (row=2, column=0, columnspan=1, sticky='EWNS')
scoring_btn.grid (row=3, column=0, columnspan=1, sticky='EWNS')
dashboard_btn.grid (row=4, column=0, columnspan=1, sticky='EWNS')
commentary.grid (row=0, column=0, columnspan=1, sticky='EWNS')
commentarylabel.grid(row=0, column=1, columnspan=2, sticky='EWNS')
processing_bar.grid (row=0, column=3,columnspan=1, sticky='EWNS')
window.after(POLLING_RATE, poll, window, processing_bar)
window.mainloop()