この記事ではCanvasの図形や画像をtag_bind()
を利用してドラッグ・アンド・ドロップで動かすということをやってみます。
tkinterのアプリケーションはmainloopメソッドを実行することにより、イベントループの状態になります。イベントループの状態になっているときにイベントを発生させることにより、様々な用途のアプリケーションを開発することができます。
イベントを発生させるには例えば、キーボードを押したりマウスをクリック、ドラッグしたときなどです。今回やることはマウス操作で図形や画像を動かすアプリケーションを作ることです。これを応用すればパズルゲームなどができるようになると思います。
関連リンク
Events and Bindings
長方形と画像をCanvas上でドラッグ・アンド・ドロップで動かす
今回はCanvas上に作成した長方形と画像をドラッグ・アンド・ドロップで動かしてみます。流れとしては以下のようになります。
- 長方形と画像を作る。
- 作ったオブジェクトとイベントを結びつける
- イベントが発生したときの処理
1. 長方形と画像を作る。
まずは、長方形と画像をCanvas上に用意します。
作り方についてはこちら。
www.pytry3g.com
今回、長方形と画像を作るときに引数のtags
にそれぞれのオブジェクトを識別するための名前を渡しています。長方形にはrect
, 画像にはimg
という名前をそれぞれ付けています。(※下のプログラムで使用しているsmile.pngはここにあります。)
import tkinter as tk
from PIL import Image, ImageTk
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300, bg="white")
canvas.pack()
canvas.create_rectangle(100, 100, 120, 120, fill="red", tags="rect")
img = Image.open("smile.png")
tkimg = ImageTk.PhotoImage(img)
canvas.create_image(200, 200, image=tkimg, tags="img")
root.mainloop()
上のプログラムを実行すると下のウィンドウが立ち上がります。
2. 作ったオブジェクトとイベントを結びつける。
長方形と画像が用意できたので、それぞれのオブジェクトをイベントと結びつけます。今回はオブジェクトを左クリックしたとき(ButtonPress-1)と、左クリックした状態でマウスを動かしたとき(B1-Motion)にイベントを発生させます。(※そのほかのイベントについてはこちら。)
オブジェクトとイベントを結びつけるにはtag_bind(tag, event, func)
を使います。こんな感じ。
canvas.tag_bind("rect", "<ButtonPress-1>", pressed)
canvas.tag_bind("img", "<ButtonPress-1>", pressed)
canvas.tag_bind("rect", "<B1-Motion>", dragged)
canvas.tag_bind("img", "<B1-Motion>", dragged)
tag_bind
でtag
とevent
を結び付け、イベントが発生したときに指定した関数が呼ばれます。
今回の例だとオブジェクトが左クリックされたときはpressed()
が呼ばれ、左クリックした状態でマウスを動かしたときはdragged()
が呼ばれます。
全体のコードは下のようになります。
import tkinter as tk
from PIL import Image, ImageTk
def pressed(event):
print("クリックされました")
def dragged(event):
print("動かされてる~")
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300, bg="white")
canvas.pack()
canvas.create_rectangle(100, 100, 120, 120, fill="red", tags="rect")
img = Image.open("smile.png")
tkimg = ImageTk.PhotoImage(img)
canvas.create_image(200, 200, image=tkimg, tags="img")
canvas.tag_bind("rect", "<ButtonPress-1>", pressed)
canvas.tag_bind("img", "<ButtonPress-1>", pressed)
canvas.tag_bind("rect", "<B1-Motion>", dragged)
canvas.tag_bind("img", "<B1-Motion>", dragged)
root.mainloop()
3. イベントが発生したときの処理。
オブジェクトを左クリックしたときと、左クリックした状態でマウスを動かしたときの処理をそれぞれpressed()
とdragged()
に書いていきます。
まずpressed()
について
pressed_x = pressed_y = 0
item_id = -1
def pressed(event):
global pressed_x, pressed_y, item_id
item_id = canvas.find_closest(event.x, event.y)
tag = canvas.gettags(item_id[0])[0]
item = canvas.type(tag)
pressed_x = event.x
pressed_y = event.y
pressed()
は長方形か画像が左クリックしたときに呼び出されます。pressed()
の引数にはevent
があります。このevent
にはイベントが発生したオブジェクトの情報が入っています。
上のプログラムでやっていることは、オブジェクトをクリックしたときのCanvas上の位置(x, y)をcanvasのメソッドfind_closet()
に渡して、クリックされたオブジェクトのIDをitem_id
に入れています。(※オブジェクトのIDはオブジェクトを生成したときに戻り値として返ってきます。参考)
次にオブジェクトのIDをcanvasのメソッドgettags()
に渡して、オブジェクトに設定したタグを取得します。
さらに、canvasのメソッドtype()
にタグを渡して、アイテム名を取得しています。
dragged()
について。
def dragged(event):
global pressed_x, pressed_y, item_id
item_id = canvas.find_closest(event.x, event.y)
tag = canvas.gettags(item_id[0])[0]
item = canvas.type(tag)
delta_x = event.x - pressed_x
delta_y = event.y - pressed_y
if item == "rectangle":
x0, y0, x1, y1 = canvas.coords(item_id)
canvas.coords(item_id, x0+delta_x, y0+delta_y, x1+delta_x, y1+delta_y)
else:
x, y = canvas.coords(item_id)
canvas.coords(item_id, x+delta_x, y+delta_y)
pressed_x = event.x
pressed_y = event.y
dragged()
でやっていることは、
event
からイベントが発生したオブジェクトの情報を手に入れる。
- それぞれのオブジェクトを動かせるようにしたいので、canvasのメソッド
coords(tag or ID)
にオブジェクトのIDかタグを渡してオブジェクトの現在の位置を取得する。
coords(tag or ID)
にオブジェクトのIDかタグと、オブジェクトの新しい位置を渡して移動させる。
この記事で使ったコードをまとめたもの。
import tkinter as tk
from PIL import Image, ImageTk
pressed_x = pressed_y = 0
item_id = -1
def pressed(event):
global pressed_x, pressed_y, item_id
item_id = canvas.find_closest(event.x, event.y)
tag = canvas.gettags(item_id[0])[0]
item = canvas.type(tag)
pressed_x = event.x
pressed_y = event.y
def dragged(event):
global pressed_x, pressed_y, item_id
item_id = canvas.find_closest(event.x, event.y)
tag = canvas.gettags(item_id[0])[0]
item = canvas.type(tag)
delta_x = event.x - pressed_x
delta_y = event.y - pressed_y
if item == "rectangle":
x0, y0, x1, y1 = canvas.coords(item_id)
canvas.coords(item_id, x0+delta_x, y0+delta_y, x1+delta_x, y1+delta_y)
else:
x, y = canvas.coords(item_id)
canvas.coords(item_id, x+delta_x, y+delta_y)
pressed_x = event.x
pressed_y = event.y
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300, bg="white")
canvas.pack()
canvas.create_rectangle(100, 100, 120, 120, fill="red", tags="rect")
img = Image.open("smile.png")
tkimg = ImageTk.PhotoImage(img)
canvas.create_image(200, 200, image=tkimg, tags="img")
canvas.tag_bind("rect", "<ButtonPress-1>", pressed)
canvas.tag_bind("img", "<ButtonPress-1>", pressed)
canvas.tag_bind("rect", "<B1-Motion>", dragged)
canvas.tag_bind("img", "<B1-Motion>", dragged)
root.mainloop()
イベントを利用したアプリケーションの例
三目並べ
tkinterを使った三目並べの例。
www.pytry3g.com
オセロ
tkinterを使ったオセロの例。
www.pytry3g.com