Tkinter:通过按钮点击执行顺序线程调度

时间:2018-05-27 11:41:40

标签: python multithreading python-3.x tkinter python-multithreading

我有一个带有多个按钮的Tkinter GUI,每个按钮使用线程调用不同的函数。我必须按顺序自动点击按钮。所以我使用单个 START 按钮,它将点击第一个按钮,等待相应功能的执行完成,然后单击下一个按钮,依此类推。

我使用线程,因为我需要在任何函数运行时保持Progressbar运行。

我也将按钮文本的颜色从红色(尚未运行)更改为蓝色(正在运行)到绿色(已完成执行)。我知道我需要在某处使用join(),但它不起作用。

此当前代码一次运行所有按钮invoke()方法,而不是按顺序运行。

import tkinter as tk
from tkinter import ttk

from threading import Thread
def sample_function():
    for i in range(1,10000) :
        print(i)

def run_function(name, func,btn_variable):
    # Disable all buttons
    btn_variable.configure(style = 'blue.TButton')
    processing_bar.start(interval=10)
    print(name, 'started')
    func()
    processing_bar.stop()
    print(name, 'stopped')
    btn_variable.configure(style = 'green.TButton')

def run_thread(name, func,btn_variable):
    Thread(target=run_function, args=(name, func,btn_variable)).start()


def prepare_clicked():
    run_thread('prepare', sample_function,prepare_btn)
    prepare_btn.configure(style = 'green.TButton')


def social_clicked():
    run_thread('social', sample_function,social_btn)
    social_btn.configure(style = 'green.TButton')


def anomaly_clicked():
    run_thread('anomaly', sample_function,anomaly_btn)
    anomaly_btn.configure(style = 'green.TButton')

def scoring_clicked():
    run_thread('scoring', sample_function,scoring_btn)
    scoring_btn.configure(style = 'green.TButton')

def dashboard_clicked():
    run_thread('dashboard', sample_function,dashboard_btn)
    dashboard_btn.configure(style = 'green.TButton')


def start_all():
    prepare_btn.invoke()
    anomaly_btn.invoke()
    social_btn.invoke()
    scoring_btn.invoke()
    dashboard_btn.invoke()



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')

buttons = [prepare_btn, anomaly_btn, social_btn,scoring_btn,dashboard_btn]


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.mainloop()

1 个答案:

答案 0 :(得分:1)

这里有一些东西可以演示如何做你想做的事。它的工作原理是因为在主线程之外的线程中运行的代码不会进行任何tkinter调用。为了使线程一个接一个地顺序运行,它使用表示每个线程的条目的Queue个FIFO,并在最后一个条目完成时启动新的线程。

要像在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:
            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()