今日も見に来てくださって、ありがとうございます。石川さんです。まだまだCanvasについて、調べていきたいと思います。
先日までは、Canvasの移動ができるようになりました。今回は、前回の予言通り、拡大、縮小にチャレンジしたいと思います。イベントにMouseWheelというのがありましたので、これを使いたいと思います。Excelで調べてみたら、ホイールを普通に回すと上下のスクロール、Ctrlキーを押してホイールを回すと拡大縮小、Ctrl+Shiftキーを押してホイールをま指すと左右にスクロールしましたので、こちらに合わせてみたいと思います。
ソースコード
今回もイメージは変わらなかったので、ソースコードのみ掲載したいと思います。
import tkinter as tk class App(tk.Tk): def __init__(self): super().__init__() self.title("Canvas move test") self.geometry("400x200") sx = tk.Scrollbar(self, orient=tk.HORIZONTAL) sy = tk.Scrollbar(self, orient=tk.VERTICAL) c = tk.Canvas(self, background="white",xscrollcommand=sx.set,yscrollcommand=sy.set) sx.config(command=c.xview) sy.config(command=c.yview) self.info = tk.Label(self) self.info.grid(row=2,columnspan=2) c.grid(row=0, column=0, sticky=tk.NSEW) sx.grid(row=1, column=0, sticky=tk.EW) sy.grid(row=0, column=1, sticky=tk.NS) self.canvas = c self.start = None c.bind("<Motion>",self.move) c.bind("<ButtonPress>",self.button_press) c.bind("<ButtonRelease>",self.button_release) c.bind("<MouseWheel>",self.mouse_wheel) self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.bind("<Configure>", self.resize) c.create_rectangle(40,40,60,60) def move(self, event): x = self.canvas.canvasx(event.x) y = self.canvas.canvasy(event.y) self.info.configure(text=f"mouse position = ({event.x}, {event.y}) ({x},{y})") if self.start and self.item: original_x, original_y = self.start self.canvas.move(self.item,x - original_x,y - original_y) self.start = x, y self.resize(event) elif event.state == 256: # Button1 self.canvas.scan_dragto(event.x, event.y, gain=1) def button_press(self, event): x, y = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y) self.start = x, y self.item = self.canvas.find_overlapping(x-10,y-10,x+10,y+10) if self.item: self.item = self.item[0] else: self.canvas.scan_mark(event.x, event.y) if event.num == 3: self.item = self.canvas.create_rectangle(x-10,y-10,x+10,y+10) def button_release(self, event): self.start = None def resize(self, event): region = self.canvas.bbox(tk.ALL) if region[0] > 0: region = (0, *region[1:]) if region[1] > 0: region = (region[0], 0, *region[2:]) self.canvas.configure(scrollregion=region) def mouse_wheel(self, event): if event.state == 5: # Shift|Control self.canvas.scan_mark(event.x, event.y) self.canvas.scan_dragto(event.x + event.delta // 120, event.y, gain=10) elif event.state == 4: # Control scale = 1 + event.delta / 1200 self.canvas.scale(tk.ALL,event.x, event.y, scale, scale) elif event.state == 1: # Shift pass elif event.state == 0: # None self.canvas.scan_mark(event.x, event.y) self.canvas.scan_dragto(event.x, event.y + event.delta // 120, gain=10) self.resize(event) if __name__ == "__main__": app = App() app.mainloop()
解説
まずは、ホイールのイベントをバインドします。25行目にc.bind("<MouseWheel>",self.mouse_wheel)
を追加しました。これによって、ホイールが回されたときにmouse_wheel
が呼び出されるようになります。
それぞれコントロールキーを押されたとき、シフトキーを押されたとき、両方押されたときの実行結果をevent
をprint
してみました。
<MouseWheel event delta=-120 x=244 y=110> <MouseWheel event state=Control delta=-120 x=244 y=110> <MouseWheel event state=Shift delta=-120 x=244 y=110> <MouseWheel event state=Shift|Control delta=-120 x=244 y=110>
このままではstate
の値がわからないので、別途event.state
をprint
してみたところ、以下のようになっていました。
- Shift=1
- Ctrl=4
- Shift+Ctrl=5
delta
の値は、ホイールが下に回されると、-120、上に回されると120がセットされてきました。これらの値と先日使った、scan_mark
とscan_dragto
で上下スクロールを実装、scale
を使って拡大縮小を実装しました。拡大縮小のscale
は、このページを参考にさせていただきました。
あとは、resize
したときに左上が(0,0)の位置じゃなくなってしまうのは、どうなのかなぁ、と、思ったので、resize
を変更してみました。もしかしたら、もっといい方法があるかも知れませんが、とりあえず満足いく動作になりました。
まとめ
今回は、割合すんなりと、ホイールを使った拡大縮小ができるようになりました。拡大縮小したときに、箱を作ると、大きさの違う箱になってしまうので、拡大率、縮小率を保持して箱を作るときに参照する必要がありそうですね。
“Python tkinter GUI プログラミング Canvas scale” への2件の返信