Code

PythonでTkinerを使ってTimerを作ってみる【その2】

Code

第1回:https://syachiku.net/python-timer1/
第2回:https://syachiku.net/python-timer2/ <-今回

第3回:https://syachiku.net/python-timer3/
第4回:https://syachiku.net/python-timer4/
第5回:https://syachiku.net/python-timer5/

第1回 の続きになります。

今回はカウントダウンの部分のロジック部分を作っていきます。

2-1. 残り時間の部分に動的な値を表示する

前回までで残り時間を表示している部分は、固定されたテキストの表示まででした。このテキストを変数を使って動的に変更していきます。

動的に表示を変更するにはTimerViewのLabelで指定していた「text」を「textvariable」に変更した上で、「変数」の設定を行います。

Tkinterで動的な変数を使うには「TkinterのStringVar」をはじめに宣言する必要があるため、initで宣言します。

今回は残り時間などのロジック部分はView側ではなくて、TimerMain側に持たるので利用する変数をTimerMain側へ定義します。

※各変数の役割はコメントを参照してください。

// timer_main.py
import tkinter as tk

class TimerMain():
    def __init__(self):
        self.__start_statys = True

        self.__text_min = tk.StringVar()  # フォームの分
        self.__text_sec = tk.StringVar()  # フォームの秒
        self.__remain_min = tk.StringVar() # 残り時間Labelの分
        self.__remain_sec = tk.StringVar() # 残り時間Labelの秒

        self.__text_min.set(str(60)) # 最初の設定値 60分
        self.__text_sec.set(str(0))  # 最初の設定値 0秒
        self.__remain_min.set(str("-")) # 最初の設定値 -
        self.__remain_sec.set(str("-")) # 最初の設定値 0秒

        self.__text_start_stop_button = tk.StringVar() # Startボタン
        self.__text_start_stop_button.set("Start") # 最初はStartと表示

        self.__scheduled_finish_time = tk.StringVar() # 後で実装する推定終了時間
        self.__scheduled_finish_time.set(str("-")) # 最初は - で表示する

    def click_timer_button(self):
        print("Click!!!!")

次にTimerView側で今ほどの変数を利用するのですが、__(アンダースコア2つ)でprivate化しているので@propetryを使ってgetterを作ります。(@propetryがわからない方はPythonでのgetterとかsetterを調べるといいかと思います)

click_timer_buttonの下側に続けて以下を記載します。

// timer_main.py
(略)
    def click_timer_button(self):
        print("Click!!!!")

    @property
    def text_min(self):
        return self.__text_min

    @property
    def text_sec(self):
        return self.__text_sec

    @property
    def text_start_stop_button(self):
        return self.__text_start_stop_button

    @property
    def remain_min(self):
        return self.__remain_min

    @property
    def remain_sec(self):
        return self.__remain_sec

    @property
    def scheduled_finish_time(self):
        return self.__scheduled_finish_time

TimerMain内での変数側の宣言が終わったのでTimerView側から利用する変数として指定します。

先ほどの繰り返しになりますが、変数部分は「text」から「textvariable」に変更し、TimerMain内の変数を呼びだすようにしています。

from tkinter import ttk
from tkinter import *

from timer_main import TimerMain


class TimerView(ttk.LabelFrame):
    def __init__(self, master=None):
        self.timer_main = TimerMain()
        __frame = ttk.LabelFrame(master, text="タイマー")
        __frame.pack(anchor="w", fill="x", expand=True, padx=30, pady=10)

        __label = Label(__frame, text="タイマー入力")
        __label.grid(row=0, column=0)

        __label = Entry(__frame, justify="right", width="8", textvariable=self.timer_main.text_min)
        __label.grid(row=0, column=1)

        __label = Label(__frame, text="分")
        __label.grid(row=0, column=2)

        __label = Entry(__frame, justify="right", width="8", textvariable=self.timer_main.text_sec)
        __label.grid(row=0, column=3)

        __label = Label(__frame, text="秒")
        __label.grid(row=0, column=4)

        __label = Button(__frame, textvariable=self.timer_main.text_start_stop_button,
                         command=self.timer_main.click_timer_button)
        __label.grid(row=0, column=5)

        __label = Label(__frame, text="残時間")
        __label.grid(row=1, column=0)

        __label = Label(__frame, textvariable=self.timer_main.remain_min)
        __label.grid(row=1, column=1)

        __label = Label(__frame, text="分")
        __label.grid(row=1, column=2)

        __label = Label(__frame,  textvariable=self.timer_main.remain_sec)
        __label.grid(row=1, column=3)

        __label = Label(__frame, text="秒")
        __label.grid(row=1, column=4)

        __label = Label(__frame, text="推定終了時刻:")
        __label.grid(row=1, column=5)

        __label = Label(__frame, textvariable=self.timer_main.scheduled_finish_time)
        __label.grid(row=1, column=6)

この状態で実行すると以下の様な画面になり、先ほど変数で指定した初期値が表示されるかと思います。まだ、この時点ではStartボタンを押してもClickと表示されるだけです

という事で、いよいよカウントダウンの部分を作っていきましょう。

2-2. カウントダウンのロジックを作成する

では、Startボタンがクリックされたらカウントダウンタイマーの処理が始まるようにロジック部分を作成します。

具体的にはTinkerでタイマーを実装するために「after関数」を使います。

「タイマーに設定した〇分後に実行する」というだけであればtime関数のsleepでも問題ないのですが、今回は前回作ったの要件定義にある1秒単位でカウントダウンする、というのが必要です。

  • 「Start」ボタンをクリックするとタイマーが開始され、1秒単位でカウントダウンする

上の要件の通りにするにはsleepのように完全に処理を止める(=1秒単位でのカウントダウンをしない)ということでは困るので「特定の処理を指定した時間分遅らせて実行する」役割を持つ「after関数」を利用することになります。

1秒単位でTkinerのビューをリフレッシュする際に、TimerViewのFrameを再読み込みさせる必要があります。TimerMain側で利用できるようにTimerMainにframeを渡します。

先ほどまでは先頭でTimerMainインスタンスを作ってましたが、frameを渡すために順番を遅らせてます。

// timer_view.py
    def __init__(self, master=None):

        __frame = ttk.LabelFrame(master, text="タイマー")
        __frame.pack(anchor="w", fill="x", expand=True, padx=30, pady=10)
        self.timer_main = TimerMain(__frame)

次に受け取るTimerMain側の処理です。一気にStartがクリックされた際のafter部分まで作ってしまいます。

// timer_main.py
(略)
    def click_timer_button(self):
        self.__remain_sec.set(int(self.__text_sec.get()))
        self.exec_timer()

    def exec_timer(self):
        __tmp_time_sec = int(self.__remain_sec.get())
        __tmp_time_sec -= 1
        self.__remain_sec.set(str(__tmp_time_sec))
        self.__frame.after(1000, self.exec_timer)
(略)

Startをクリックすると残時間の秒の部分がタイマー的な動きになっているのが分かったと思います。(マイナスになっていってますが・・・)

簡単に解説します。

StartボタンをクリックしてTimerMainのclick_timer_buttonが呼ばれます。始めにフォームで入力された秒の値(__text_sec)を残時間の秒(__remain_sec)にセットします。

exec_timerを呼び出しして、__remain_secをマイナス1してからafter関数で自分自身を呼び出ししてます。afterで指定している1000は1000ms=1秒となります

マイナスしている部分ですが、StringVar形式なので単純にマイナス1はできずに一旦getで呼び出し→一時的な変数で処理→setで設定という流れを踏んでます。

まとめ

今回ではで見た目のところで残っていた部品をとりあえず配置してから、after関数を使って、タイマーの中核に当たる部分が完成できました。

要件でいうと以下の黄色塗りの部分です。残り時間の部分は秒だけしか完成していないのでまだ作成中となります。

  • Timerで設定できるのは「分」と「秒」とし、それぞれがフォーム入力可能とする(第1回)
  • ボタンは1つとして、開いたタイミングでは「Start」を表示(第1回)
  • 「Start」ボタンをクリックするとタイマーが開始され、1秒単位でカウントダウンする(第2回)
  • 0秒になったらアラートのポップアップメッセージを表示する(今回はTkinerのmesageboxで実装する)
  • 「Start」ボタンを押したらタイマーが開始され、ボタンが「Stop」の表示に切り替わる
  • Stopを押したらタイマーが停止する
  • 残り時間として「分」と「秒」を表示する
  • 上と共に「推定での終わる時刻」を表示する。M/D hh:mm:ss形式とする
  • 「推定での終わる時刻」は1分で随時更新される

次は「分」の部分の計算を実装していきましょう。

第2回終了時のGithubコード

以下に保管してますので参考にどうぞ

GitHub - gogoloon/python-timer at Lesson#2
Contribute to gogoloon/python-timer development by creating an account on GitHub.

Pythonのオススメ勉強方法

私がオススメするPython初心者向けの最初に購入すべき書籍は「シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全です。

シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全

この書籍は実際にシリコンバレーの一流エンジニアとして活躍している酒井潤さんが書いた本です。

内容も初心者から上級者までまとめられており、各Lessonも長すぎずに分かりやすくまとめられているので、初心者の方にもおすすめです。

シリコンバレー一流プログラマーが教える Pythonプロフェッショナル大全

今回は以上となります。

コメント

タイトルとURLをコピーしました