| ■ | キャンバスのありがちなお絵描きなの |
from u4 import *
class A(U4Widget):
def init(self):
can = Canvas(self, bg="white", width=300, height=200)
can.pack()
self.sx = self.sy = None
can.bind("<Button1-Motion>", self.motion)
can.bind("<ButtonRelease-1>", self.release)
can.bind("<Button-3>", self.button3)
# 右ボタンが押されると呼ばれます
def button3(self, event):
self.quit()
# 左ボタンが押されたまま引きずられると呼ばれます
def motion(self, event):
can = event.widget
if self.sx != None:
can.create_line(self.sx, self.sy, event.x, event.y,
fill="#700040", width=5)
self.sx = event.x; self.sy = event.y
# 左ボタンが離されると呼ばれます
def release(self, event):
self.sx = self.sy = None
a = A("Canvas Example")
# end.
|
だいたいナニをやっているかはお分かり頂けると思います。 キャンバスに線を引くには、キャンバスのcreate_line メソッドを使います。先頭から4つが(x1, y1, x2, y2)で、 始点の座標と終点の座標を指定します。そのあとの fill=とwidth=はオプションです。
| ■ | キャンバスと図形アイテム |
from u4 import *
class A(U4Widget):
def init(self):
can = Canvas(self, bg="white", width=300, height=200)
can.pack()
self.circ = can.create_oval(135, 85, 165, 115, outline="#000060",
fill="#600000")
can.bind("<Button1-Motion>", self.motion)
can.bind("<Button-3>", self.button3)
def button3(self, event):
self.quit()
def motion(self, event):
can = event.widget
can.coords(self.circ, event.x-15, event.y-15, event.x+15, event.y+15)
a = A("Canvas Example")
# end.
|
一度描いた図形に対し、あとになって座標を変えたり、 色などの属性を変えたりしたいという場合には、キャンバスの create_なにがしというメソッドが返す値(図形ID) をPython変数に持たせておき、それを使って処理をします。 ここではスクリーンショットでご覧の通り円を描いているので、 create_ovalメソッドを使います。 その先頭から4つの引数は、 (左上のx座標, 左上のy座標, 右下のx座標, 右下のy座標)です。
self.circ = can.create_oval(135, 85, 165, 115, outline="#000060", fill="#600000") |
can.coords(self.circ, event.x-15, event.y-15, event.x+15, event.y+15) |
can.itemconfigure(self.circ, fill="#000060") |
| ■ | 円が動くなり |
from u4 import *
class A(U4Widget):
def init(self):
can = Canvas(self, bg="white", width=300, height=200)
can.pack()
self.circ = can.create_oval(135, 85, 165, 115,
outline="#000060", fill="#600000")
can.bind("<Button1-Motion>", self.motion)
can.bind("<Button-3>", self.button3)
self.after(300, self.autodrop, can)
def autodrop(self, can):
(x1, y1, x2, y2) = can.coords(self.circ)
can.coords(self.circ, x1, y1+3, x2, y2+3)
self.after(300, self.autodrop, can)
def button3(self, event):
self.quit()
def motion(self, event):
can = event.widget
can.coords(self.circ, event.x-15, event.y-15,
event.x+15, event.y+15)
a = A("Canvas Example")
# end.
|
ウィジェットのafter関数は通常2個ないし3個の引数をとります (3個目の引数がオプションです)。
self.after(300, self.autodrop) |
これすなわち、300ミリ秒後に関数self.autodropを実行するように、 という指令です。このとき、呼び出したい関数に何か追加の情報を渡したい場合には、
self.after(300, self.autodrop, can) |
このようにその情報を後ろにつけます。 ここで呼ばれるように指定される(コールバック)関数の定義は、
def autodrop(self, can): (x1, y1, x2, y2) = can.coords(self.circ) can.coords(self.circ, x1, y1+3, x2, y2+3) self.after(300, self.autodrop, can) |
このようになります。Tcl/Tkのafterと同様、 Tkinterのafterも一度呼ばれたらそれっきりなので、 一定間隔ごとに繰り返し呼ばせたい場合には、 コールバック関数の中で再度同じ関数を呼ぶようにafter関数を使います。
| ■ | Python/Tkinterのもっとキャンバス |
import sys
from Tkinter import *
class Application(Frame):
def quit_clicked(self):
sys.exit(0)
def __mousepressed(self, event):
self.start_x = event.x
self.start_y = event.y
def __mousemoved(self, event):
self.can.coords(self.circ, event.x-15, event.y-15,
event.x+15, event.y+15)
def __enter(self, event):
self.can.itemconfigure(self.circ, fill='yellow', outline='yellow')
def __leave(self, event):
# アイテムの変数でなく、タグでも指定できます。
self.can.itemconfigure('sample_circle', fill='red', outline='yellow')
def init(self):
self.can = Canvas(self, bg='white')
cmda = Button(self, text='Quit', command=self.quit_clicked)
self.can.pack(side='top')
cmda.pack(side='top', anchor='e')
# タグは文字列で指定
self.circ = self.can.create_oval(50, 50, 80, 80,
fill='red', outline='red', tag='sample_circle')
self.can.bind('<Button1-Motion>', self.__mousemoved)
# アイテムへのバインドは(Canvasのbindサブコマンド)は
# tag_bindメソッドになっています
self.can.tag_bind(self.circ, '<Enter>', self.__enter)
self.can.tag_bind(self.circ, '<Leave>', self.__leave)
def __init__(self, master=None):
Frame.__init__(self, master)
self.master.title('Main')
self.init()
self.pack()
app = Application()
app.mainloop()
# end.
|
一度描いた図形を後からコマンドひとつで場所や色などを変えることができるのは、
数あるGUIツールキットの中でTcl/Tkの最も強力な長所ですが
(Java AWT/Swing, Gtk+, Qtなどでは、一旦描いた図形を動かしたりするには、
描画を全部クリアして描きなおす処理を全部書かないといけません)、
Python/Tkinterでも同じ要領でメソッドひとつで図形を操作できます。
図形の位置を動かすにはcoordsまたはmoveメソッドを使います。前者は動かす先の座標を直接指定します。
後者は現在の位置からどれだけずらすかを指定します。
使いやすいほうを適宜選べばOKです。
図形の色や形状に関わるオプションはitemconfigure
メソッドで操作できます。
self.can.itemconfigure(self.circ, fill='yellow', outline='yellow') |
ところで、図形にはタグとゆーものが使えます。 これはPython/Tkinterでは好きな文字列が指定でき、 このタグをcoordsやitemconfigureに渡すこともできます。 複数の図形に同じタグをつけると、それらの図形をグループ化してまとめて動かしたり、色を変えたり、寿命を終えたら消したりすることができるので便利です。
self.can.itemconfigure('sample_circle', fill='yellow', outline='yellow')
|
最後に、ウィジェットにイベントをバインドするのと同様に、 キャンバスウィジェットの図形にイベントをバインドすることもできます。 Tcl/Tkではキャンバスのbindサブコマンドを使いますが、 Python/Tkinterではキャンバス自身のイベントを操るbindメソッドと名前がぶつかってしまうため、 tag_bindという名前になっています。
self.can.tag_bind(self.circ, '<Enter>', self.__enter) |
| ■ | スケール |
from u4 import *
class A(U4Widget):
def init(self):
f = Frame(self)
self.R = IntVar(); self.S = IntVar()
self.R.set(15); self.S.set(300)
sca1 = Scale(f, label="R(pixels):", variable=self.R, to=20,
orient=HORIZONTAL)
sca1["from"]=2
sca2 = Scale(f, label="Speed:", variable=self.S, to=500,
orient=HORIZONTAL)
sca2["from"] = 30
for e in (sca1, sca2): e.pack(side=LEFT)
can = Canvas(self, bg="white", width=300, height=200)
for e in (can, f): e.pack(side=TOP)
self.circ = can.create_oval(135, 85, 165, 115, outline="#000060",
fill="#600000")
can.bind("<Button1-Motion>", self.motion)
can.bind("<Button-3>", self.button3)
self.after(self.S.get(), self.autodrop, can)
def autodrop(self, can):
(x1, y1, x2, y2) = can.coords(self.circ)
if y1+3 >= 200: y1 = 0
self.moveCircleTo(can, x1, y1+3)
self.after(self.S.get(), self.autodrop, can)
def button3(self, event):
self.quit()
def moveCircleTo(self, can, x, y):
can.coords(self.circ, x, y, x+self.R.get()*2, y+self.R.get()*2)
def motion(self, event):
self.moveCircleTo(event.widget, event.x, event.y)
a = A("Canvas Example")
# end.
|
スケールはScaleクラスのオブジェクトです。 現在選択されている値を変数に保持させるために、 エントリやチェックボタンのところで出てきた変数オブジェクト をまたしても使っています。 値の性質上、IntVarかDoubleVarオブジェクトを使うことになるでしょう。
self.R = IntVar()
self.R.set(15)
sca1 = Scale(f, label="R(pixels):",
variable=self.R, to=20, orient=HORIZONTAL)
sca1["from"]=2
|
セクションのサブメニューに戻る
(first uploaded 2001/08/06 last updated 2002/03/11)