●エントリーウィジェット
- エントリー (entry) は 1 行の文字列を入力、または編集することができるウィジェット
- エントリーオブジェクトは Entry() で生成する
- よく使うオプションは textvariable で、入力されたデータは指定したオブジェクトに格納される
- オブジェクトの値が変更されると、エントリーの内容も変更される
- オプション show はパスワードのように画面に見えてはいけない文字列を打ち込むときに使う
- たとえば、show = '*' とすれば、入力された文字は * として表示される
- メソッドも多数用意されているが、文字列の入力だけならば、それらを使う機会はあまりないと思われる
リスト : 式入力電卓
import tkinter as tk
from math import *
root = tk.Tk()
root.option_add('*font', ('', 14))
# 式を格納するオブジェクト
buffer = tk.StringVar()
buffer.set("")
# 計算
def calc(event):
if buffer.get():
value = eval(buffer.get())
buffer.set(str(value))
# エントリー
e = tk.Entry(root, textvariable = buffer)
e.pack()
e.focus_set()
# バインディング
e.bind('<Return>', calc)
root.mainloop()
- 計算は関数 eval() を使えば簡単
- eval() は与えられた文字列を Python の式として評価する
- モジュール math をインポートしておけば、sin(), cos(), tan() などの関数を呼び出すことができる
- 一般の GUI アプリケーションの場合、キー入力はアクティブになっているウィンドウに渡される
- Tk では、これを「フォーカスウィンドウ (focus window)」という
- フォーカスはメソッド focus_set() によってプログラムで設定することができる
widget.focus_set()
e.focus_set() でフォーカスウィンドウをエントリーに設定する
bind() はあとで説明する
数式を入力する
リターンキーで計算する
●イベント処理
widget.bind(eventsequence, callback)
すでにバインドされているコールバック関数がある場合、新しい関数に差し替えられる
callback を省略すると、そのイベントにバインドされている関数が返される
引数を省略すると、そのウィジェットにバインドされているすべてのイベントをタプルに格納して返す
コールバック関数の引数にはイベントを表すオブジェクト(クラス Event のインスタンス)が渡される
このオブジェクトからイベントの詳細情報を取得することができる
<modifier-modifier-type-detail>
type は GUI 環境上で発生するイベントタイプを表す
ユーザーが操作するときに発生する主なイベントタイプには次のようなものがある
表:イベントタイプ
Key, KeyPress | キーが押された |
KeyRelease | キーが離された |
Button, ButtonPress | マウスのボタンが押された |
ButtonRelease | マウスのボタンが離された |
Motion | マウスの移動 |
Enter | マウスカーソルがウィンドウの中に入った |
Leave | マウスカーソルがウィンドウから出た |
- このほかにも、ウィンドウが破棄されたときに発生するイベントなど、様々なイベントタイプがある
- マウスとキーのイベントには、ボタンやキーの種類を detail で指定する
- マウスでは左ボタンが 1 となる
- キーの種類は名前で指定する
- 英数字はその文字がそのまま名前となる
- このほかに、改行キーに対する Return、バックスペースキーに対する BackSpace などがある
- detail を指定する場合は type を省略することができる
- <1> という指定は <KeyPress-1> ではなく <Button-1> となるので注意
- 通常の英数字の場合、<> も省略することができる
- つまり、<KeyPress-a> は a と書くことができる
- <KeyPress> のように detail を省略すると、種類によらずキーが押されたときにバインドされたコマンドが実行される
- イベントタイプの前にはモディファイア (modifier) をつけることができる
- たとえば、<Control-d> はコントロールキーと d キーを同時に押したときのイベントを表す
- 主なモディファイアを下表に示す
表:モデファイア
Control | Ctrl キーを押しながらの入力 |
Shift | Shift キーを押しながらの入力 |
Alt | Alt キーを押しながらの入力 |
Button1, B1 | マウスの左ボタンを押しながらの入力 |
Button3, B3 | マウスの右ボタンを押しながらの入力 |
Double | ダブルクリック |
Triple | トリプルクリック |
- Tk の出身地である X Window は 3 ボタンマウスを使う
- Button2 は右ボタンではなく中ボタンとなる
- たとえば、左ボタンのダブルクリックに対応するイベントは <Double-1> となる
- イベントタイプは複数個指定することができる
- たとえば、<Escape>a はEsc キーが押されたあとで a キーを押したイベントに対応する
- イベントの詳細情報はイベントオブジェクトのインスタンス変数にセットされてコールバック関数に渡される
- たとえば、下表に示すようなイベント情報がある
表 : イベントの詳細情報
変数名 | データ |
num | マウスボタンの番号 |
x,y | マウスカーソルの座標 |
time | イベントの発生時刻 |
char | キーに対応する文字 |
keysym | キーに対応する名前 |
- これ以外にも、いろいろな情報がある
- 詳細は Tkinter のマニュアルを参照
リスト : キーの名前を表示
import tkinter as tk
root = tk.Tk()
root.option_add('*font', ('', 14))
buffer = tk.StringVar()
buffer.set('')
# キーの表示
def print_key(event):
key = event.keysym
buffer.set('push key is {}'.format(key))
# ラベルの設定
tk.Label(root, text = '*** push any key ***').pack()
a = tk.Label(root, textvariable = buffer)
a.pack()
a.bind('<Any-KeyPress>', print_key)
a.focus_set()
root.mainloop()
- 関数 print_key() にはイベントオブジェクト event が渡される
- event.keysym で押されたキーの名前を求めることができる
- 実際に試してみると、F1 や F2 キーには F1, F2 という名前が割り当てられていることがわかる
F1 キーを押したときの動作
●リストボックス
- リストボックスは複数の文字列を表示し、ユーザーはその中からひとつ以上の文字列を選択することができる
- リストボックスのオブジェクトは Listbox() で生成する
- Listbox() で指定する主なオプションを下表に示す
表 : Listbox() の主なオプション
xscrollcommand | x 方向のスクロールメソッドを指定 |
yscrollcommand | y 方向のスクロールメソッドを指定 |
selectmode | セレクションモード |
- xscrollcommand/yscrollcommand にはスクロールバーウィジェットのメソッド set() を指定する
- リストボックスの表示範囲が変更されたとき、指定したメソッド set() が呼び出される
- これは Scrollbar() と一緒に説明する
- セレクションには以下のモードが用意されている
- single
ひとつの行をマウスの左クリックで選択する。
- browse
single と同じだが、ドラッグによって選択される行が変化し、
ボタンを離したところの行が選択される。
- multiple
左クリックで複数行を選択する(ドラッグは不可)。
- extended
ドラッグで複数行を選択するが、左クリックではいままで選択した行はキャンセルされ、クリックした行のみ選択される。
CTRL キーを押しながら左クリックするとトグル動作(結果が反転)となり、
シフトキーを押しながら左クリックすると直前に左クリックした行から現在の行までが選択される。
- デフォルト値は browse
- データの挿入、削除、取得は次のメソッドで行う
- insert(index, *element)
指定した位置の直前に文字列を挿入
- delete(first, last)
指定した範囲の行を削除する
- get(first, last)
指定した範囲の行をリストに格納して返す
- index(index)
指定した位置の行番号を返す
- curselection()
選択された行番号をリストにして返す
- see(index)
指定した位置が見えるようにスクロールする
- このほかに、スクロールバーに関係する xview/yview メソッドが重要
- これはスクロールバーのところで説明する
- 位置の指定方法
- n (数値) : n 行目
- active : 左ボタンを離したときの行
- anchor : 左ボタンを押したときの行
- end : 最後の行、insert() で指定すると最終行の次にデータが追加される
- @x,y : 指定した座標に最も近い行
- セレクションモードが extended のときにドラッグで選択した場合、最初の行が anchor で最後の行が active となる
- したがって、delete に anchor と active を指定すると、選択した行をリストボックスから削除することになる
リスト : 色の選択
import tkinter as tk
root = tk.Tk()
root.option_add('*font', ('', 12))
buff = tk.StringVar()
buff.set("")
# 色の選択
def get_color(event):
xs = lb.curselection()
if len(xs) != 0:
color = lb.get(xs[0])
buff.set(color)
la.configure(bg = color)
# ラベルの生成
la = tk.Label(root, textvariable = buff)
la.pack(fill = tk.X)
# リストボックスの生成
lb = tk.Listbox(root)
lb.pack()
# バインディング
lb.bind('<ButtonRelease-1>', get_color)
for x in ['red', 'green', 'blue', 'yellow', 'cyan', 'pink', 'white', 'black']:
lb.insert('end', x)
root.mainloop()
- リストボックスに表示された色を選択してラベルの背景色を変更する
- 関数 get_color() で選択した色を取得する
- curselection() の返り値 xs が空リストの場合は未選択
- xs の先頭に選択した行の番号が格納されている
- get_color() はリストボックスのイベント ButtonRelease-1 にバインドする
- イベントには <ListboxSelect> があるので、それにバインドしてもよい
- 具体的には lb.bind('<<ListboxSelect>>', get_color) とする
- <ListboxSelect> はリストボックス上でマウスをドラッグしている間も発生する
- その間ラベルの背景色が変わることになる
- <ListboxSelect> にバインドするときは selectmode を single に設定したほうが使いやすいかもしれない
初期状態
yellow を選択
pink を選択
●スクロールバー
- スクロールバーはほかのウィジェットの表示範囲を制御するウィジェット
- スクロールバーはその両端に矢印がつき、中央付近には四角いスライダが表示される
- 矢印を左クリックするか、スライダをドラッグすることで表示位置を変更する
- また、矢印とスライダの隙間をクリックすると 1 画面分スクロールする
- スクロールバーは Scrollbar() でオブジェクトを生成する
- Scrollbar() で主に使用されるオプションを下表に示す
表:Scrollbar() の主なオプション
orient | スクロールバーの方向 |
troughcolor | 矢印とスクロールの隙間の色 |
command | スクロールバーが動いたときに実行するメソッド |
- orient で horizontal または h を指定すると水平になり、vertical または v で垂直になる
- command はスクロールバーを動かしたときに実行する関数を指定する
- リストボックスと連動させる場合、ここにリストボックスの表示位置を制御するメソッド xview() や yview() を指定する
- スクロールバーのメソッド set() はリストボックスのオプション xscrollcommand / yscrollcommand で使用する
xscrollcommand = scrollbar_widget.set
リストボックスで表示範囲が変更されると set() が実行される
このとき、リストボックスの表示範囲 (first, last) が引数として渡される
first と last は 0 から 1 の間の実数で、表示されている範囲を表す
たとえば、リストボックスの全体の行数が 100 行で 20 行目から 30 行分表示されているとすると、set(0.2, 0.5) となる
つまり、データ全体の 20 % の位置から 50 % の位置まで表示されていることを表す
- スクロールバーを変更したとき、それをリストボックスに反映させるためのオプションが command
- ここにリストボックスのメソッド xview や yview を指定する
command = listbox_widget.yview
スクロールバーの操作によって、メソッドには次に示す文字列が引数として渡される
- 'moveto', 数値
指定した数値 (0 - 1.0) の位置までスクロール
- 'scroll', n, unit
上または下に n 単位スクロールする
- 'scroll', n, pages
上または下に n ページスクロールする
リスト : フォントの表示
import tkinter as tk
import tkinter.font as font
root = tk.Tk()
# フォントの選択
def get_font(event):
xs = lb.curselection()
if len(xs) != 0:
la.configure(font = (lb.get(xs[0]), 12))
# ラベルの生成
la = tk.Label(root, text = "Hello, world!, こんにちは世界!!", font = ('', 12))
la.pack(fill = tk.X)
# リストボックスの生成
lb = tk.Listbox(root, selectmode = 'single', height = 20, width = 40)
lb.pack(side = 'left')
# スクロールバーの生成
sb = tk.Scrollbar(root, command = lb.yview)
sb.pack(side = 'left', fill = 'y')
lb.configure(yscrollcommand = sb.set)
# バインディング
lb.bind('<<ListboxSelect>>', get_font)
for x in sorted(font.families()):
lb.insert('end', x)
root.mainloop()
- 使用できるフォント名は tkinter.font.families() で取得できる (返り値はリスト)
- フォント名をリストボックスに入れて、右横にスクロールバーを付ける
- lb と sb の pack() には side = 'left' を指定する
- sb.pack() には fill = 'y' の指定も必要になる
- リストボックスを生成するとき、まだ Scrollbar() は実行されていない
- Scrollbar() を実行してから configure() で yscrollcommand に sb.set をセットする
- 関数 get_font() で選択したフォント名を取得し、ラベルの font を変更する
初期状態
Consolas を選択
Ricty Diminished を選択
メイリオを選択
●Gridder
- メソッド grid() はウィジェットを格子状に配置するジオメトリーマネージャ (Gridder)
- ウィンドウを M 行 N 列のセルに分割し、そこにウィジェットを配置する
- x 方向の位置はオプション column で指定する
- y 方向の位置は row で指定する
- オプション columnspan() と rowspan() は、複数のセルにまたがってウィジェットを配置するために使う
- columnspan は x 方向にまたがるセルの数を指定する
- rowspan は y 方向にまたがるセルの数を指定する
- grid() はオプション sticky でウィジェットを引き伸ばす
表:sticky オプション
n | 上寄せ |
s | 下寄せ |
e | 右寄せ |
w | 左寄せ |
ns | 上下方向に引き伸ばす |
ew | 左右方向に引き伸ばす |
- sticky は pack() のオプション anchor と同じ機能もあわせ持っている
リスト : ボタンを格子状に配置
import tkinter as tk
root = tk.Tk()
for x in range(5):
for y in range(5):
tk.Button(text = ' {} '.format(y * 5 + x + 1)).grid(column = x, row = y, sticky = 'ew')
root.mainloop()

リスト : 履歴付き式入力電卓
import tkinter as tk
from math import *
root = tk.Tk()
root.option_add("*font", ('', 14))
# 式を格納するオブジェクト
buffer = tk.StringVar()
buffer.set("")
# Entry の生成
e = tk.Entry(root, width = 30, textvariable = buffer)
# Listbox の生成
lb = tk.Listbox(root, width = 30, selectmode = 'single')
# Scrollbar の生成
sb1 = tk.Scrollbar(root, orient = 'v', command = lb.yview)
sb2 = tk.Scrollbar(root, orient = 'h', command = lb.xview)
# Listbox の設定
lb.configure(yscrollcommand = sb1.set)
lb.configure(xscrollcommand = sb2.set)
# 計算
def calc(event):
expr = buffer.get()
lb.insert('end', expr)
lb.see('end')
value = eval(expr)
buffer.set(str(value))
e.icursor(0)
# 式の取り出し
def get_expr(event):
xs = lb.curselection()
if len(xs) != 0:
buffer.set(lb.get(xs[0]))
e.focus_set()
# バインディング
e.bind('<Return>', calc)
lb.bind('<Double-1>', get_expr)
# grid による配置
e.grid(row = 0, columnspan = 2, sticky = 'ew')
lb.grid(row = 1, column = 0, sticky = 'nsew')
sb1.grid(row = 1, column = 1, sticky = 'ns')
sb2.grid(row = 2, column = 0, sticky = 'ew')
# フォーカスの設定
e.focus_set()
root.mainloop()
- エントリーは 2 つのセル (0, 0) と (0, 1) にまたがって配置するので columnspan を 2 に設定する
- リストボックスはセル (1, 0) に配置する
- リストボックスの右側のセル (1, 1) と下側のセル (2, 0) にスクロールバーをセットする
- エントリーではリターンキーが入力されると関数 calc() が実行される
- calc() では式をリストボックスに代入し、計算結果を buffer にセットする
- メソッド see() を使ってセットした計算式が見えるようにスクロールする
- エントリーのメソッド icursor(index) はカーソルを index の位置に移動する
- 今回は先頭 (0) に移動する (最後尾の場合は 'end' または tk.END を指定する)
- リストボックスで行をダブルクリックすると関数 get_expr() が実行される
- 選択した式を取り出して buffer にセットし、フォーカスをエントリーにセットする
式の履歴が残るように改造した電卓
リターンキーを押せば計算結果も履歴に入る
式の選択
●チェックボタンとラジオボタン
- チェックボタンは Checkbutton() で生成し、ON / OFF のような二者択一の情報を設定するために使う
- ラジオボタンは Radiobutton() で生成し、複数の値からひとつを選ぶ場合に使う
- どちらのウイジェットもオプション text でボタンの名前を指定する
- Radiobutton() は選択する値をオプション value で指定する
- その値を格納するオブジェクトをオプション variable で指定する
- また、オプション command でコールバック関数を指定することもできる
- CheckButton() は値が ON /OFF の 2 通りしかない
- variable には BooleanVar のオブジェクトを指定するといい
リスト : チェックボタンとラジオボタン
import tkinter as tk
root = tk.Tk()
root.option_add('*font', ('', 14))
opts1 = tk.BooleanVar()
opts1.set(True)
opts2 = tk.BooleanVar()
opts2.set(False)
opts3 = tk.BooleanVar()
opts3.set(True)
action = tk.IntVar()
action.set(1)
tk.Label(text ='Check Button').pack()
tk.Checkbutton(text = 'option 1', variable = opts1).pack()
tk.Checkbutton(text = 'option 2', variable = opts2).pack()
tk.Checkbutton(text = 'option 3', variable = opts3).pack()
tk.Label(text ='Radio Button').pack()
tk.Radiobutton(text = 'action A', variable = action, value = 0).pack()
tk.Radiobutton(text = 'action B', variable = action, value = 1).pack()
tk.Radiobutton(text = 'action C', variable = action, value = 2).pack()
root.mainloop()
チェックボタンとラジオボタン
●イメージ
earth.gif の表示
上のキャプチャー画像 (image1.png) の表示
●ファイルの選択
- Tkinter にはファイルを選択するためのモジュール filedialog が用意されている
- Python2 は tkFileDialog だったが、Ptyhon3 からは filedialog になった
- いろいろな関数が用意されているが、次の関数を使うのが簡単
- askopenfilename(), 入力ファイルを選択
- asksaveasfilename(), 出力ファイルを選択
- これらの関数を実行すると、ファイル選択のウィンドウ (ダイアログ) が開かれる
- ウィンドウ上の操作でディレクトリをたどりファイルを選ぶことができる
- 使用できるオプションを示す
- initialdir = ディレクトリ
最初に選択されているディレクトリ
- initialfile = ファイル
最初に選択されているファイル(出力ファイルのみ有効)
- defaultextension = 拡張子
最初に選択されている拡張子
- filetypes = パターン
使用可能なファイル種別と拡張子を指定
- title = 文字列
ダイアログボックスのタイトル
- filetypes はアプリケーションで扱うことができるファイル種別を拡張子で指定し、そのファイルだけを表示する
- 指定はリストとタプルで行う
filetypes = [ファイル種別, ... ]
ファイル種別 := (名前, (拡張子, ...))
たとえば、GIF/PPM ファイルを指定する場合は次のようになる
filetypes = [('Image Files', ('.gif', '.ppm'))]
この場合は GIF と PPM ファイルが一緒に表示される
次のように指定すると、表示するファイルをダイアログの操作で切り替えることができる
filetypes = [('GIF Files', '.gif'),
('PPM Files', '.ppm'),
('ALL Files', '*')]
指定する拡張子がひとつしかない場合はタプルを使う必要はない
すべてのファイルを表示する場合はワイルドカード * を使う
空文字列 '' を指定すると、拡張子のないファイルを表示する
ファイルを選択すると、ファイル名をフルパス形式で返す
選択しない (キャンセルした) 場合は、空文字列が返される
リスト : 画像ローダー
import tkinter as tk
import tkinter.filedialog as fd
import sys, os.path
# ファイルの選択
def load_file():
global image_data, path_name
filename = fd.askopenfilename(filetypes = [('Image Files', ('.gif', '.png', '.ppm')),
('GIF Files', '.gif'),
('PNG Files', '.png'),
('PPM Files', '.ppm')],
initialdir = path_name)
if filename != "":
path_name = os.path.dirname(filename)
image_data = tk.PhotoImage(file = filename)
label.configure(image = image_data)
# トップウィンドウ
root = tk.Tk()
# グローバル変数
path_name = os.getcwd()
image_data = tk.PhotoImage(width = 64, height = 64)
# ラベル
label = tk.Label(root, image = image_data)
label.pack()
# メニュー
m0 = tk.Menu(root);
root.configure(menu = m0);
m1 = tk.Menu(m0, tearoff = 0)
m1.add_command(label = 'Open', under = 0, command = load_file)
m1.add_separator
m1.add_command(label = 'Exit', under = 0, command = sys.exit)
m0.add_cascade(label = 'File', under = 0, menu = m1 )
root.mainloop()
- メニュー File の下に、ファイルを選択する Open とアプリケーションを終了する Exit の 2 つのメニューを設定する
- グローバル変数 path_name は選択されたファイルのパスを格納する
- askopenfilename() にこのパスを指定することで、次にファイルを選ぶときは同じディレクトリから始めることができる
- 初期値はカレントディレクトリ、関数 os.getcwd() で取得する
- アプリケーションの開始時にはファイルは指定されていないので、空のイメージを作って表示しておく
- 画像ファイルのロードは関数 load_file() で行う
- askopenfilename() でファイル名を取得する
- モジュール os.path の関数 dirname() でパスを取り出してグローバル変数 path_name にセットする
- dirname() はファイル名からパス部分を取り出して返す
- ファイル名をゲットしたら、それが空文字列でないことを確認する
- 新しいイメージを PhotoImage() で生成し、ラベルの configure() で表示するイメージを変更する
表示するファイルの選択
GIF ファイルの選択
PNG ファイルの選択