リストボックスとスクロールバー

from Tkinter import *
import tkMessageBox, os.path, string

def j(str, encoding="japanese.shift_jis"):
  return unicode(str, encoding).encode("utf8")

class App(Frame):
  def init(self):
    self.master.title("ファイルエントリの検索")
    fa = Frame(self)
    la = Label(fa, text=j("ディレクトリ名:"))
    self.dirNameVar = StringVar()
    self.dirNameVar.set("C:/usr/lang/python")
    ea = Entry(fa, width=40, textvariable=self.dirNameVar)
    ba = Button(fa, text=j("検索"), command=self.search)
    for e in [la, ea, ba]: e.pack(side=LEFT)

    fb = Frame(self)
    # リストボックスとスクロールバー
    self.lsta = Listbox(fb, selectmode="single", width=30, height=6)
    scrv = Scrollbar(fb, orient=VERTICAL, command=self.lsta.yview)
    scrh = Scrollbar(fb, orient=HORIZONTAL, command=self.lsta.xview)
    self.lsta.config(xscrollcommand=scrh.set, yscrollcommand=scrv.set)
    # bindコマンドによるイベントのバインド
    # 登録する関数には「event」オブジェクトの引数が自動的に渡されます。
    self.lsta.bind("", self.listbox_clicked)
    # gridで配置すると簡単です
    self.lsta.grid(row=1, column=1)
    scrv.grid(row=1, column=2, sticky=NS)
    scrh.grid(row=2, column=1, sticky=EW)

    fa.pack(side=TOP, padx=3, pady=3)
    fb.pack(side=LEFT, padx=3, pady=3)

    c = Button(self, text=j("終了"), command=self.quit)
    c.pack(side=LEFT, padx=5, pady=3, anchor=SE)

  def listbox_clicked(self, event):
    # eventオブジェクトの属性で情報が取得できます。
    listbox = event.widget
    indexes = listbox.curselection()
    item = listbox.get(indexes[0])
    tkMessageBox.showinfo(message="selected: " + item)

  def quit(self):
    self.master.destroy()

  def search(self):
    dirname = self.dirNameVar.get()

    if os.path.isdir(dirname):
      # リストボックスをクリアします。
      self.lsta.delete(0, END)
      # ディレクトリに含まれるファイルを追加します。
      for e in os.listdir(dirname):
        self.lsta.insert(END, e)
    else:
      tkMessageBox.showerror(title="Ootto!",
        message="Ootto! " + dirname + " is not a directory.")

  def __init__(self, master=None):
    Frame.__init__(self, master)
    self.pack();    self.init()

if __name__ == "__main__": app = App();  app.mainloop()
# end.

スクリーンショット

 Tkinterのリストボックスの一番のホネは、 スクロールバーとのリンケージです。 これはTcl/Tkのときと同様、決まった書き方があるので、 毎回これを使い回せばよいでしょう。 注意点は、リストボックスとスクロールバーで、 command=オプションやxscrollcommand=オプションの値として互いのインスタンスメソッドを呼び合う形になるので、 下のように片方のインスタンスは2段階の処理を行う必要があります。

self.lsta = Listbox(fa, selectmode="single", width=30, height=6)
scrv = Scrollbar(fa, orient=VERTICAL, command=self.lsta.yview)
scrh = Scrollbar(fa, orient=HORIZONTAL, command=self.lsta.xview)
self.lsta.config(xscrollcommand=scrh.set, yscrollcommand=scrv.set)

 次に、リストボックスのイベント処理ですが、 これにはTcl/Tkでおなじみのbindを使っています。 bindはTkinterでは各ウィジェットのメソッドになっていて、 その引数でイベントの種類と呼び出す関数を指定します。

self.lsta.bind("<Double-Button-1>", self.callbackfunc)

ここで指定する関数には、後ろにイベントに関する情報をもつ Eventクラスのオブジェクトが自動的に渡されるので、 関数の形は次のようになります:

def callbackproc(self, event):
  pass

このEventオブジェクトからは、 イベントの発生した位置や対象のウィジェットなどが取得できます。 主だったものを挙げてみると…
xイベントの発生した位置x(ウィジェットの左上を基準)
yイベントの発生した位置y(ウィジェットの左上を基準)
x_rootイベントの発生した位置x(ディスプレイ左上を基準)
y_rootイベントの発生した位置y(ディスプレイ左上を基準)
widgetイベントを発生させたウィジェット
keycode押されたキーのASCIIコード
keysym押されたキーのX11キーシンボル文字列
これらはそれぞれ、Tcl/Tkのイベントハンドラ内で使用できる置換変数、 %x、 %y、 %X、 %Y…に対応しています。

def callbackproc(self, event):
  widget = event.widget
  widget.config(bg="yellow")


メニュー

 Tkinterで使えるメニューは、 大きく分けて画面上辺に横に並ぶメニューバータイプと、 マウスを右クリックするなどして現れるポップアップタイプのものがあります。 前者はさらに、トップレベル(Toplevel)ウィジェットのmenu=オプションで指定する方法と、 Frameを横長に画面上辺に貼りつけ、これにメニューボタン(Menubutton) ウィジェットを載せていく方法の2つがあります。ここでは後者の方法を紹介しますが、 前者のトップレベルを使う方法も後ろのページで出てきますよ。

from Tkinter import *
import tkMessageBox

def j(str, encoding="japanese.shift_jis"):
  return unicode(str, encoding).encode("utf8")

class App(Frame):
  # 初期化処理
  def init(self):
    self.master.title("メニューとテキスト")
    # メニューボタンに張り付けるタイプのメニュー
    fm = Frame(self, relief=RAISED, bd=2)
    mb1 = Menubutton(fm, text=j("ファイル"))
    mb1.pack(side=LEFT, anchor=W)
    menu1 = Menu(mb1)
    menu1.add_command(label=j("終了"), command=self.quit)
    mb1.config(menu=menu1)
    fm.pack(side=TOP, anchor=W, fill=X)

    # テキスト
    fa = Frame(self, relief=GROOVE)
    ta = self.ta = Text(fa, width=40, height=6)
    scrv = Scrollbar(fa, orient=VERTICAL, command=ta.yview)
    scrh = Scrollbar(fa, orient=HORIZONTAL, command=ta.xview)
    ta.config(xscrollcommand=scrh.set, yscrollcommand=scrv.set)
    ta.bind("<Button-3>", self.rclicked)
    # gridで配置すると簡単です
    ta.grid(row=1, column=1)
    scrv.grid(row=1, column=2, sticky=NS)
    scrh.grid(row=2, column=1, sticky=EW)
    fa.pack(side=TOP, padx=4, pady=4)
    # ポップアップするメニュー
    self.pmenu = Menu()
    self.pmenu.add_command(label=j("プロパティ"), command=self.showProperty)

  def rclicked(self, event):
    # イベントが発生した場所はx_root, y_rootで取得できます。
    self.pmenu.tk_popup(event.x_root, event.y_root)

  # ポップアップメニューの項目「プロパティ」のコールバック関数
  def showProperty(self):
    text = self.ta.get("0.1", END)
    tkMessageBox.showinfo(message=text)

  def 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.

スクリーンショット

 まず、メニューバータイプのメニューですが、

  1. Frameを作り、ウィンドウのウィジェットに載せます。
  2. Menubuttonを作り、Frameに載せます。
  3. Menuを作ります。
  4. Menuにadd_command()などで項目を載せます。
  5. Menubuttonのmenu=オプションでMenuをひもづけます。
という、決まった手順があるので、毎回機械的にこうすればよいと思います。

fm = Frame(self, relief=RAISED, bd=2)
mb1 = Menubutton(fm, text=j("ファイル"))
mb2 = Menubutton(fm, text=j("編集"))
mb3 = Menubutton(fm, text=j("表示"))
for e in (mb1, mb2, mb3):
  e.pack(side=LEFT)
menu1 = Menu(mb1)
menu1.add_command(label=j("新規作成"), command=self.new)
menu1.add_command(label=j("開く..."), command=self.open)
menu1.add_command(label=j("上書き保存"), command=self.save)
mb1.config(menu=menu1)

ポップアップするメニューの場合は、 Menuを作って項目を追加しておき、画面には出しません。 マウスが右クリックされるなどのタイミングで、 tk_popupメソッドを呼ぶことで、 ポップアップメニューが表示されます。

self.pmenu = Menu()
self.pmenu.add_command(label=j("切り取り"), command=self.cut)
self.pmenu.add_command(label=j("コピー"), command=self.copy)
self.pmenu.add_command(label=j("プロパティ"), command=self.showProperty)

セクションのサブメニューに戻る
(first uploaded 2001/08/05 last updated 2002/03/11)


[PR]ベビー用品はたまひよ♪:子育てが楽しくなる便利アイテムいっぱい