概要
tkinterでの定期実行処理方法について、以下の2つを示します。
- threadingモジュールを使用した場合
- scheduleモジュールを使用した場合
簡単な処理であれば、threadingモジュールやscheduleモジュールを使わなくても良いですが、重たい処理になるとtk自体の動作(ボタンを押す/画面を移動させるなど)が重くなります。そのため、面倒でも以上のような方法で実装すると良いかもしれません。
threadingモジュールを使用した場合
以下はサンプルコードです。「self.label」の値を1ずつ増やすプログラムです。やろうと思えばミリ秒単位の定期実行が可能です。pythonなので、時間精度はそこまで良くありませんが、そこまで時間精度を気にする必要がなければこれで十分です。
import tkinter as tk
import threading
class GUI(tk.Frame):
def __init__(self,master = None):
super().__init__(master)
self.master = master
master.geometry("300x300")
master.title("Test")
self.count = 0 # labelに定義する値のバッファ
self.test = tk.Frame(self.master)# 必要ないっちゃないですが、画面を管理するために一旦別フレームを生成しています。
self.test.pack()
self.label = tk.Label(self.test)
self.label.pack()
self.label["text"] = str(self.count) # labelの値を初期化
self.timeEvent()# タイマー起動
# タイマー起動用関数
def timeEvent(self):
th = threading.Thread(target=self.update)# スレッドインスタンス生成
th.start()# スレッドスタート
self.after(1000, self.timeEvent)# ここで、再帰的に関数を呼び出す
# スレッド処理実体
def update(self):
self.count += 1
print(self.count) # デバッグメッセージ
self.label["text"] = str(self.count) # labelの値を更新
if __name__ == "__main__":
gui = tk.Tk()
app = GUI(master = gui)
app.mainloop()
もっと時間精度が欲しい場合は、少し処理が複雑になりますが、以下のような書き方が出来ます。2倍は時間精度が良くなると思いますが、可読性が少し落ちます。
import tkinter as tk
import threading
import time
class GUI(tk.Frame):
def __init__(self,master = None):
super().__init__(master)
self.master = master
master.geometry("300x300")
master.title("Test")
self.count = 0
self.test = tk.Frame(self.master)
self.test.pack()
self.label = tk.Label(self.test)
self.label.pack()
self.label["text"] = str(self.count)
self.buftime = time.time()# タイマー初期化
self.timeEvent()
def timeEvent(self):
tmp = time.time()
if(tmp - self.buftime) >= 1: # 1sec経過した場合
th = threading.Thread(target=self.update)
th.start()
print(tmp - self.buftime)
self.buftime = tmp
self.after(1, self.timeEvent)
def update(self):
self.count += 1
self.label["text"] = str(self.count)
if __name__ == "__main__":
gui = tk.Tk()
app = GUI(master = gui)
app.mainloop()
scheduleモジュールを使用した場合
「schedule」モジュールを使用しても同様のことは出来ます。ただ、こちらはpipでインストールの必要があります。また、ミリ秒単位での定期実行は調べた範囲では出来ない様子です。
秒単位であれば、こちらの方が可読性が高くなると思うで、目的に合わせて選択すると良いかもしれません。
pip install schedule
import tkinter as tk
import schedule
class GUI(tk.Frame):
def __init__(self,master = None):
super().__init__(master)
self.master = master
master.geometry("300x300")
master.title("Test")
self.count = 0
self.test = tk.Frame(self.master)
self.test.pack()
self.label = tk.Label(self.test)
self.label.pack()
self.label["text"] = str(self.count)
schedule.every(1).seconds.do(self.update)# scheduleにタスクを追加
self.sc()
def sc(self): # scheduleモジュールにて定期的にタスク状態を確認
schedule.run_pending()
self.after(1, self.sc) # 再帰的に実行
def update(self):
self.count += 1
print(self.count)
self.label["text"] = str(self.count)
if __name__ == "__main__":
gui = tk.Tk()
app = GUI(master = gui)
app.mainloop()
(補足)Pythonにおけるスレッド処理について
Pythonの「threading」モジュールは、インスタンス一回宣言ごとに一回のみ実行されます。これはthreadingモジュールの仕様です。以下に例を示します。
import threading
th = threading.Thread(target=self.update)
th.start()
th.start() # ←ここでErrorになる「RuntimeError: threads can only be started once」