前回の記事では、Tkinter を使う上で必要となる前提知識(考え方、階層構造、配置、イベント処理など)について解説しました。
今回は、実際のサンプルソースを使って、個々のコントロール(=ウィジェット)についてもう少し細かいレベルで解説致します。
今回作成したGUIのデモ画面
前回の記事 と同じ画面ですが、メニューからフォルダやファイルを選択するダイアログを表示するようにしています。
今回のデモプログラムで使っているコントロールは次の通りです。
コントロール名 | 内容 |
---|---|
Label | ラベル |
Entry | テキストボックス(1行) |
Text | テキストボックス(複数行) |
ScrolledText | テキストボックス(複数行、スクロールバー付) |
Button | ボタン |
Checkbutton | チェックボックス |
Combobox | コンボボックス |
Scale | スライダー |
Progressbar | プログレスバー |
Spinbox | スピンボックス |
Listbox | リストボックス |
Canvas | キャンバス |
Menu | メニュー |
デモ画面のソースコード
以下は今回のデモ画面のソースコードです。
個々のコントロールについて、必要最小限の機能のみ使うようにしています。
必要に応じて Tkinter公式サイト から情報を入手して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
import tkinter.ttk as ttk import tkinter as tk import tkinter.scrolledtext as st import PIL.ImageTk as itk import tkinter.filedialog as dlg #Windowの生成 root = tk.Tk() root.title("Software Title") root.geometry("600x220") #ラベルの生成 label = tk.Label(text='名前') label.grid(row=0,column=0) #テキストボックス(1行)の生成 textbox = tk.Entry(width=20,foreground='black',background='#7fffd4') textbox.insert(tk.END,"山田太郎") textbox.grid(row=0,column=1) #テキストボックス(複数行)の生成 text = tk.Text(width=20,height=10) text.insert(tk.END,"1234567890") text.grid(row=2,column=0,columnspan=2) #テキストボックス(複数行、スクロールバー付)の生成 textfield = st.ScrolledText(width=20,height=10) textfield.insert(tk.END,"\n".join([str(x) + '行' for x in range(15)])) textfield.configure(state='normal') textfield.grid(row=2,column=2,columnspan=2) #ボタンから呼び出されるイベントハンドラの定義 def myfunc(event,msg): #テキストボックスの中身を置き換える textbox.delete(0,tk.END) textbox.insert(0,msg) #ボタンの生成 button = tk.Button(text='ボタン',width=10) button.bind('<Button-1>',lambda e : myfunc(e,'Button Click')) button.grid(row=0,column=2) #チェックボックスにセットする初期値の作成 val = tk.BooleanVar() val.set(True) #チェックボックスの生成 chkbox = tk.Checkbutton(text='チェック', variable=val,command=lambda : myfunc(None,val.get())) chkbox.grid(row=0,column=3) #コンボボックスの生成 combobox = ttk.Combobox(textvariable=tk.StringVar(),values=['項目1','項目2','項目3'], width=18) combobox.set('項目1') combobox.bind("<<ComboboxSelected>>",lambda e : myfunc(e,combobox.get())) combobox.grid(row=0,column=5,padx=5) #スライダーの生成 scale = tk.Scale(from_=0,to=100,variable=tk.DoubleVar(),orient=tk.HORIZONTAL,command=lambda e : myfunc(e,scale.get()),width=10,length=160) scale.grid(row=1,column=0,columnspan=2) #プログレスバーの生成 pval = tk.IntVar(value=0) progress = ttk.Progressbar(orient=tk.HORIZONTAL,variable=pval,maximum=10,length=160, mode='determinate') progress.grid(row=1, column=2,columnspan=2) #スピンボックスの生成 spinbox = ttk.Spinbox(from_=0.0, to=9.0, textvariable=tk.StringVar(),width=10,command=lambda :pval.set(pval.get() + 1)) spinbox.set(0) spinbox.grid(row=0,column=6) #リストボックスの生成 listbox = tk.Listbox(listvariable=tk.StringVar(value=['リスト1','リスト2','リスト3']), selectmode='browse',width=20,height=8) listbox.bind('<<ListboxSelect>>', lambda e : myfunc(e,listbox.curselection())) listbox.grid(row=2,column=4,columnspan=2) #キャンバスの生成 img = itk.PhotoImage(file = 'p:/dog.png') canvas = tk.Canvas(width=100,height=180,background='red') canvas.create_image(0,0,image=img,anchor=tk.NW) canvas.create_rectangle(10, 80, 90, 150, fill = 'red', stipple = 'gray25') canvas.grid(row=1,column=6,rowspan=2) #メニューの生成 mymenu = tk.Menu(root) filemenu = tk.Menu(mymenu, tearoff=0) filemenu.add_command(label='フォルダ選択', command=lambda : dlg.askdirectory(initialdir='P:/')) filemenu.add_command(label='ファイル選択', command=lambda : dlg.askopenfilename(filetypes=[("", "*")], initialdir='P:/')) filemenu.add_command(label='ファイルを保存', command=lambda :dlg.asksaveasfilename(filetypes=[("", "*")], initialdir='P:/')) filemenu.add_separator() filemenu.add_command(label='終了', command=quit) mymenu.add_cascade(label='ファイル', menu=filemenu) root.config(menu=mymenu) root.mainloop() |
ソースコードの解説
プログラムはいくつものブロックに分かれていますので、プロックごとに解説していきます。
インポートについて
今回は下記5行のimport を行っています。
このうち、PIL.ImageTk は canvas に表示するイメージファイルを読み出すために使っており、tkinter.filedialog は ファイルやフォルダ選択ダイアログを表示するために使っています。
これらが不要な場合は import の必要はありません。
1 2 3 4 5 |
import tkinter.ttk as ttk import tkinter as tk import tkinter.scrolledtext as st import PIL.ImageTk as itk import tkinter.filedialog |
ルートwindowsの生成
ここでは、Tkのインスタンス(オブジェクト)を生成し、WindowタイトルとWindowサイズを指定しています。
1 2 3 4 |
#Windowの生成 root = tk.Tk() root.title("Software Title") root.geometry("600x220") |
ラベルの生成
ラベルに限らず、多くのコントロールは widthで横幅、height で縦幅、 foreground で文字色、 backgroundで背景色が指定可能です。
ただ、withとheight はコントロールによってドット単位か文字単位に分かれており、ラベルの場合は文字単位になります。
1 2 |
#ラベルの生成 label = tk.Label(text='名前') |
テキストボックス(1行)の生成
テキストボックスのwidth、height は文字単位でサイズの指定を行います。
width=20 になっているので、この場合は半角20文字分のテキストボックスが生成されます。
1 2 3 |
#テキストボックス(1行)の生成 textbox = tk.Entry(width=20,foreground='black',background='#7fffd4') textbox.insert(tk.END,"山田太郎") |
テキストボックスに初期値を設定する場合、insertメソッドを使います。
第一引数はインサート位置になるのですが、tk.ENDと指定することで、文字列の末尾にインサートが行われます。
一見、tk.ENDの代わりに0を指定しても良い気がしますが、テキストボックス生成時は文字列が空なので、0だとインデックスオーバーフローのエラーが発生します。
tk.ENDを指定すると、空でも適切に処理してくれます。
ちなみに、テキストボックスの中身を空にしたい場合、deleteメソッドに文字列の開始と終了位置を指定し、その区間を削除するという方法を使います。
詳細は後述する「イベントハンドラの定義」をご覧ください。
テキストボックス(複数行)
複数行の入力が可能なテキストボックスですが、こちらは行が増えてもスクロールバーが表示されません。
スクロールバーを表示したい場合、ScrolledText を使います。
1 2 3 |
#テキストボックス(複数行)の生成 text = tk.Text(width=20,height=10) text.insert(tk.END,"1234567890") |
テキストボックス(複数行、スクロールバー付)
スクロールバー付き複数行入力可能なテキストボックスです。
configure メソッドの引数に state='disabled' とすると、書き込み禁止になります。
1 2 3 4 |
#テキストボックス(複数行、スクロールバー付)の生成 textfield = st.ScrolledText(width=20,height=10) textfield.insert(tk.END,"\n".join([str(x) + '行' for x in range(15)])) textfield.configure(state='normal') |
イベントハンドラの定義
こちらはデモプログラムの中で使うための汎用的なイベントハンドラです。
テキストボックスの中身を置き換えるには、delete と insert を使ってこのように記述します。
本当はイベントハンドラをソースコードの冒頭に記述したかったのですが、textbox を参照しているため、やむなくこの位置に記述しています。
1 2 3 4 |
def myfunc(event,msg): #テキストボックスの中身を置き換える textbox.delete(0,tk.END) textbox.insert(0,msg) |
ボタン
tk.Button のcommand 引数にイベントハンドラを指定する方が簡単なのですが、説明のために bind で指定してみました。
bind の第一引数にはいろいろなイベントが指定できますが、ここではマウスの左ボタン(<Button-1>)を指定しています。
他にも、<Button-2> や <Button-3> があり、それぞれマウスホィールボタンのクリック、右クリックに対応しています。
今回はイベントハンドラに 'Button Click' という文字列を渡したいのでラムダ式を使っていますが、その必要が無い場合は button.bind('<Button-1>',myfunc) と記述します。
1 2 3 |
#ボタンの生成 button = tk.Button(text='ボタン',width=10) button.bind('<Button-1>',lambda e : myfunc(e,'Button Click')) |
チェックボックス
チェックボックスに対して値を設定したり読み出したりする場合、 BooleanVar メソッドにてオブジェクトを作成し、これを使う必要があります。
チェックボックスからのイベントハンドラ呼び出し時、イベントハンドラにイベント引数は渡されません。
そこで、イベント引数の場所に None を指定し、myfunc(None,・・・)と記述しています。
チェックボックスから値を取り出したい場合は、 val.get() と記述します。
1 2 3 4 5 6 |
#チェックボックスにセットする初期値の作成 val = tk.BooleanVar() val.set(True) #チェックボックスの生成 chkbox = tk.Checkbutton(text='チェック', variable=val,command=lambda : myfunc(None,val.get())) |
コンボボックス
コンボボックスのテキスト部に値を設定する場合は、setメソッドを使い、選択肢を設定する場合は values 引数を使います。
Combobox の command 引数で指定したイベントハンドラは、選択肢が選択されたタイミングで呼び出されます。
bind で同じことをする場合、イベント名に <<ComboboxSelected>> と記述します。
コンボボックスから選択された値を取得する場合は、 combobox.get() を使います。
1 2 3 4 |
#コンボボックスの生成 combobox = ttk.Combobox(textvariable=tk.StringVar(),values=['項目1','項目2','項目3'], width=18) combobox.set('項目1') combobox.bind("<<ComboboxSelected>>",lambda e : myfunc(e,combobox.get())) |
スライダーの生成
スライダーの orient 引数に tk.HORIZONTAL を指定すると横方向に、tk. VERTICAL を指定すると縦方向にスライダーが表示されます。
スライダーの幅と太さは、それぞれ widthと length で指定しますが、この単位はドットです。
スライダーから値を取得する場合は、 scale.get() で取得可能です。
イベントハンドラについては、bind で指定可能且つ適切なイベント名が見つかりませんでしたので、素直にcommand 引数で指定しています。
スライダーに初期値を設定したい場合、tk.DoubleVar(value=初期値) と記述します。
1 2 |
#スライダーの生成 scale = tk.Scale(from_=0,to=100,variable=tk.DoubleVar(),orient=tk.HORIZONTAL,command=lambda e : myfunc(e,scale.get()),width=10,length=160) |
プログレスバー
プログレスバーの値を保持させるため、pval = tk.IntVar(value=0) でオブジェクトを生成し、それを使っています。
ttk.Progressbar( ・・・ ,variable= tk.IntVar(value=0) ,・・・) でも構わないのですが、スピンボックスのイベント処理にて値を増加させたかったので、pval という変数を使っています。
プログレスバーの値を設定する場合は、 pval.set(値) を使い、取得する場合は pval.get() を使います。
1 2 3 |
#プログレスバーの生成 pval = tk.IntVar(value=0) progress = ttk.Progressbar(orient=tk.HORIZONTAL,variable=pval,maximum=10,length=160, mode='determinate') |
スピンボックス
スピンボックスの値を増加させると、プログレスバーが進むようにしたかったので、 スピンボックスを1回クリックする度に、pval.set(pval.get() + 1) で値を増加させています。
スピンボックスの初期値は、spinbox.set() メソッドを使います。
1 2 3 |
#スピンボックスの生成 spinbox = ttk.Spinbox(from_=0.0, to=9.0, textvariable=tk.StringVar(),width=10,command=lambda :pval.set(pval.get() + 1)) spinbox.set(0) |
リストボックス
リストボックスの選択肢は value引数にリスト形式で値を渡します。
ここでは bind でイベントを指定していますが、選択肢が選択(クリック)された時のイベント名は <<ListboxSelect>> となります。
リストボックスが選択されている位置(反転している位置)は listbox.curselection() で取得可能で、0から始まる数値で返されます。
1 2 3 |
#リストボックスの生成 listbox = tk.Listbox(listvariable=tk.StringVar(value=['リスト1','リスト2','リスト3']), selectmode='browse',width=20,height=8) listbox.bind('<<ListboxSelect>>', lambda e : myfunc(e,listbox.curselection())) |
キャンバス
キャンバスは、線、円、四角形、多角形などの図形を描画したり、イメージ画像を表示することが出来ます。
今回のデモでは、Pドライブにある dog.png というファイルを表示するとともに、 create_rectangle で四角形も表示しています。
イメージを取得するために、PIL.ImageTk に存在する PhotoImage を使っていますが、これは
PGM,PPM,GIF,PNG の4種類のフォーマットしかサポートしていませんのでご注意ください。
1 2 3 4 5 |
#キャンバスの生成 img = itk.PhotoImage(file = 'p:/dog.png') canvas = tk.Canvas(width=100,height=180,background='red') canvas.create_image(0,0,image=img,anchor=tk.NW) canvas.create_rectangle(10, 80, 90, 150, fill = 'red', stipple = 'gray25') |
メニュー
サンプルソースでは各種ダイアログを表示するため、ごちゃごちゃとなっていますが、その部分を省くと以下の様にシンプルになります。
Menuのオブジェクトに対して、子供となるMenuを追加しているだけです。
1 2 3 4 5 6 7 8 9 10 |
#メニューの生成 mymenu = tk.Menu(root) filemenu = tk.Menu(mymenu, tearoff=0) filemenu.add_command(label='フォルダ選択', command=イベントハンドラ) filemenu.add_command(label='ファイル選択', command=イベントハンドラ) filemenu.add_command(label='ファイルを保存', command=イベントハンドラ) filemenu.add_separator() filemenu.add_command(label='終了', command=quit) mymenu.add_cascade(label='ファイル', menu=filemenu) root.config(menu=mymenu) |
ファイル・フォルダ選択ダイアログ
今回使用したダイアログは3種類ですが、いずれの場合も戻り値として選択された結果(フォルダ又はファイル名)が返されます。
initialdir は選択先フォルダの初期値を指定します。
filetypes は[('CSVファイル','*.csv'),('TSVファイル','*.tsv),・・・] という風に表示名とフィルタをタプルで括って、リスト形式に纏めて指定します。
1 2 3 4 5 6 |
#フォルダ選択ダイアログ dlg.askdirectory(initialdir='P:/')) #ファイル選択ダイアログ dlg.askopenfilename(filetypes=[("", "*")], initialdir='P:/')) #ファイル保存ダイアログ dlg.asksaveasfilename(filetypes=[("", "*")], initialdir='P:/')) |
まとめ
今回は良く使われそうなコントロールを使ったデモプログラムのソースコードと、それぞれのコントロールに関する簡単な説明を行いました。
それぞれのコントロールについても、最低限必要となる設定項目や、各コントロールが保持する値の設定と取得、イベント発生時の処理方法について触れましたので、この記事の情報を駆使するだけでも、そこそこの画面が作れるのではないかと思います。
PythonでGUIを作る際の参考になれば幸いです。
コメント