HOME Python download 書き込む

7. Widget てんこ盛り画像 Viewer


1. 初めに

Tkinter にもだいぶ慣れてこられたと思うので、小物 widget をまとめて 紹介してしまおうと思います。

お題は画像ファイル Viewer (viewer.py) です。これだと前回までの話を引き継げるし、 無理なくいろいろな widget を使えます。生成する Window (図1) をみてお分かりになるように、いろいろな Widget が使ってあります。

前回までの解説でお分かりになったと思いますが、Tkinter の widget の使い方は 種類にかかわらず同じです。つまり、

  1. 作成するときは、第一引数に親 widget、それ以降に、widget の属性を定義するキーワード引数が続ける。
  2. pack, grid, place を用いて配置する。
  3. 属性を変化させたいときは .configure メソッドを使う。
です。Widget の種類ごとに取りうる属性と、widget 特有のメソッドは以下のサイトに詳しいので、参照してください。 また、Tkinter demo も参考にしてみてください。


(a)


(b)

(c)

(d)

(e)

図1.viewer.py で生成する Window。
a) メイン window。
b) オリジナルサイズの画像に (GIF の場合)背景色をつけたもの。
c) 背景色調節用 window
d) ディレクトリ選択 window
e) 説明 window

2. プログラムの作成

これだけ、たくさんの widget があると、プログラムもさぞかし長いだろうt思われるかもしれませんが、 そんなことはなく、220 行ほどです。[code 1] にプログラムを示します。

[code 1] (viewer.py)

 01:     #! /usr/bin/env python
 02:     # -*- coding: shift_jis -*-
 03:     
 04:     """
 05:     viewer.py 
 06:     
 07:     view images in a directory
 08:     
 09:     June 30, 2005
 10:     """
 11:     
 12:     import re
 13:     import os
 14:     import os.path as P
 15:     import Tkinter as Tk
 16:     import Image as I
 17:     import ImageTk as Itk
 18:     import tkFileDialog as D
 19:     import tkMessageBox as M
 20:     
 21:     
 22:     SIZE = 100
 23:     IMAGR_TYPES = ['gif', 'png', 'bmp', 'jpg', 'tif', 'ppm']
 24:     MAM_COLN = 6
 25:     GEO_MAIN = '+20+20'
 26:     GEO_SATE = '+750+50'
 27:     GEO_SCAL = '+750+400'
 28:     BGS = [('aliceblue', '#F0F8FF'), ('azure', '#F0FFFF'), ('beige', '#F5F5DC'),              \
 29:            ('cornsilk', '#FFF8DC'), ('khaki', '#F0E68C'), ('lightgreen', '#90EE90'),          \
 30:            ('lightpink', '#FFB6C1'), ('lightskyblue', '#87CEFA'), ('palegreen', '#98FB98')]
 31:     
 32:     
 33:     ## functions ------------------------------------------------
 34:     def get_size(tup):
 35:         """ It returns the size of images on the summary"""
 36:         x, y = tup
 37:         if (x<=100 and y<=100):
 38:             return (x, y)
 39:         elif x > y:
 40:             r = float(SIZE) / float(x)
 41:             return (100, int(y*r))
 42:         else:
 43:             r = float(SIZE) / float(y)
 44:             return (int(x*r), 100)
 45:             
 46:     
 47:     def make_regexp(types):
 48:         """ It returns the regular expression of the image file type""" 
 49:         str = "\.("
 50:         for k, v in types.iteritems():
 51:             if v.get():
 52:                 str += k + '|'
 53:     
 54:         str = str[0:-1] + ')$'
 55:         return re.compile(str, re.I)
 56:     
 57:     
 58:     ## classes ------------------------------------------------------------------
 59:     class ImageLabel(Tk.Frame):
 60:         """ A Label class to show an image """
 61:     
 62:         id_original_size = None    # ID of the Label showing an original size image
 63:         bg_var = None              # variable for background color
 64:         
 65:         def __init__(self, master, image_file, img):
 66:             Tk.Frame.__init__(self, master)
 67:             self.image = img
 68:             self.image_file = image_file
 69:             img_label=Tk.Label(self, image=self.image)
 70:             img_label.pack()
 71:             txt_label=Tk.Label(self, text=P.basename(self.image_file), font=('Helvetica', '8'))
 72:             txt_label.pack()
 73:             img_label.bind('<Double-Button-1>', self.show)
 74:     
 75:         def show(self, event):
 76:             label = ImageLabel.id_original_size
 77:             if (label and label.winfo_exists()):
 78:                 top = label.winfo_toplevel()
 79:                 top.destroy()
 80:             top = Tk.Toplevel(self)
 81:             top.title(P.basename(self.image_file))
 82:             top.geometry(GEO_SATE)
 83:             img = I.open(self.image_file)
 84:             self.timg = Itk.PhotoImage(img)
 85:             label=Tk.Label(top, image=self.timg, bg=ImageLabel.bg_var.get())
 86:             label.pack()
 87:             ImageLabel.id_original_size = label
 88:     
 89:     
 90:     
 91:     class ImageFrame:
 92:         """ A Class to show summary of images """
 93:         
 94:         def __init__(self, master, **key):
 95:             self.master=master
 96:             self.key = key
 97:             self.frame=Tk.Frame(master, **key)
 98:             self.frame.pack(padx=5, pady=5)
 99:             self.once = False
100:     
101:         def update(self, dir, types):
102:             if self.once:
103:                 self.frame.destroy()
104:                 self.frame=Tk.Frame(self.master, **self.key)
105:                 self.frame.pack(padx=5, pady=5)
106:             self.once = True
107:             pat = make_regexp(types)
108:             files = [P.join(dir, file) for file in os.listdir(dir) if pat.search(file)]
109:             for i, file in enumerate(files):
110:                 img_temp = I.open(file)
111:                 img = img_temp.resize(get_size(img_temp.size), I.NEAREST)  
112:                 tkimg = Itk.PhotoImage(img)
113:                 la = ImageLabel(self.frame, file, tkimg)
114:                 la.grid(row = i/MAM_COLN, column=i%MAM_COLN, sticky=Tk.S+Tk.W+Tk.E)
115:     
116:     
117:     
118:     class Frame(Tk.Frame):
119:         """ The main class of this program. """
120:         
121:         def __init__(self, master=None):
122:             Tk.Frame.__init__(self, master)
123:             self.master.title('Image Viewer')
124:             self.master.geometry(GEO_MAIN)
125:             self.cus_top = None
126:     
127:             f_dir = Tk.LabelFrame(self, text='Directory')
128:             f_dir.pack(anchor=Tk.W, padx=10, pady=2)
129:             self.e_dir = Tk.Entry(f_dir, width=50)
130:             self.e_dir.pack(side=Tk.LEFT)
131:             self.e_dir.bind('<Return>', self.show_sum)
132:             self.b_dir = Tk.Button(f_dir, text='Browse', command=self.browse)
133:             self.b_dir.pack(side=Tk.LEFT, padx=2)
134:             f=Tk.Frame(self)
135:             f.pack(fill=Tk.X)
136:             b_info=Tk.Button(f, bitmap='info', width=25, command=self.show_info)
137:             b_info.pack(side=Tk.RIGHT, padx=20, pady=10, anchor=Tk.CENTER)        
138:             f_type = Tk.LabelFrame(f, text='File Type')
139:             f_type.pack(side=Tk.LEFT, padx=10, pady=2)
140:             b_custom_bg = Tk.Button(f, text='Customize Background', command=self.cus_bg)
141:             b_custom_bg.pack(side=Tk.LEFT, anchor=Tk.S, padx=10)
142:             
143:             self.var_type = dict()
144:             for image_type in IMAGR_TYPES:
145:                 self.var_type[image_type] = Tk.IntVar()
146:                 cb = Tk.Checkbutton(f_type, text=image_type, variable=self.var_type[image_type],
147:                                     relief=Tk.FLAT, justify = Tk.LEFT)
148:                 cb.select()
149:                 cb.pack(side=Tk.LEFT, padx=5)
150:     
151:             ImageLabel.bg_var = Tk.StringVar()
152:             ImageLabel.bg_var.set('#FFFFFF')
153:     
154:             self.f_bg = Tk.LabelFrame(self, text='Background')
155:             self.f_bg.pack(anchor=Tk.W, padx=10, pady=2)
156:             
157:             for name, code in BGS:
158:                 rb = Tk.Radiobutton(self.f_bg, text=name, variable=ImageLabel.bg_var, value=code,
159:                                     bg=code, command=self.change_bg, bd=1, relief=Tk.GROOVE)
160:                 rb.pack(side=Tk.LEFT, padx=5)
161:     
162:             self.image_frame = ImageFrame(self, relief=Tk.RIDGE, border=2)
163:     
164:     
165:     
166:     
167:         def change_bg(self):
168:             bg1 = ImageLabel.bg_var.get()
169:             label = ImageLabel.id_original_size
170:             if label and label.winfo_exists():
171:                 label.configure(bg=bg1)
172:                 top = label.winfo_toplevel()
173:                 top.focus_set()
174:             
175:         def browse(self):
176:             dir = D.askdirectory()
177:             if dir:
178:                 self.e_dir.delete(0, Tk.END)
179:                 self.e_dir.insert(0, dir)
180:                 self.image_frame.update(dir, self.var_type)
181:     
182:         def show_sum(self, event):
183:             self.image_frame.update(self.e_dir.get(), self.var_type)
184:             
185:     
186:         def show_info(self):
187:             M.showinfo(u"使い方",  u"Entry か Browse ボタンでディレクトリを選択してください。\n"
188:                                    u"そのディレクトリに含まれる画像ファイルの一覧が表示されます。\n"
189:                                    u"一覧にある画像をクリックすると、別窓でオリジナルサイズの画像が表示されます。\n"
190:                                    u"透過型 GIF の場合は背景色を変えることができます。\n"
191:                                    u"ラジオボタンにある背景色が気に入らない場合は、\n"
192:                                    u"Customize Background ボタンを押して現れる\n赤、緑、青用の3つのスケールで"
193:                                    u"背景色を調節してください。")
194:     
195:         def cus_bg(self):
196:             if not (self.cus_top and  self.cus_top.winfo_exists()):
197:                 self.cus_top = Tk.Toplevel(self)
198:                 self.cus_top.title('Create Back Ground')
199:                 self.cus_top.geometry(GEO_SCAL)
200:                 cusf = Tk.Frame(self.cus_top)
201:                 cusf.pack(fill=Tk.BOTH, padx=10, pady=10)
202:                 self.scale = dict()
203:                 for i, color in enumerate(('red', 'green', 'blue')):
204:                     l=Tk.Label(cusf, text=color+': ', anchor=Tk.W, fg=color, font=('Helvetica', '10', 'bold'))
205:                     l.grid(row=i, column=0, sticky=Tk.W)
206:                     self.scale[color] = Tk.Scale(cusf, orient=Tk.HORIZONTAL, length=300, from_=0, to=255, 
207:                                                  command=self.customize_bg, tickinterval=50)
208:                     self.scale[color].set(255)
209:                     self.scale[color].grid(row=i, column=1)
210:     
211:                 self.cus_top.bind('<FocusIn>', self.customize_bg)
212:     
213:         def customize_bg(self, event):
214:             ImageLabel.bg_var.set('#%02X%02X%02X' %
215:                             (self.scale['red'].get(), self.scale['green'].get(), self.scale['blue'].get()))
216:             self.change_bg()
217:     
218:     ##------------------------------------------------ 
219:     
220:     if __name__ == '__main__':
221:         f = Frame()
222:         f.pack()
223:         f.mainloop()

このプログラムのミソは、ImageLabel クラスです。 刺激に反応する Widget は Tk.Widget の派生クラスとして 定義するとうまく書けます。与えられた刺激にどう反応するかを method として含めてしまえばいいのです。 また、クラス変数 id_original_sizebg_var を定義してメインフレーム (図1a) と ImageLabel のインスタンスで変数を共有しています。

プログラムが若干長いので、部分ごとに説明していきたいと思います。見出しのカッコ内の数字はコードの行数です。

2.1. import するモジュールの宣言

12-19 行目でre, os, os.path(P), Tkinter(Tk), Image(I), ImageTk(Itk), tkFIleDialog(D), tkMessageBox(M) の8つのモジュールを読み込んでいます。 このうち、tkFileDialogtkMessageBox は初出なので、 3.8.3.9. で説明しています。

2.2. 定数の宣言

22--30 行目で定数を宣言しています。
定数 説明
SIZE 一覧に表示する画像のサイズです。
IMAGR_TYPES 画像ファイルの種類です。
MAM_COLN 一覧に表示する列の数です。
GEO_MAIN メイン window の位置を指定します。
GEO_SATE 原寸画像 window の位置を指定します。
GEO_SCAL 背景色調節 window の位置を指定します。
BGS あらかじめ登録されている背景色のリストです。

2.3. 関数の宣言

以下の2つの関数を使います。
get_size(tup) (34--44)
tup のどちらかの値が 100 を超えている場合、tup の2つの値の比を保ちながら、 両方の値が 100 以下になるように縮小した値のタプルを返します。
画像ファイルのリサイズに使います。
make_regexp(types) (47--55)
Tk.IntVar のマップ types (キーはファイルタイプ)に基づいて、 ファイル名を選別する正規表現を作成して返します。

2.4. class ImageLabel(Tk.Frame)

一覧表示する画像のクラスです。クリックされると原寸大のイメージを別窓で表示します。(59--87)

2.5. class ImageFrame

イメージの一覧を表示するフレームを定義するクラスです。(91--114)

2.6. class Frame(Tk.Frame):

いよいよメインのクラスの説明です。__init__ でいろいろな widget を定義しています。(118--216)

3. Widget ごとの説明

以下に viewer.py で登場した widget について簡単に説明します。

3.1. Tk.Checkbutton

Tk.Checkbutton はチェックリストを作るとき使います。 Tk.IntVar と組み合わせて使います。通常個の変数はチェックされたとき 1, されていないとき 0 をとりますが、変えることもできます。通常は1つの Tk.CheckbuttonIntVar を対にして使います。

以下にに使用例を示します。

[code 2](jcheck.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     """
04:     jcheck.py 
05:     
06:     """
07:     
08:     
09:     import Tkinter as Tk
10:     
11:     class Frame(Tk.Frame):
12:         def __init__(self, master=None):
13:             Tk.Frame.__init__(self, master)
14:             la = Tk.Label(self, text=u"普段お使いの OS は何ですか?(複数選択可)")
15:             la.pack(padx=5, pady=5)
16:             f1 = Tk.Frame(self)
17:             f1.pack(padx=5, pady=5)
18:             self.str = Tk.StringVar()
19:             self.v=dict()
20:             for your_os in ['Unix', 'Windows', 'Mac', 'VMS', 'OS/2']:
21:                 self.v[your_os] = Tk.IntVar()
22:                 cb = Tk.Checkbutton(f1, text=your_os, variable=self.v[your_os], command=self.echo)
23:                 cb.pack(side=Tk.LEFT)
24:     
25:             lb = Tk.Label(self, textvariable = self.str, justify=Tk.LEFT, anchor=Tk.W)
26:             lb.pack(padx=5, pady=5, fill=Tk.X)
27:     
28:         def echo(self):
29:             if sum ([v.get() for v in self.v.values()]) == 0:
30:                 self.str.set(u"私はメインフレームしか使ったことがありません。")
31:             else:
32:                 str = u"私が使っている OS は、\n"
33:                 for your_os, v in self.v.iteritems():
34:                     if v.get():
35:                         str += your_os + u"と、"
36:                 self.str.set(str[0:-2] + u"です。")
37:     
38:     ##------------------------------------------------ 
39:     
40:     if __name__ == '__main__':
41:         f = Frame()
42:         f.pack()
43:         f.mainloop()
こんな感じの window ができます。

Tk.Checkbutton の option とメソッドについては以下を参照してください。

3.2. Tk.Entry

ユーザーからのキー入力を受け付けるための widget です。 使用例は
Appendix 2. Widget に日本語を書く: 2. Entry で取得した文字列を日本語で表示する。 を見てください。 Tk.Entry の option と method については以下を参照してください。

3.3. Tk.LabelFrame

外枠にテキストが表示されるほかは Tk.Frame とほぼ同じです。使用例は [code 3] を見てください。 option, method については、
The Tkinter LabelFrame Widget を見てください。

3.4. Tk.RadioButton

複数の選択肢から1つを選ぶための widget です。 Tk.StringVar, Tk.IntVar, または Tk.DoubleVar を共有させます。 以下に例を示します。

[code 3](jradio.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     """
04:     jradio.py 
05:     
06:     """
07:     
08:     
09:     import Tkinter as Tk
10:     
11:     COLS = [(u'黒', 'black'), (u'白', 'white'), (u'赤', 'red'), (u'緑', 'green'), (u'黄', 'yellow'), (u'青', 'blue')]
12:     
13:     
14:     def assoc(key, ls):
15:         for k, v in ls:
16:             if key == k:
17:                 return v
18:     
19:     class Frame(Tk.Frame):
20:         def __init__(self, master=None):
21:             Tk.Frame.__init__(self, master)
22:             
23:             self.vf = Tk.StringVar()            # 文字色
24:             self.vb = Tk.StringVar()            # 背景色
25:             self.str = Tk.StringVar()           # 表示文字列
26:     
27:     
28:             
29:             ff = Tk.LabelFrame(self, text=u'文字色')
30:             ff.pack(padx=5, pady=5)
31:             fb = Tk.LabelFrame(self, text=u'背景色')
32:             fb.pack(padx=5, pady=5)
33:             
34:             for jp, en in COLS:
35:                 # 文字色の選択肢
36:                 rbf = Tk.Radiobutton(ff, text=jp, variable=self.vf, value=jp, command=self.echo)
37:                 if en == 'black':
38:                     rbf.select()
39:                 rbf.pack(side=Tk.LEFT)
40:                 # 背景色の選択肢
41:                 rbb = Tk.Radiobutton(fb, text=jp, variable=self.vb, value=jp, command=self.echo)
42:                 if en == 'white':
43:                     rbb.select()
44:                 rbb.pack(side=Tk.LEFT)
45:                 
46:             self.la = Tk.Label(self, textvariable=self.str, font=('Helvetica', '14', 'bold'))
47:             self.la.pack(padx=5, pady=5, fill=Tk.X)
48:             
49:             self.echo()
50:     
51:     
52:         def echo(self):
53:             f = self.vf.get()
54:             b = self.vb.get()
55:             self.str.set (u'%s地に%s字' % (b, f))
56:             self.la.configure(fg=assoc(f, COLS))
57:             self.la.configure(bg=assoc(b, COLS))
58:     
59:     ##------------------------------------------------ 
60:     
61:     if __name__ == '__main__':
62:         f = Frame()
63:         f.pack()
64:         f.mainloop()

こんな感じの window ができます。

Tk.Radiobutton の option とメソッドについては以下を参照してください。

3.5. Tk.Scale

値を微妙に変えるために用いる widget です。以下に使用例を示します。 スケールをいじると Frame の色が変わります。

[code 4] (jscale.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     """
04:     jscale.py 
05:     
06:     """
07:     
08:     
09:     import Tkinter as Tk
10:     
11:     
12:     def e2j(en):
13:         if en=='red':
14:             return u'赤'
15:         elif en=='green':
16:             return u'緑'
17:         else:
18:             return u'青'
19:     
20:     
21:     def change_bg_rec(item, color):
22:         item.configure(bg=color)
23:         for child in item.winfo_children():
24:             change_bg_rec(child, color)
25:     
26:         
27:         
28:     class Frame(Tk.Frame):
29:         def __init__(self, master=None):
30:             Tk.Frame.__init__(self, master)
31:             info = Tk.Label(self, text=u'スケールをいじってフレームの色を変えてください。', font=('Helvetica', '12', 'bold'))
32:             info.pack(padx=10, pady=5)
33:             f = Tk.Frame(self)
34:             f.pack(padx=5, pady=5)
35:             self.sc = dict()
36:             for i, color in enumerate(['red', 'green', 'blue']):
37:                 la = Tk.Label(f, text=e2j(color), fg=color, font=('Helvetica', '12', 'bold'))
38:                 la.grid(row=0, column=i, padx=10, pady=5)
39:                 self.sc[color] = Tk.Scale(f, from_=0, to=255, length=150, bd=0, 
40:                                           command=self.change_bg, tickinterval=50, orient=Tk.VERTICAL)
41:                 self.sc[color].set(255)
42:                 self.sc[color].grid(row=1, column=i, padx=10, pady=5)
43:                 
44:     
45:         def change_bg(self, event):
46:             color = '#%02X%02X%02X' % (self.sc['red'].get(), self.sc['green'].get(), self.sc['blue'].get())
47:             change_bg_rec(self, color)
48:     
49:             
50:     
51:     ##------------------------------------------------ 
52:     
53:     if __name__ == '__main__':
54:         f = Frame()
55:         f.pack()
56:         f.mainloop()

こんな感じの window ができます。

Tk.Scale の option とメソッドについては以下を参照してください。

3.6. Tk.Spinbox

Tk.Spinbox は [code 1] に出てきませんでしたが、ついでに説明します。 Tk.Listbox と同じように選択肢から値を選ぶとき使います。

[code 5](jspinbox.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     """
04:     jspinbox.py
05:     """
06:     
07:     
08:     
09:     import Tkinter as Tk
10:     
11:     
12:     CONSTELLATIONS = ( u'水瓶座', u'魚 座', u'牡羊座', u'牡牛座', u'双子座', u'蟹 座', u'獅子座',
13:                        u'乙女座', u'天秤座', u'蠍 座', u'射手座', u'山羊座')
14:     
15:     
16:     BLOOD = ('A', 'B', 'O', 'AB')
17:     
18:     FORTUNE = [u'大吉', u'中吉', u'吉', u'凶']
19:     
20:     
21:     def position(obj, tup):
22:         for i, o in enumerate(tup):
23:             if o==obj:
24:                 return i
25:     
26:     class Frame(Tk.Frame):
27:         def __init__(self, master=None):
28:             Tk.Frame.__init__(self, master)
29:             info = Tk.Label(self, text=u'Spinbox をつかったいかさま占い。')
30:             info.pack(padx=5, pady=5)
31:             f = Tk.Frame(self)
32:             f.pack(padx=5, pady=5)
33:             lb=Tk.Label(f, text=u'血液型')
34:             lb.grid(row=0, column=0, padx=5, pady=5)
35:             self.sb=Tk.Spinbox(f, values=BLOOD)
36:             self.sb.grid(row=0, column=1, padx=5, pady=5)
37:             lc=Tk.Label(f, text=u'星座')
38:             lc.grid(row=1, column=0, padx=5, pady=5)
39:             self.sc = Tk.Spinbox(f, values=CONSTELLATIONS)
40:             self.sc.grid(row=1, column=1, padx=5, pady=5)
41:             b=Tk.Button(self, text=u'占う', command=self.tell_fortune)
42:             b.pack(padx=5, pady=5)
43:             self.fortune=Tk.StringVar()
44:             lf = Tk.Label(self, textvariable=self.fortune, fg='red', font=('Helvetica', '14', 'bold'))
45:             lf.pack(padx=5, pady=5)
46:     
47:         def tell_fortune(self):
48:             i = position(self.sb.get(), BLOOD) + 1
49:             j = position(self.sc.get(), CONSTELLATIONS) + 1
50:             self.fortune.set(FORTUNE[(i*j*j)%4])
51:         
52:     
53:     ##------------------------------------------------ 
54:     
55:     if __name__ == '__main__':
56:         f = Frame()
57:         f.pack()
58:         f.mainloop()

こんな感じの window ができます。

option, method については、The Tkinter Spinbox Widge を参照してください。

3.7. Tk.Toplevel

別 Window を表示するための Widget です。作成するときに親 widget を指定することができ、 親 widget があるときは親 widget が消えると親子関係にある toplevel も消えます。 [code 5] に例を示します。

[code 5](jtop.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     """
04:     jtop.py 
05:     
06:     """
07:     
08:     
09:     import Tkinter as Tk
10:     
11:     class Mtop:
12:         def __init__(self, master):
13:             self.master = master
14:     
15:         def __call__(self, event=None):
16:             top = Tk.Toplevel(self.master)
17:             f = Tk.Frame(top)
18:             f.pack()
19:             la = Tk.Label(f, text=self.master and u'子供' or u'他人', font =('Helvetica', '12'))
20:             la.pack()
21:             lb = Tk.Label(f, text=self.master and u'この Toplevel は親フレームとともに消えます。' or
22:                                                   u'この Toplevel は親フレームが消えても残ります。' )
23:             lb.pack()
24:     
25:     class Frame(Tk.Frame):
26:         def __init__(self, master=None):
27:             Tk.Frame.__init__(self, master)
28:             l = Tk.Label(self, text=u'下のボタンを押して、下の \'親フレーム\' と親子関係にある TopLevel と \n'
29:                          u'独立した Toplevel を作成してください。\n'
30:                          u'それから、\'親フレームを消す\'ボタンを押すと、\n親フレームと、親子関係にある Toplevel が消え、\n'
31:                          u'独立した Toplebel は残ります。', justify=Tk.LEFT)
32:             l.pack(padx=10, pady=5)
33:             f = Tk.LabelFrame(self, text=u'親フレーム')
34:             f.pack(padx=10, pady=5)
35:             bc = Tk.Button(f, text=u'親フレームの子供のトップレベルを作成', command=Mtop(f))
36:             bc.pack(fill=Tk.X, pady=5, padx=10)
37:             bn = Tk.Button(f, text=u'他人のトップレベルを作成', command=Mtop(None))
38:             bn.pack(fill=Tk.X, pady=5, padx=10)
39:             bd = Tk.Button(f, text=u'親フレームを消す', command=f.destroy)
40:             bd.pack(fill=Tk.X, pady=5, padx=10)
41:             
42:             
43:     
44:     ##------------------------------------------------ 
45:     
46:     if __name__ == '__main__':
47:         f = Frame()
48:         f.pack()
49:         f.mainloop()
Tk.Toplevel の option とメソッドについては以下を参照してください。

こんな感じの window ができます。




3.8. tkFileDialog

ファイル(名)を取得するダイアログを生成するモジュールです。 以下の関数があります。
askdirectory(**options) ディレクトリ名を入力するダイアログを表示し、ディレクトリ名を返します。
askopenfile(mode='r', **options) ファイル名を入力するダイアログを表示し、開いたファイルハンドルを返します。
askopenfilename(**options) ファイル名を入力するダイアログを表示し、ファイル名を返します。
askopenfilenames(**options) ファイル名を入力するダイアログを表示し、ファイル名のリストを返します。
askopenfiles(mode='r', **options) ファイル名を入力するダイアログを表示し、ファイルハンドルのリストを返します。
asksaveasfile(mode='w', **options) ファイル名を入力するダイアログを表示し、開いているファイルハンドルを返します。
asksaveasfilename(**options) ファイル名を入力するダイアログを表示し、ファイル名を返します。
**option には filetypes, initialdir, initialfile の3つがあります。

[code 6] に選択したファイルを開いて、標準出力に最初の 15 行を出力するプログラムを示します。

[code 6](py_head.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     
04:     import tkFileDialog as FD
05:     
06:     f = FD.askopenfile(mode='r', initialfile='index.html', 
07:                        initialdir='D:/doc/05-07/sh_home',
08:                        filetypes =[('html files', ('*.html', '*.htm')),
09:                                    ('text files', ('*.txt', '*.css'))])
10:                                    
11:     
12:     for i, l in enumerate(f):
13:         if i<15:
14:             print '%02d: %s' % (i+1, l.rstrip())
15:     

3.9. tkMessageBox

メッセージを表示するダイアログを作成するモジュールです。 以下の関数があります。
askokcancel(title=None, message=None, **options) OK か Cancel か聞いてきます。OK なら True を返します。
askquestion(title=None, message=None, **options) 何か質問をします。
askretrycancel(title=None, message=None, **options) 再試行するか聞いてきます。 Yes → True
askyesno(title=None, message=None, **options) 質問をします。 Yes → True
showerror(title=None, message=None, **options) エラーメッセージを表示します。
showinfo(title=None, message=None, **options) Info を表示します。
showwarning(title=None, message=None, **options) 警告を表示します。
**option を指定することはまずないでしょう。使用例は [code 1] の 186--193 行目を見てください。

3.10. Dialog

MessageBox は、ある程度決まった種類の質問しかできないのに対して、、Dialog いろいろな質問ができるダイアログです。 [code 7] に例を示します。

[code 7] (jdialog.py)

01:     #! /usr/bin/env python
02:     # -*- coding: shift_jis -*-
03:     
04:     import Tkinter as Tk
05:     import Dialog as D
06:     
07:     FLOWERS = [u'あさがお', u'タンポポ', u'ゆり', u'はす', u'桜']
08:     
09:     class Frame (Tk.Frame):
10:         def __init__(self, master=None, **key):
11:             Tk.Frame.__init__(self, master, **key)
12:             self.master.title(u'好きな花はなんですか')
13:             svar = Tk.StringVar()
14:             label = Tk.Label(textvariable=svar, font=('Helvetica', '12'))
15:             label.pack(fill=Tk.BOTH, expand=1)
16:             dialog = D.Dialog(self, title=u'好きな花',
17:                                     text=u'下のボタンから好きな花を選んでください。',
18:                                     bitmap='question',
19:                                     default=0,
20:                                     strings=FLOWERS)
21:             svar.set(u'%s がお好きなのですね。' % FLOWERS[dialog.num])
22:     
23:     if __name__=='__main__':
24:         f = Frame()
25:         f.pack(fill=Tk.BOTH, expand=1)
26:         f.mainloop()

こんな感じの window ができます。

16 行目の Dialog で、回答を促すダイアログを生成します。 最初の引数は親 widget, 残りはキーワードパラメターです。

押されたボタンの番号は内部変数 .num に保存されます。(21 行目)

4. 終わりに

今回は、少し長くてうんざりされたかもしれません。 興味のあるところを拾い読みしてください。 これで、小物 widget の使い方は一通り説明したことになります。 ここで示したコードは付録についていますので、気が向いたら遊んでください。

さて、今回作った Image Viewer は Widget が多くてうるさいので、 メニューバーにまとめたものを次回作ります。 また、一覧を表示するのがフレームだとスクロールバーがつけられないので、 一覧を ScrolledText に表示するようにします。