「凍結」を理解するには、理解する必要がありますmainloop()
。このメソッドを呼び出すと、tkinterイベント ループが開始されます。メイン スレッドがこのループを担当します。したがって、作業集約型の関数がメイン スレッドで実行されると、メインループにも干渉します。これを防ぐために、セカンダリを使用しThread
て関数を実行できます。セカンダリ スレッドに tkinter オブジェクトへのアクセスを許可しないことをお勧めします。mtTkinterの著者である Allen B.Taylor は次のように述べています。
この問題は、_tkinter モジュールが、他のスレッドからの呼び出しを処理するときに、ポーリング手法を介してメイン スレッドの制御を取得しようとするという事実に起因しています。それが成功すれば、すべてがうまくいきます。失敗した場合 (つまり、タイムアウト後)、アプリケーションは次のメッセージを含む例外を受け取ります。「RuntimeError: メイン スレッドがメイン ループにありません」
セカンダリ スレッドに情報をQueue
. 次に、メソッドを介して、メインループ内でxミリ秒ごとにキューをチェックする関数を用意します。after()
最初に、Progressbarのmaximumオプションの値をどのようにするかを決定します。
これは、プログレスバーの最大インジケーター値 (プログレスバーを満たすために必要な単位数) です。たとえばmaximum=4
、適切な指標値を設定してから、4 つの関数のそれぞれの後にキューに入れることができます。次に、メイン スレッドはこれらの値を (キューから) 取得して、 を介して進行状況を設定できますtkinter.IntVar()
。( を使用すると、プログレスバーは 4 (完全に塗りつぶされた) に達するのではなく、最後に 0 (空) にリセットされることに注意してくださいprogbar.step()
。)
tkinter.IntVar()
プログレスバーで a を使用する方法を簡単に説明します。
int_var = tkinter.IntVar()
pb_instance = ttk.Progressbar(root, maximum=4)
pb_instance['variable'] = int_var
pb_instance.pack()
# completely fill the Progressbar
int_var.set(4)
# get the progress value
x = int_var.get()
これはあなた自身に基づく例です(「メイン」関数の名前を「任意」に変更しました):
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
import Queue as queue
else:
from tkinter import ttk
import queue
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.int_var = tkinter.IntVar()
progbar = ttk.Progressbar(self.root, maximum=4)
# associate self.int_var with the progress value
progbar['variable'] = self.int_var
progbar.pack()
self.label = ttk.Label(self.root, text='0/4')
self.label.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.int_var.set(0) # empty the Progressbar
self.label['text'] = '0/4'
# create then start a secondary thread to run arbitrary()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
# check the Queue in 50ms
self.root.after(50, self.check_que)
def check_que(self):
while True:
try: x = que.get_nowait()
except queue.Empty:
self.root.after(25, self.check_que)
break
else: # continue from the try suite
self.label['text'] = '{}/4'.format(x)
self.int_var.set(x)
if x == 4:
self.b_start['state'] = 'normal'
break
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
que.put(1)
func_b()
que.put(2)
func_c()
que.put(3)
func_d()
que.put(4)
que = queue.Queue()
gui = GUI_Core() # see GUI_Core's __init__ method
gui.root.mainloop()
アクティビティがあることをユーザーに示すものだけが必要な場合は
、プログレスバーのモードオプションを に設定できます'indeterminate'
。
このモードでは、インジケータは前後に跳ねます (速度は最大オプションに関連します)。
start()
次に、セカンダリ スレッドを開始する前に、Progressbar のメソッドを直接呼び出すことができます。Falseを返した後
に呼び出します。stop()
secondary_thread.is_alive()
次に例を示します。
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
else: from tkinter import ttk
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.progbar = ttk.Progressbar(self.root)
self.progbar.config(maximum=4, mode='indeterminate')
self.progbar.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.progbar.start()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
self.root.after(50, self.check_thread)
def check_thread(self):
if self.secondary_thread.is_alive():
self.root.after(50, self.check_thread)
else:
self.progbar.stop()
self.b_start['state'] = 'normal'
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
func_b()
func_c()
func_d()
gui = GUI_Core()
gui.root.mainloop()
→プログレスバー参照