memopy

pythonで作ってみました的なブログ

python tkinter クリックされたウィジェットのテキストや属性を取得する

python tkinter クリックされたウィジェットを取得する

f:id:memopy:20170611175835p:plain
前回の記事は、ボタンがクリックされたとき、commandオプションにコールバック関数を定義して、どのボタンがクリックされたのか調べる方法を紹介した。
python tkinter どのボタンが押されたか判定する方法 - memopy
今回は、bindメソッドを使用して、前回よりもっと幅広く、ボタンに限らず全てのウィジェットに対して、それらがクリックされたときに、そのテキストや色などの値を取得する方法を解説する。
参考サイト(英語)
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/events.html

bindメソッドを使用して、どのボタンがクリックされたか判定する

※python3で作成(python2で使用する場合は、モジュール名をTkinterに変更する)

# -*- coding:utf-8 -*-
import tkinter as tk
root = tk.Tk()

# ウィジェットがクリックされたときのイベントを定義
def callback(event):
    print(event.widget["text"])

for i in range(5):
    # ボタンの定義
    button = tk.Button(root,text="ボタン"+str(i))
    # ウィジェットが左クリックされたときの関数を定義
    button.bind("<1>",callback)
    # ボタンの配置
    button.pack(fill="x")

root.mainloop()

f:id:memopy:20170613200953p:plain
ボタンをクリックすると、textオプションで定義したボタンのテキストが出力された!

クリックやキー入力によってイベントを処理をするbindメソッド

ユーザがGUIに対して行ったクリックやキー入力操作をトリガーにして、何らかの処理を行うよう定義するのがbindメソッドである。
commandオプションは、Buttonウィジェットなど一部のものにしか定義できないが、bindメソッドは全てのウィジェットに対して定義できるのも特徴だ。

# 構文
widget.bind(sequence=None, func=None, add=None)
sequence

イベントのトリガーとなる操作を定義する。
(例)

sequence 説明
<Button-1> 又は
<1>
左クリック
<Button-2> 又は
<2>
ホイールクリック
<Button-3> 又は
<3>
右クリック
<KeyPress-H> 又は
h
hキー入力
<Control-Shift-KeyPress-H> Ctrl + Shift + h 入力

※キー操作によりイベントを処理するためには、ウィジェットにforcusが必要

func

イベントを処理する関数名を定義

def callback(event):
    pass
widget.bind("<1>",callback)

実際に、イベントが実行されると、イベントオブジェクトを引数として関数が実行される。
そのため、イベントを処理する関数の第1引数には、イベントオブジェクトを受けなければならない。

add

複数のイベントを定義するオプション。通常、同一のトリガー(sequence)に対して複数のイベント(func)を定義すると、最後に定義したイベント(func)に上書きされるが、addオプションを"+"にすると、複数のイベントを定義できる。

def callback(event):
    pass
def another(event):
    pass
widget.bind("<1>",callback)
widget.bind("<1>",another,"+")

event オブジェクトとは

bindメソッドによってイベントとして定義した関数が呼び出されると、イベントオブジェクトも一緒に渡される。

def callback(event):
    pass

このeventオブジェクトも様々な属性を持っている。
今回ここで紹介したいのは、イベントの原因となったウィジェットを取得する event.widget である。

def callback(event):
    print(event.widget["text"])

このように定義すれば、tk.Button["text"]と同様、原因となったウィジェットのtextの値を取得できる。
これを実装し、押されたボタンの判定をしたのが、冒頭のスクリプト全文となる。

クリックされたウィジェットの色を変更する例
# -*- coding:utf-8 -*-
import tkinter as tk
root = tk.Tk()

# ウィジェットがクリックされたときのイベントを定義
def callback(event):
    # ボタンの背景色がデフォルト値だったら赤に変更し、
    if event.widget["bg"] == "SystemButtonFace":
        event.widget["bg"] = "red"
    # 赤色になっていたら、元に戻す。
    else:
        event.widget["bg"] = "SystemButtonFace"

for i in range(5):
    button = tk.Button(root,text="ボタン"+str(i))
    button.bind("<1>",callback)
    button.pack(fill="x")

root.mainloop()

f:id:memopy:20170613212548p:plain
ボタンを押すと、赤くなり、もう一度押すと元に戻るスクリプトができた。

フレーム内にある全てのウィジェットに対してイベントを定義する bind_all メソッド

bindメソッドは、イベントを定義したいウィジェットに対して個別に定義した。
しかし、フレーム内にある全てのウィジェットに対して同一のイベントを定義したい場合には、bind_allメソッドを使うことができる。

# -*- coding:utf-8 -*-
import tkinter as tk
root = tk.Tk()

# ウィジェットがクリックされたときのイベントを定義
def callback(event):
    # ボタンの背景色がデフォルト値だったら赤に変更し、
    if event.widget["bg"] == "SystemButtonFace":
        event.widget["bg"] = "red"
    # 赤色になっていたら、元に戻す。
    else:
        event.widget["bg"] = "SystemButtonFace"

# ラベルの定義
label = tk.Label(root,text="これはラベルだけど色が変わるよ",font=("",14))
label.pack()
# ボタンの定義
for i in range(5):
    button = tk.Button(root,text="ボタン"+str(i))
    button.pack(fill="x")
# ラベルの定義
label2 = tk.Label(root,text="どこを押しても色が変わるよ")
label2.pack()

# ルートフレームに対してイベントの処理を定義
root.bind_all("<1>",callback)

root.mainloop()

注:今回はルートフレーム(root)に対して、bind_allメソッドを設定したが、実際はどのウィジェットに設定してもよい。
(ただし、全体に対する設定なので、rootに対して設定するのが一般的ではないだろうか)
f:id:memopy:20170613213846p:plain
全てのウィジェットに対して、イベントの処理を実装することができた。


今回紹介したのはイベント処理のほんの一例であり、まだまだ奥は深い。
また、イベントについて掘り下げて紹介したいと思う。