
| ■ | Python/Tkinterのとっつきぶぶん |
●クラスを使わない方法
ではまず、ダメなほう、じゃなかった、うーん、なんと申しましょうか、
クラスを使わないスクリプト言語としてのPythonに寄った書きかたで、
「Hello, World」という文字と押すとプログラムが終了するボタンのついたウィンドウを出す簡単なアプリケーションを書いてみます。
from Tkinter import *
import sys
def main():
root = Tk() # Tkのルートウィンドウ
# ウィジェットを作って
laba = Label(text="Hello, World",bg="white",fg="red")
cmda = Button(text="Bye")
# 載せます
laba.pack(side="top")
cmda.pack(side="top")
cmda.bind("<ButtonRelease-1>", clicked)
root.mainloop() # イベントループに入ります
def clicked(event):
sys.exit(0)
if __name__ == "__main__":
main()
# end.
|
この書き方に関しては、当サイトでは以後二度と出てきませんので、 「一期一会」という言葉を胸に、今生の別れをしておきましょう。 ただし、これだけのサンプルでも、幾つかの事柄が紹介できます。
むー、これだけのサンプルではこれだけですか。 あと幾つか、気になる部分もあると思いますが、それらは後述の「クラスを使う」 サンプル以降では本当に必要ないので、おっぽっといてしまいましょう。
●クラスを使う方法、超基本
さて、さっそくその「クラスを使う」、もっと説明的に言いますと、
オブジェクト指向言語としてのPythonに寄った書き方で、
プログラム全体をクラスとし、
アプリケーションの起動をそのクラスのインスタンス化とするものです。
Java AWT/SwingやQtは100%この書き方で、
PythonがそれらのGUIツールキットの先駆け?と思うほどよく似ています。
Pythonの文法なんか全然知らねーよ、という方でも、
Java AWT/SwingやQtの経験があれば、なんとなく分かります…よね?
# これは決まり文句です。
from Tkinter import *
# Tkinterの標準クラスFrameを継承します。
# Javaのアプレットと同じ作り方ですね
class App(Frame):
# ウィジェットを載せていきます。
# Tcl/Tkと同じ用語なので対応を覚えるとわかりやすいはずです
def init(self):
b = Button(self, text="Hello, World", command=self.cmd_clicked)
b.pack(side="top")
# ボタンが押されたときの処理を書きます
def cmd_clicked(self):
print "Hello"
self.master.destroy()
# ここから後も定石ということで
def __init__(self, master=None):
Frame.__init__(self, master)
self.init()
self.pack()
app = App()
app.mainloop() # イベントループに入ります。
# end.
|
__init__ の中の記述はもう定石ということで毎回使い回してOKです。 要点は何箇所かありますが、
b = Button(self, text="Hello, World", command=self.cmd_clicked) |
b['text'] = "Hello, World" b['font'] = "maru16" |
b.config(text="Hello, World", command=self.cmd_clicked) |
ところで、
b = Button(self, text="Hello, World", command=self.cmd_clicked) |
もう1点、Buttonウィジェットのcommand=に関する話です。 これもTcl/Tkから移られた人(というか私)には混乱するところでした。 もともと、Tcl/Tkのcommandオプションには好きな形のTclコマンドを指定することができました。 セミコロン「;」で区切ったり、大かっこ「{ }」で囲むことで複数のコマンドを書くこともできましたし、 「-command "puts {Hello, World}"」のように、任意の引数(オプション)をつけることもできました。 しかし! Pythonでは、command=に指定できるのはPython命令の文字列ではなく、 関数の名前(もっと厳密に言うと、関数オブジェクトへの参照) だ、という点に注意されないといけません。ですから、指定できる関数は1つだけ、その形も 上の「def cmd_clicked(self):」にある通り、selfを除けば 引数を全く持たない形にしなくてはいけません。 ここはTcl/Tkを知らなければなんでもないのですが、 Tclerの皆さんは最初混乱するはずなので、十分注意してくださいね。
●簡単なサンプル、および
では、もう少しだけ違うサンプルを作ってみます。
from Tkinter import *
class App(Frame):
def init(self):
a = Label(self, text="Hello, World",bg="white",fg="red")
b = Button(self, text="Bye", command=self.cmd_clicked)
a.pack(side=TOP)
b.pack(side=TOP)
def cmd_clicked(self):
self.master.destroy()
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.init()
if __name__ == "__main__":
app = App()
app.mainloop()
# end.
|
ちょっとメモ(1) - プログラムを終了する2つめの方法
Tkinterのプログラムを終了するには、
もちろんPythonのsys.exit()関数を使ってもいいのですが、
その他にはTkのルートウィンドウをdestroy()メソッドで吹き飛ばす、
という方法があり、このサイトでもそちらを使っています。
self.master.destroy()
ちょっとメモ(2) - ウィジェット変数
えーと、Tcl/Tkと違って、各ウィジェットには「名前」はありません。
「Label」「Entry」などを使ってウィジェットを作ったときに返される値
(=オブジェクト)をPythonの変数に格納して保持します。
このへんはC言語的というか、Java言語的というか、
ポインタチックな扱いになっているので、つまり、
self.a = Label(self, text="Hello")
self.b = self.a
self.b['text'] = "Hello, again"
のように変数間で代入したりができます。
で、ラベルのようにあとあとウィジェットを参照することがまったくないことがはっきりしているウィジェットの場合は、
a = Label(self, "Hello")
このようにローカル変数「使い捨て」でも構いません。
しかし、エントリのようにあとあとになって入力された内容を知りたいなどの理由で参照する必要があるウィジェットの場合、必ず
self.a = Entry(self)
このようにインスタンス変数にして保持するようにします。
Pythonの場合インスタンス変数も宣言せずにがんがん作れるので、
面倒なら上のように全部 self. をつけるくせにしてもいいかもしれません。
ちょっとメモ(3) - 記号定数
Tkinterでは、ウィジェットを載せるpackメソッドで位置を指定する際に、
a.pack(side='left', anchor='e')
このように文字列を使いますが、
これらに対応する記号定数も別途定義されているので、
それらを使えば、
a.pack(side=LEFT, anchor=E)
このように書くこともできます。
from Tkinter import *
class App(Frame):
def init(self):
self.master.title("Hello!")
f = Frame(self, relief=GROOVE, bd=3)
b1 = Button(f, text="Button1", command=self.cmd1)
b2 = Button(f, text="Button2", command=self.cmd2)
b3 = Button(self, text="Bye", command=self.cmd_quit)
for e in [b1, b2]: e.pack(side=LEFT, padx=5)
for e in [f, b3]: e.pack(side=TOP, padx=5, pady=5)
def cmd1(self):
print "Button 1"
def cmd2(self):
print "Button 2"
def cmd_quit(self):
self.master.destroy()
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.init()
if __name__ == "__main__":
app = App()
app.mainloop()
# end.
|
fa = Frame(self) laba = Label(fa, text = 'Value 1:') txfa = Entry(fa, width=10)これすなわち、ラベル laba とエントリ txfa がフレーム fa の上に載るという親子関係ができるわけで、こう書けばあとは
for g in [laba, txfa]:
g.pack(side = 'left')
こんな感じで実際に載せていくだけです。
なお、この親ウィジェットへの参照を省略するとTkのルートウィンドウが親となりますが、
このサイトで紹介している書き方では全てのウィジェットは
「class App(Frame):」とある通り、ルートウィンドウに乗せたFrameが始祖となります。
従って、親ウィジェットへの参照を省略すると、
pack順序が変になったりするおかしなバグの元になるので、必ず指定が必要です。
セクションのサブメニューに戻る
(first uploaded 2000/04/08 last updated 2002/03/11)