Python tkinter GUI プログラミング Canvas scale

 今日も見に来てくださって、ありがとうございます。石川さんです。まだまだ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が呼び出されるようになります。

 それぞれコントロールキーを押されたとき、シフトキーを押されたとき、両方押されたときの実行結果をeventprintしてみました。

<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.stateprintしてみたところ、以下のようになっていました。

  • Shift=1
  • Ctrl=4
  • Shift+Ctrl=5

 deltaの値は、ホイールが下に回されると、-120、上に回されると120がセットされてきました。これらの値と先日使った、scan_markscan_dragtoで上下スクロールを実装、scaleを使って拡大縮小を実装しました。拡大縮小のscaleは、このページを参考にさせていただきました。

 あとは、resizeしたときに左上が(0,0)の位置じゃなくなってしまうのは、どうなのかなぁ、と、思ったので、resizeを変更してみました。もしかしたら、もっといい方法があるかも知れませんが、とりあえず満足いく動作になりました。

まとめ

 今回は、割合すんなりと、ホイールを使った拡大縮小ができるようになりました。拡大縮小したときに、箱を作ると、大きさの違う箱になってしまうので、拡大率、縮小率を保持して箱を作るときに参照する必要がありそうですね。

“Python tkinter GUI プログラミング Canvas scale” への2件の返信

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


The reCAPTCHA verification period has expired. Please reload the page.