HOME Python download 書き込む

3. Label だけでタイマーを作ろう


1. 初めに

Tk のデモのラベルの項には、”ラベルは動かせないからつまらない” ("Labels are pretty boring because you can't do anything with them.") と書かれていますが、マウスのクリック、キープレスなどのイベントと結びつけると いろいろと動かすことだできます。

この章では図1に示すようなアラームクロックをラベルだけで作ってみましょう。 このアラームはマウスの左ボタンで Start/Stop し、右ボタンで Reset します。

まず、Label widget の作成方法、イメージの貼り付け方を説明してから、 イベントによるラベルの変化のさせ方を説明します。

2. Label の作成

Label は以下のように作成します。
Tk.Label(master, **options)
最初の引数で親 widget を指定し、キーワード引数を続けます。
Label の option には以下があります。 Tkinter reference: 10. The Label widget や、 The Tkinter Label Widget も参照してください。
属性 説明
anchor テキストを配置する位置を指定します。デフォルトは Tk.CENTER
bg or background 背景色を指定します。
bitmap 表示する bitmap を指定します。
bd or borderwidth 縁の幅を指定します。デフォルトは 2.
cursor カーソルの形を指定します。
font テキストを表示する際のフォントを指定します。
fg or foreground フォントの色、または bitmap の色を指定します。
height ラベルの高さを行数で指定します。pixel 単位でないことに注意してください。
image ラベルに貼る image を指定します。
justify テキストが複数行にわたる場合、左寄せ、右寄せ、中央寄せを指定します。
padx 縁とテキストとの間の横の余白。
pady 縁とテキストとの間の縦の余白。
relief ヘリの形
takefocus 普通は Label には foculs はない。もし、focus を持ってきたければ、takefocus を 1 にする。
text 表示する文字列
textvariable StringVar を指定。変化する文字列を表示するとき用いる。
underline (0 から数え始めて) n 番目の文字にアンダーラインをつける。デフォルトは -1.
width Label の幅、文字数単位。pixel 単位ではない。
wraplength 改行幅

3. 反応させる

マウスボタンをクリックしたり、キーボードのキーを押したりしたときに アプリケーションが反応するようにするためには Widget と Event を結びつけることによって 行います。 Widget と Event を結び付けるには bind メソッドを使います。 また、全ての widget を同じ Event を結び付けるには bind_all メソッドを使います。 詳しくは、 Tkinter reference: 24. Events: responding to stimuli を見てください。例として、マウスの左ボタンを押すと、背景色がランダムに変わる Hello World は次のようになります。

[code 1](rhello.py)

01:     #! /usr/bin/env python
02:     
03:     import Tkinter as Tk
04:     import random as R
05:     
06:     
07:     class Label(Tk.Label):
08:         def __init__(self, master=None):
09:             Tk.Label.__init__(self, master, text='Hello world!', font=('Helvetica', '24', 'bold'))
10:             self.bind_all('<1>', self.bg_change)
11:     
12:         def bg_change(self, event):
13:             r = R.randint(0,255)
14:             g = R.randint(0,255)
15:             b = R.randint(0,255)
16:             self.configure(bg='#%02X%02X%02X' % (r, g, b))
17:     
18:     if __name__ == '__main__':
19:         l = Label()
20:         l.pack()
21:         l.mainloop()
このスクリプトの重要部分は 10 行目のイベントと関数を結びつける部分と、 16 行目の widget の属性を変化させる部分です。

10 行目が widget と Event を結びつけるメソッドです。 マウス左ボタンクリックは '<1>' と表されます。 イベントの記法については Tkinter reference: 24.2. Event sequences を見てください。

bind または bind_all メソッドの 最初の引数はイベントの種類で、2番目の引数はその Event が発生したときに呼び出される関数へのポインターです。 呼び出される関数は2引数の関数で、 最初の引数はインスタンス変数にアクセスするための self, 2番目の引数は event です。event は種種の属性を持っています。 event の属性については Tkinter reference: 24.6. Writing your handler を見てください。

16 行目の configure メソッドで widget の属性を変化させることができます。 ここでは背景色をランダムに変更しています。色の指定は名前による指定のほかに '#RGB' の形式で 指定できます。それぞれの色は 16 進法で表した 4, 8, あるいは 12 bit の正の整数です。

4. 動作を継続させる

動作を継続させるには after メソッドを使って、動作をするメソッドを 再帰的に呼び出します。
after メソッドの書式は以下の通りです。
w.after(n, func)
このメソッドは2つの引数をとり、2番目の引数は、 動作を定義した関数へのポインター(func)、最初の引数はミリ秒 (n) で、n ミリ秒後に finc を呼び出します。func が再帰的に func を呼び出すようにすると動作を継続させることができます。

[code 2] に例を示します。これは、左ボタンクリックするとHello World の温度が上がっていき、 その色がだんだん変化していきます。温度は 1000 K から 8000 K まで1秒間に 500 K の割合で変化します。

[code 2](thello.py)

01:     #! /usr/bin/env python
02:     
03:     import Tkinter as Tk
04:     import bbr as B
05:     
06:     class Frame(Tk.Frame):
07:         def __init__(self, master=None):
08:             Tk.Frame.__init__(self, master)
09:             self.label=Tk.Label(self, text='click me!', width=32, height=2, \
                                                 font=('Helvetica', '8'), fg='white', bg='black')
10:             self.label.pack(fill=Tk.BOTH)
11:             self.bind_all('<1>', self.heating_start)
12:     
13:         def heating_start(self, event):
14:             self.temp=1000
15:             self.label.configure(width=12, height=1, text='Hello world!', font=('Helvetica', '24'), fg='black')
16:             self.after(100, self.heating)
17:     
18:         def heating(self):
19:              self.label.configure(fg=B.bbrcolor_rel(self.temp))
20:              self.temp += 50
21:              if self.temp < 8000:
22:                  self.after(100, self.heating)
23:              else:
24:                  self.label.configure(fg='black', bg='white')
25:         
26:     
27:     if __name__ == '__main__':
28:         f = Frame()
29:         f.pack()
30:         f.mainloop()
まず、4 行目で温度と色とを関係づけるモジュール bbr (black body radiation) を import します。 (bbr は付録に つけておきますので、見てください。)次に、11 行目の
self.bind_all('<1>', self.heating_start)
で、widget と self.heating_start メソッドとを結び付けます。
self.heating_start はself.label を configure し、100 ミリ秒後に self.heating を呼びます。

self.heating は self.temp から算出される色で、Hello world を表示し、その後、 self.temp を 50 K 上げ、self.temp が 8000 K になるまで、100 ミリ秒後に自分自身を呼び出します。 このように after メソッドと再帰関数を使うことによって widget を変化させることが できます。after メソッドは全ての widget に共通したメソッドなので、変化しうる widget 全てに 用いることができます。

5. タイマーを作る。

いよいよタイマーを作ってみましょう。 StringVertextvariable についてはまだ説明していませんが、 タイマーのコード ([code 3]) を示しながら説明します。 Label への画像の張り込みについては Appendix 1. Widget に画像を張り込む を参照してください。

[code 3](timer.py)

01:     #! /usr/bin/env python
02:     
03:     """
04:     timer.py
05:     
06:     June 24, 2005
07:     """
08:     
09:     
10:     import Tkinter as Tk
11:     import sys
12:     
13:     BLUE = '#99CCFF'
14:     YELLOW = '#FFCC00'
15:     RED = '#FF00FF'
16:     CLOCK = 'meza-bl-2.gif'
17:     
18:     class Frame(Tk.Frame):
19:     
20:         def __init__(self, master, min):
21:             Tk.Frame.__init__(self, master)
22:             
23:             self.started = False
24:             self.echo = Tk.StringVar()
25:             self.min = min 
26:             self.echo.set('%02d:00' % (self.min))
27:             self.sec = 60 * self.min
28:             
29:             self.master.title('Alarm')
30:             self.label = Tk.Label(self, text='Click to start', font=('Helvetica', '8'))
31:             self.label.pack()
32:             f = Tk.Frame(self, relief=Tk.RIDGE, bd=4)
33:             f.pack(fill=Tk.BOTH, expand=1)
34:             self.image= Tk.PhotoImage(file=CLOCK)
35:             self.icon=Tk.Label(f, image=self.image, bg=BLUE)
36:             self.icon.pack(side=Tk.LEFT)
37:             display = Tk.Label(f, textvariable=self.echo, font=('Helvetica', '24'),
38:                                bg='white', width=5, anchor=Tk.E)
39:             display.pack(side=Tk.LEFT)
40:             self.bind_all('<1>', self.start_stop)
41:             self.bind_all('<3>', self.reset)
42:     
43:         def start_stop(self, event):
44:             if not self.started:
45:                 self.label.configure(text='Click to stop')
46:                 if 0< self.sec <= 20:
47:                    self.icon.configure(bg=YELLOW)
48:                 if 0 >= self.sec:
49:                    self.icon.configure(bg=RED)
50:                 self.after(1000, self.counting)
51:                 self.started = True
52:             else:
53:                 self.label.configure(text='Click to start, RB to reset')
54:                 self.icon.configure(bg=BLUE)
55:                 self.started = False
56:     
57:         def counting(self):
58:             if self.started:
59:                 self.sec -=1
60:                 self.echo.set('%02d:%02d' % (self.sec/60, self.sec%60))
61:                 if self.sec == 20:
62:                     self.icon.configure(bg=YELLOW)
63:                     
64:                 if self.sec <= 0:
65:                     t= -1 * self.sec
66:                     self.icon.configure(bg=RED)
67:                     self.bell()
68:                     self.echo.set('-%02d:%02d' % (t/60, t%60))
69:                     self.after(500, self.yellow)
70:                         
71:                 self.after(1000, self.counting)
72:     
73:         def yellow(self):
74:             if self.started:
75:                 self.icon.configure(bg=YELLOW)
76:     
77:     
78:         def reset(self, event):
79:             if not self.started:
80:                 self.sec=60*self.min
81:                 self.echo.set('%02d:00' % (self.min))
82:     
83:     
84:     
85:     if __name__ == '__main__':
86:         min = len(sys.argv) > 1 and  int(sys.argv[1]) or 3
87:         f = Frame(None, min)
88:         f.pack()
89:         f.mainloop()

5.1. 使い方と動作

コマンドラインから
>python timer.py m
とします。m は計測する時間(分)です。m が省略されると 3 分になります。 すると、図1で示した widget があらわれるので、それを左クリックします。すると、計測が始まります。 時計を止めるには動いている時計を左クリックし、リセットするには止まっている時計を右クリックします。

残り 20 秒を切ると時計アイコンの背景が黄色くなります。また、設定時間を過ぎるとアイコンの背景が赤−黄の 点滅をし、ベルを鳴らします。

5.2. StringVar の使い方

ラベルの表示文字が頻繁に変わるときは configure でテキストを変えるよりも textvariable を使ったほうが便利です。 textvariable には Tk.StringVar オブジェクトを指定します(24, 37 行目)。
Tk.StringVar には値をセットする set メソッドと値を取り出す get メソッドがあります(60, 68, 81 行目)。 値をセットすれば、ラベルにはセットされた値が自動的に表示されます。

5.3. プログラムの簡単な説明

6. 終わりに

ラベルに普通にテキストを貼り付ける方法は特に説明しませんでしたが、前回、前々回にもでてきたので、 そちらを見てください。また Tkinter demo も見てください。

今回は主にラベルを変化させる方法について述べました。実はこの方法は全ての widget でほとんど共通です。 従って、ここで述べた方法は他の widget でも応用が利くと思います。

次回は今回作ったアラームにボタンをつけてみます。