LoginSignup
10
10

More than 3 years have passed since last update.

tkinterを用いた時の定期実行処理【Python3】

Last updated at Posted at 2020-02-03

概要

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モジュールの仕様です。以下に例を示します。

error.py
import threading

th = threading.Thread(target=self.update)
th.start()
th.start() # ←ここでErrorになる「RuntimeError: threads can only be started once」
10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10