コマンドではなく、GUI画面から入力して動かしたくなるケースがあります。
今回、おそらく、もっとも手軽な選択肢「tkinter」の話題です。
はじめに
tkinterは、Python標準のGUIライブラリで、 Tcl/Tk の Python インタフェースです。
GUIは古く感じるデザインです。
なので、これで本番アプリケーションを作ろうとは思いません。
設計が古いせいか、ちょっと癖がありますしね。
でも、「ちょこっと試す」ためのGUIを構築するには便利なライブラリです。
今回は、よく使う以下のWidgetに絞って自分の使い方を紹介しようと思います。
- Entry : 1行テキスト入力(テキストボックス)
- Text : 複数行テキスト入力(テキストエリア)
- OptionMenu : プルダウンメニュー
- CheckButton : チェックボックス
- Button : サブミットボタン
他にもいろいろありますが、正直、ちょっと試す程度にしか使わないので、今のところ使ってません。(笑)
必要になったら、こちらのドキュメントとかを参考にして追加しますけどね。
tkinterで自分がめんどくさいと思うこと
tkinterを素で使うと、レイアウトがめんどくさいです。
GUIの設計画面があるわけもありませんし、なにより、配置する座標をX・Yの座標で指定してやって、表示して微調整・・が面倒です。
自分的には、ちゃちゃっと試すのに使いたいだけなので、こんなことに神経をつかいたくはありません。
ということで。
汎用性を犠牲にして、部品を1行ずつペタペタ書いていけば、それなりの画面になるっていうように使い方をするための、こんな感じのクラスを作ってます。
gui_templete.py
import tkinter as tk from tkinter import messagebox class Gui: def __init__(self, title='Demo window'): self.window_width = 640 self.window_height = 480 self.left_space = 20 self.right_space = 30 self.top_space = 20 self.row_span = 50 self.col_span = 10 self.label_width = 80 self.x = self.left_space self.y = self.top_space self.root = tk.Tk() self.root.title(title) self.root.geometry(str(self.window_width) + "x" + str(self.window_height)) self.parts_list = [] def text_box(self, label): x = self.x y = self.y w = self.label_width pw = 80 lb = tk.Label(text=label) lb.place(x=x, y=y) box = tk.Entry(width=pw) box.place(x=x + w + self.col_span, y=y) self.y = self.y + self.row_span return box def text_area(self, label): x = self.x y = self.y w = self.label_width pw = 68 lb = tk.Label(text=label) lb.place(x=x, y=y) box = tk.Text(width=pw, height=6) box.place(x=x + w + self.col_span, y=y) self.y = self.y + self.row_span + 50 return box def check_box(self, label, var): x = self.x y = self.y w = self.label_width box = tk.Checkbutton(text=label, variable=var) box.place(x=x + w + self.col_span, y=y) self.y = self.y + self.row_span return box def combo_box(self, label, values, select): x = self.x y = self.y w = self.label_width lb = tk.Label(text=label) lb.place(x=x, y=y) optionList = values variable = tk.StringVar(self.root) variable.set(optionList[0]) box = tk.OptionMenu(self.root, variable, *optionList, command=select) box.place(x=x + w + self.col_span, y=y) self.y = self.y + self.row_span return box def button(self, label, command): button = tk.Button(text=label, command=command) button.place(x=self.x, y=self.y) self.y = self.y + self.row_span return button def show(self): self.root.mainloop() def msgbox(self, msg1, msg2): messagebox.showinfo(msg1, msg2) def textindex1(self): return '1.0' def textindex2(self): return 'end -1c'
簡単に保続します。
Windowsサイズとか、左右トップの各マージンとかは固定にしてます。
self,xとself.yに「次に描画する部品の座標」を保持するように、部品の描画後に更新するようにしています。
あと、複数行テキストの「Text」の場合、1行目から最終行までをとりだす場合の指定の仕方がちょっと独特です。
text.get('1.0','end-1c')
なんともはや。
こんな書き方・・たいてい使うときには忘れてます。
なので、それを「textindex1」「textindex2」メソッドで指定できるようにしました。
あと。
optionMenuを使うプルダウンリスト(combo_boxという名前にしてますが(;^_^A)の表示データの渡し方で、「*optionList」みたいにポインタ渡しが必要だったりするところとか。
checkbuttonもそうですが、価の取得のためのコールバック関数名を渡してやらないといけないところとか・・。
特徴的な部分で、わかりづらいのは・・こんなもんですかね。
このクラスを使うサンプルです
パーツをぺたぺたのひとつずつ配置し、ボタンを押したときに入力値を取得して、メッセージボックスに表示してみます。
ソースコードはこんな感じです。
import gui_templete as gt import tkinter as tk def button_click(): s = t1.get() + "\n" + selected + "\n" + str(chkValue.get()) + \ "\n" + t4.get(wdw.textindex1(), wdw.textindex2()) wdw.msgbox("クリックイベント", s) def select(value): global selected selected = value wdw = gt.Gui() t1 = wdw.text_box(label='入力部品1') oplist = [ "選択するもの1", "選択するもの2" ] selected = oplist[0] chkValue = tk.BooleanVar() chkValue.set(True) t2 = wdw.combo_box(label='入力部品2', values=oplist, select=select) t3 = wdw.check_box(label='入力部品3', var=chkValue) t4 = wdw.text_area(label='入力部品4') b1 = wdw.button(label='実行ボタン', command=button_click) wdw.show()
各部品をひとつずつ配置してます。
実行すると、以下のような画面を表示します。
適当に入力したり、選択しなおしてみます。
実行ボタンを押してみます。
入力値は、取得できているみたいです。
データの取得について補足です
ikinterのwidgetでデータを取得する方法を簡単にまとめておきます。
まず、1行入力のテキストボックス(Entry)は簡単です。
t1.get()
のように、get()を呼ぶだけです。
これが複数行テキスト入力(Text)になると、get()の引数に、開始行(index1)と終了行(index2)の指定が必要なのは、前に書きましたので飛ばします。
プルダウンリスト(OptionMenu)は、状態が変化したときに呼び出されるコールバック関数を用意して、その中でglobal変数に値をセットして、それを参照するみたいなことをする必要があります。
上記のソースなら、この部分です。
def select(value): global selected selected = value
なのでボタンクリックで参照しているのは、selectedなのです。
チェックボックス(CheckButton)の場合は、状態をセットする参照用の変数を定義してやる必要があります。
それがこの部分です。
chkValue = tk.BooleanVar() chkValue.set(True) t3 = wdw.check_box(label='入力部品3', var=chkValue)
この場合だと、チェックボックスが選択されたり、解除されたりしたときに、chkValueの値が変化します。
なので。
chkValue.get()
でとりだせているわけです。
この変数定義しないといけないところが、クラスのインタフェースとしてはイマイチだなあ・・と思ってはいますが、まあいいかな・・と放置(笑)してます。
かなり、いい加減なクラスですが、自分の用途(ちょっとお試しで画面を使いたいときの使い捨て画面作成)には十分かなとわりきってます。
今回はこんなところで。
ではでは。