Move and zoom a tkinter canvas with mouse Move and zoom a tkinter canvas with mouse tkinter tkinter

Move and zoom a tkinter canvas with mouse


Mouse events are reported in 'screen coordinates'. When you have a scrolledcanvas, you often need to convert those numbers to 'canvas (ie. scrollregion)coordinates'.

eg. for your zoom focus:

true_x = canvas.canvasx(event.x)true_y = canvas.canvasy(event.y)


This is a simplified zoom example. You should use more advanced techniques to not cram the memory with a huge resized image for the large zooms.

Do not forget to place a path to your image at the end of the script.

P.S. For the advanced zoom example look here.

# -*- coding: utf-8 -*-# WARNING: This is a simplified zoom example.# You should use more advanced techniques to not cram the memory# with a huge resized image for the large zooms.import randomimport tkinter as tkfrom tkinter import ttkfrom PIL import Image, ImageTkclass AutoScrollbar(ttk.Scrollbar):    ''' A scrollbar that hides itself if it's not needed.        Works only if you use the grid geometry manager '''    def set(self, lo, hi):        if float(lo) <= 0.0 and float(hi) >= 1.0:            self.grid_remove()        else:            self.grid()        ttk.Scrollbar.set(self, lo, hi)    def pack(self, **kw):        raise tk.TclError('Cannot use pack with this widget')    def place(self, **kw):        raise tk.TclError('Cannot use place with this widget')class Zoom(ttk.Frame):    ''' Simple zoom with mouse wheel '''    def __init__(self, mainframe, path):        ''' Initialize the main Frame '''        ttk.Frame.__init__(self, master=mainframe)        self.master.title('Simple zoom with mouse wheel')        # Vertical and horizontal scrollbars for canvas        vbar = AutoScrollbar(self.master, orient='vertical')        hbar = AutoScrollbar(self.master, orient='horizontal')        vbar.grid(row=0, column=1, sticky='ns')        hbar.grid(row=1, column=0, sticky='we')        # Open image        self.image = Image.open(path)        # Create canvas and put image on it        self.canvas = tk.Canvas(self.master, highlightthickness=0,                                xscrollcommand=hbar.set, yscrollcommand=vbar.set)        self.canvas.grid(row=0, column=0, sticky='nswe')        vbar.configure(command=self.canvas.yview)  # bind scrollbars to the canvas        hbar.configure(command=self.canvas.xview)        # Make the canvas expandable        self.master.rowconfigure(0, weight=1)        self.master.columnconfigure(0, weight=1)        # Bind events to the Canvas        self.canvas.bind('<ButtonPress-1>', self.move_from)        self.canvas.bind('<B1-Motion>',     self.move_to)        self.canvas.bind('<MouseWheel>', self.wheel)  # with Windows and MacOS, but not Linux        self.canvas.bind('<Button-5>',   self.wheel)  # only with Linux, wheel scroll down        self.canvas.bind('<Button-4>',   self.wheel)  # only with Linux, wheel scroll up        # Show image and plot some random test rectangles on the canvas        self.imscale = 1.0        self.imageid = None        self.delta = 0.75        width, height = self.image.size        minsize, maxsize = 5, 20        for n in range(10):            x0 = random.randint(0, width - maxsize)            y0 = random.randint(0, height - maxsize)            x1 = x0 + random.randint(minsize, maxsize)            y1 = y0 + random.randint(minsize, maxsize)            color = ('red', 'orange', 'yellow', 'green', 'blue')[random.randint(0, 4)]            self.canvas.create_rectangle(x0, y0, x1, y1, outline='black', fill=color,                                         activefill='black', tags=n)        # Text is used to set proper coordinates to the image. You can make it invisible.        self.text = self.canvas.create_text(0, 0, anchor='nw', text='Scroll to zoom')        self.show_image()        self.canvas.configure(scrollregion=self.canvas.bbox('all'))    def move_from(self, event):        ''' Remember previous coordinates for scrolling with the mouse '''        self.canvas.scan_mark(event.x, event.y)    def move_to(self, event):        ''' Drag (move) canvas to the new position '''        self.canvas.scan_dragto(event.x, event.y, gain=1)    def wheel(self, event):        ''' Zoom with mouse wheel '''        scale = 1.0        # Respond to Linux (event.num) or Windows (event.delta) wheel event        if event.num == 5 or event.delta == -120:            scale        *= self.delta            self.imscale *= self.delta        if event.num == 4 or event.delta == 120:            scale        /= self.delta            self.imscale /= self.delta        # Rescale all canvas objects        x = self.canvas.canvasx(event.x)        y = self.canvas.canvasy(event.y)        self.canvas.scale('all', x, y, scale, scale)        self.show_image()        self.canvas.configure(scrollregion=self.canvas.bbox('all'))    def show_image(self):        ''' Show image on the Canvas '''        if self.imageid:            self.canvas.delete(self.imageid)            self.imageid = None            self.canvas.imagetk = None  # delete previous image from the canvas        width, height = self.image.size        new_size = int(self.imscale * width), int(self.imscale * height)        imagetk = ImageTk.PhotoImage(self.image.resize(new_size))        # Use self.text object to set proper coordinates        self.imageid = self.canvas.create_image(self.canvas.coords(self.text),                                                anchor='nw', image=imagetk)        self.canvas.lower(self.imageid)  # set it into background        self.canvas.imagetk = imagetk  # keep an extra reference to prevent garbage-collectionpath = 'doge2.jpg'  # place path to your image hereroot = tk.Tk()app = Zoom(root, path=path)root.mainloop()