PythonでGUIアプリを作ることになって,Tkinter使ったら早かったので,次使うとき用の備忘録です.
雛形
import Tkinter as Tk
class Application(Tk.Frame):
def __init__(self, master=None):
Tk.Frame.__init__(self, master)
self.pack()
root = Tk.Tk()
app = Application(master=root)
app.mainloop()
ここに色々なWidgetをくっつけていくことになります.
Widget1
名称 | 役割 |
---|---|
Button | ボタン |
Canvas | 画像を貼り付けたり描画したり |
Checkbutton | チェックボタン |
Entry | 1行のテキストエリア.複数行はText |
Frame | 他のWidgetをまとめるフレーム |
Label | ラベル.画像も表示できる |
Listbox | リストボックス |
Menu | メニューバーの作成 |
Message | 複数行のラベル |
OptionMenu | コンボボックス |
Radiobutton | ラジオボタン |
Scale | スライダー |
Scrollbar | スクロールバー |
Text | 複数行のテキストエリア |
Toplevel | ポップアップウィンドウなどを作る用 |
Widgetの配置
Widgetの配置にはpack,place,gridメソッドのいずれかを使います.
packはWidgetを一列,または1行に並べていきます.placeは各Widgetの場所を指定して配置します.gridは2次元座標上にWidgetを配置していきます(Excel方眼紙のようなイメージです).
以降は基本的にgridを使います.
def __init__(self, master=None):
# 省略
self.create_widgets()
def create_widgets(self):
self.label = Tk.Label(self, text=u'入力ファイル')
self.entry = Tk.Entry(self)
self.button = Tk.Button(self, text=u'開く')
self.check = Tk.Checkbutton(self, text=u'拡張子をtxtに限定')
self.text = Tk.Text(self)
self.label.grid(column=0, row=0)
self.entry.grid(column=1, row=0)
self.button.grid(column=2, row=0)
self.check.grid(column=0, row=1)
self.text.grid(column=0, columnspan=3, row=2)
配置の調整と伸縮への対応
はじめにexpand,fill,anchorを設定し,親フレームを伸縮に対応させます.
anchorや後述のstickyで出てくるTk.XXは位置を東西南北(NSEW)で指定できます.
def __init__(self, master=None):
Tk.Frame.__init__(self, master)
self.pack(expand=1, fill=Tk.BOTH, anchor=Tk.NW)
self.create_widgets()
そして各Widgetをgridで配置するときに,stickyでどこを起点にどう伸縮させるかを指定します.
もう一つ大事なのは,gridでWidgetを配置したときには,columnconfigureとrowconfigureでどの行・列がどの比率で伸縮するのかを指定してやる必要があります.
デフォルトではweightパラメータが0(伸縮しない)になっているので,単純には伸縮させたい行・列のweightを1にしてやります.
以下の例ですと,選択したファイル名が入ることを想定しているEntryのある1列目をTextのある2行目のみが伸縮に対応します.
self.label.grid(column=0, row=0, sticky=Tk.W)
self.entry.grid(column=1, row=0, sticky=Tk.EW)
self.button.grid(column=2, row=0, sticky=Tk.E)
self.check.grid(column=0, columnspan=2, row=1, sticky=Tk.W)
self.text.grid(column=0, columnspan=3, row=2, sticky=Tk.NSEW)
self.columnconfigure(1, weight=1)
self.rowconfigure(2, weight=1)
Entryの中身やCheckbuttonのチェック状態の取得
Entryの中身やCheckbuttonのチェック状態は(text)variableオプションに変数を設定しておくことで取得できます.
self.var_entry = Tk.StringVar()
self.entry = Tk.Entry(self, textvariable=self.var_entry)
self.var_check = Tk.BooleanVar()
self.check = Tk.Checkbutton(self, text=u'拡張子をtxtに限定',
variable=self.var_check)
Tk.StringVar()やTk.BooleanVar()の値の設定や取得にはset(),get()を使います.
ボタン押下時のアクション
ボタン押下時に実行するメソッドはcommandオプションで指定します.
def create_widgets(self):
self.var_entry = Tk.StringVar()
self.entry = Tk.Entry(self, textvariable=self.var_entry)
self.button = Tk.Button(self, text=u'開く', command=self.button_pushed)
def button_pushed(self):
self.var_entry.set(u'ボタンが押されました.')
ファイルダイアログ
ファイルダイアログを開くには,tkFileDialogをimportして,askopenfilenameを使います.filetypesオプションを使うことでファイルタイプの指定ができます.
tkFileDialogには他に,askopenfile,askopenfiles,askopenfilenames,asksaveasfile,asksaveasfilename,askdirectoryなどがあります.
import tkFileDialog as tkFD
def button_pushed(self):
ft = [('text files', '.txt')] if self.var_check.get() else []
self.var_entry.set(tkFD.askopenfilename(filetypes=ft))
変数の変化に応じたアクション
変数を監視して変化した時にメソッドを呼ぶには,traceメソッドを使います.
def create_widgets(self):
self.var_entry = Tk.StringVar()
self.var_entry.trace('w', self.entry_changed)
def entry_changed(self, *args):
if os.path.exists(self.var_entry.get()):
self.text.delete('1.0', Tk.END)
self.text.insert('1.0', open(self.var_entry.get()).read())
var_entryの値が書き換えられた時にentry_changedが呼ばれます.
最初にTextの内容を全部消去してから,ファイルの内容を表示します.
スクロールバー
Textにスクロールバーをつけることができます.2
垂直方向のスクロールバーが最初から付いているScrolledTextというWidgetもあるようです.
self.text = Tk.Text(self, wrap=Tk.NONE)
self.yscroll = Tk.Scrollbar(self, command=self.text.yview)
self.xscroll = Tk.Scrollbar(self, command=self.text.xview,
orient=Tk.HORIZONTAL)
self.text['yscrollcommand'] = self.yscroll.set
self.text['xscrollcommand'] = self.xscroll.set
self.text.grid(column=0, columnspan=3, row=2, rowspan=2, sticky=Tk.NSEW)
self.yscroll.grid(column=2, row=2, sticky=Tk.NS + Tk.E)
self.xscroll.grid(column=0, columnspan=3, row=3, sticky=Tk.EW + Tk.S)
画像の貼り付け
Tkinterでは基本的にgifを想定しているようです.一方で例えばmatplotlib(pyplot)のsavefigではgifでの画像保存ができません(よね?).そこで私は以下のように変換してから貼り付けているのですが,もう少しスマートな方法ないでしょうか.
from PIL import Image
def create_widgets(self):
self.canvas = Tk.Canvas(self)
self.canvas.grid(column=0, columnspan=3, row=4, sticky=Tk.NSEW)
def set_image(self):
img = Image.open('foo.png')
img.save('_tmp.gif')
self.image_data = Tk.PhotoImage(file='_tmp.gif')
self.canvas.create_image(200, 100, image=self.image_data)
標準出力のリダイレクト
stackoverflowからのコピペですが...
標準出力の内容をTextなどに出力させることができます.
import sys
def __init__(self, master=None):
Tk.Frame.__init__(self, master)
self.pack(expand=1, fill=Tk.BOTH, anchor=Tk.NW)
self.create_widgets()
sys.stdout = self.StdoutRedirector(self.text)
sys.stderr = self.StderrRedirector(self.text)
class IORedirector(object):
def __init__(self, text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
def write(self, st):
self.text_area.insert(Tk.INSERT, st)
class StderrRedirector(IORedirector):
def write(self, st):
self.text_area.insert(Tk.INSERT, st)
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
Python3対応
Tkinterはtkinter,tkFileDialogはfiledialogに名前が変わっているようです.