プログラミングの勉強を始めたけれども作りたいものがないというのはよくあることです。文法を一通り勉強しようとしても、途中で挫折してしまうことも多いです。
pythonでは標準のライブラリを使って簡単にボタンやウィンドウを備えたアプリを作ることができます。今回は、設定した時間が経過すると音と表示で知らせるキッチンタイマーを作ってみましょう。
プログラムの流れ
最初にボタンと時間表示を並べて配置します。
各ボタンを押したときの処理を記述する。
最後にタイマーの処理を記述します。
出来上がったタイマーの画面は以下のようになります。
操作画面の説明
操作画面について説明します。
操作画面は以下の要素からなります。TKinterというPythonの標準のライブラリをインポートすることで簡単にウィンドウを備えたアプリを作成できます。
スピンボックス
右端アップダウンボタンで数値の指定ができます。今回はキッチンタイマーの時間設定に使います。
時間(H),分(M),秒(s)の3つのスピンボックスを入力に使います。
ボタン
押したら何等かの処理が実行されます。
今回はカウントダウンをスタートさせるスタートボタンとタイマーをゼロに戻すリセットボタンで使用しました。
ラベル
ウィンドウの文字列を表示させます。今回は残り時間を表示させ、1秒ごとに更新します。
スクリプトの解説
スクリプトを説明していきます。個々の機能ごとに解説して最後にまとめたスクリプトを掲載します。
ウィジェットの配置
ボタンやスピンボックス、ラベルを総称してウィジェットと呼びます。まずウィジェットの配置を記述していきます。記述するのは_init_メソッドの中です。
_init_メソッドは初期設定のようなもので、変数の初期値を設定したり、ウィジェットの配置を設定するための関数です。
ボタンと時間表示のラベルを2次元に並べるのはgridという機能を使います。
このgridというはウィンドウをエクセルのようにマス目状に見立てて、マス目に合わせてボタンを配置していく方法です。
start_button.grid(row=1, column=0, padx=5, pady=2,sticky=Tk.W+Tk.E)
rowでウィンドウ上での行を指定します。columnでウィンドウ上での列を指定します。padxでボタンの縦方向の間隔、padyでボタンの横方向の間隔を指定できます。stickyで左右の間隔を詰めるように指定しています。
ウィジェット配置のスクリプトを以下に示します。
def __init__(self,master=None):
Tk.Frame.__init__(self,master)
#ウィンドウの名前を設定
self.master.title('timer')
#分と秒のラベルを設定
#minute = Tk.Label(self,text=u'分',font='Arial, 12')
#minute.grid(row=0, column=1, padx=5, pady=2,sticky=Tk.W)
#seconds = Tk.Label(self,text=u'秒',font='Arial, 12')
#seconds.grid(row=0, column=3, padx=5, pady=2,sticky=Tk.W)
#spinboxの設定
self.spinbox0=Tk.Spinbox(self, from_ = 0, to = 99, increment = 1, width = 10)
self.spinbox0.grid(row=0, column=0, padx=5, pady=2)
self.spinbox1 = Tk.Spinbox(self, from_ = 0, to = 99, increment = 1, width = 10)
self.spinbox1.grid(row=0, column=1, padx=5, pady=2)
self.spinbox2 = Tk.Spinbox(self, from_ = 0, to = 59, increment = 1, width = 10)
self.spinbox2.grid(row=0, column=2, padx=5, pady=2)
#labelの設定
self.clock=Tk.Label(self,text=u'00:00:00',font='Arial, 25')
self.clock.grid(row=2, column=0,columnspan=4, padx=5, pady=2,sticky=Tk.W+Tk.E)
#スタートボタンとリセットボタン
start_button = Tk.Button(self,text='Start',command=self.start)
start_button.grid(row=1, column=0,columnspan=1, padx=5, pady=2,sticky=Tk.W+Tk.E)
reset_button = Tk.Button(self,text='Reset',command=self.reset)
reset_button.grid(row=1, column=1,columnspan=1, padx=5, pady=2,sticky=Tk.W+Tk.E)
これでスピンボックスと残り時間の表示およびボタンが配置できます。
ボタン押下時の処理
ボタンが押下されたときの処理を押下していきます。スタートボタンが押されるとスピンボックスの値を合計し秒で計算します。計算のためにint型に変換しています。
def minute_to_second(self): #入力した分+秒を秒に換算
hour_input=self.spinbox0.get() ##spinbox0の値を取
minute_input=self.spinbox1.get() #spinbox1の値を取得
second_input=self.spinbox2.get() #spinbox2の値を取得
hour_input_int=int(hour_input)
minute_input_int=int(minute_input)
second_input_int=int(second_input)
self.set_time=hour_input_int*60*60+minute_input_int*60+second_input_int
カウントする時間set_timeを現在時刻に足してカウントダウンが終わる時刻finishを算出します。
def start(self): #Startを押したときの動作
self.started=True
self.minute_to_second()
self.finish = time.time() + self.set_time #現在時刻にスピンボックスからの入力値を足して終了時刻にする。
self.count()
最後にタイマーの働きをするcount関数を実行します。
タイマー処理の解説
最後にキッチンタイマーの本体となるcount関数の中身について解説していきます。count関数を説明する前に必要なメインループ関数についてまず説明します。
Pythonでウインドウが表示されているときには常にメインループが回り続けています。その様子を以下に図時しています。
ループ内で各ボタンが押された状態かを確認しています。ボタンの押下などイベントが検出されるとそのボタンに紐づいた処理を実行し、実行し終わるとメインループに戻ります。ループが高速に回っているためボタンを押した瞬間に反応しているように人間は感じます。
メインループは高速で回し続ける必要があるのでsleep関数など処理を一時停止させるような関数は使えません。
そこで、キッチンタイマーの機能を実装するために、定周期で割り込み処理をするafterメソッドを使います。afterメソッドで1秒ごとにcount関数を呼び出し残り時間を1秒ずつ減らします。これでループを回しながら正確に1秒ごとに割り込み処理ができます。
self.after(100, self.count) #1秒ごとに定周期割込みcount関数を実行
スクリプトの全体像
キッチンタイマーの処理の流れを解説しました。最後にスクリプトをまとめて示します。
# coding: utf-8
import tkinter as Tk
import time
import winsound
class Timer(Tk.Frame):
def __init__(self,master=None):
Tk.Frame.__init__(self,master)
#ウィンドウの名前を設定
self.master.title('timer')
#spinboxの設定
self.spinbox0=Tk.Spinbox(self, from_ = 0, to = 99, increment = 1, width = 10)
self.spinbox0.grid(row=0, column=0, padx=5, pady=2)
self.spinbox1 = Tk.Spinbox(self, from_ = 0, to = 99, increment = 1, width = 10)
self.spinbox1.grid(row=0, column=1, padx=5, pady=2)
self.spinbox2 = Tk.Spinbox(self, from_ = 0, to = 59, increment = 1, width = 10)
self.spinbox2.grid(row=0, column=2, padx=5, pady=2)
#labelの設定
self.clock=Tk.Label(self,text=u'00:00:00',font='Arial, 25')
self.clock.grid(row=2, column=0,columnspan=4, padx=5, pady=2,sticky=Tk.W+Tk.E)
#スタートボタンとリセットボタンの設定
start_button = Tk.Button(self,text='Start',command=self.start)
start_button.grid(row=1, column=0,columnspan=1, padx=5, pady=2,sticky=Tk.W+Tk.E)
reset_button = Tk.Button(self,text='Reset',command=self.reset)
reset_button.grid(row=1, column=1,columnspan=1, padx=5, pady=2,sticky=Tk.W+Tk.E)
def start(self): #Startを押したときの動作
self.started=True
self.minute_to_second()
self.finish = time.time() + self.set_time #現在時刻にスピンボックスからの入力値を足して終了時刻にする。
self.count()
def reset(self): #停止処理
self.started=False
self.clock.config(text='00:00:00')
def count(self):
if self.started:
remaining_time = self.finish - time.time() #残り時間
if remaining_time <= 0:
winsound.PlaySound("SystemAsterisk",winsound.SND_ALIAS) #時間になったらwindowsのみ音で知らせる
self.clock.config(text="Time is over!")
else:
self.clock.config(text='%02d:%02d:%02d'%(remaining_time/3600,remaining_time/60%60,remaining_time%3600%60,)) #表示時間を1秒毎に書き換え
self.after(100, self.count) #1秒ごとに定周期割込みcount関数を実行
def minute_to_second(self): #入力した分+秒を秒に換算
hour_input=self.spinbox0.get() ##spinbox0の値を取
minute_input=self.spinbox1.get() #spinbox1の値を取得
second_input=self.spinbox2.get() #spinbox2の値を取得
hour_input_int=int(hour_input)
minute_input_int=int(minute_input)
second_input_int=int(second_input)
self.set_time=hour_input_int*60*60+minute_input_int*60+second_input_int
if __name__ == '__main__': #スクリプトファイルとして実行されたとき用
f = Timer()
f.pack()
f.mainloop()
Pythonでキッチンタイマーのアプリをつくるスクリプトを解説しました。pythonは環境構築に手間をかけずにアプリを作ることができます。ウィンドウを備えたアプリはコンソールで動くアプリと違い、ループを回し続ける必要があります。そのため1秒ごとに処理するためにはafterメソッドを使って割り込みする必要があります。
コメント