Home > Mobile >  How to stop grid from moving when dragged in tkinter canvas?
How to stop grid from moving when dragged in tkinter canvas?

Time:01-07

At the moment the code that I have will display and zoom an image perfectly fine, but I want to create a grid, like the one that you get on Windows 10. The problem is, that the code moves everything that is in the canvas, including the grid. How can I prevent this? The problem is probably in the move_from and move_to functions. Also, a disclaimer I did not write the code for most of the zooming functions.

Here is the code:

import random
import tkinter as tk
from tkinter import ttk
from PIL import Image, ImageTk

class Zoom(ttk.Frame):
    def __init__(self, mainframe, path):
        ''' Initialize the main Frame '''
        ttk.Frame.__init__(self, master=mainframe)
        self.master.title('Simple zoom with mouse wheel')

        # Open image
        self.image = Image.open(path)
        # Create canvas and put image on it
        self.canvas = tk.Canvas(self.master, highlightthickness=0)
        self.canvas.grid(row=0, column=0, sticky='nswe')

        # 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

        # 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='')
        self.canvas.bind("<Configure>", self.create_grid)
        self.show_image()
        self.canvas.configure(scrollregion=self.canvas.bbox('all'))

    def move_from(self, event):
        self.canvas.scan_mark(event.x, event.y)

    def move_to(self, event):
        self.canvas.scan_dragto(event.x, event.y, gain=1)

    def wheel(self, event):
        scale = 1.0
        if event.delta == -120:
            scale        *= self.delta
            self.imscale *= self.delta
        if 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(self.image, x, y, scale, scale)
        self.show_image()
        self.canvas.configure(scrollregion=self.canvas.bbox(self.image))

    def show_image(self):
        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-collection

    def create_grid(self, event=None):
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()
        self.canvas.delete("grid_line")

        for i in range(0, w, 100):
            self.canvas.create_line([(i, 0), (i, h)], tag="grid_line")

        for i in range(0, h, 100):
            self.canvas.create_line([(0, i), (w, i)], tag="grid_line")

path = 'image editor data/me.jpg'  # place path to your image here
root = tk.Tk()
app = Zoom(root, path=path)
root.mainloop()

CodePudding user response:

If you want to keep the grid not moving, you need to redraw the grid after dragging by calling create_grid() inside move_to().

Since the top-left corner is not (0, 0) anymore after dragging, you need to get the new (x, y) using Canvas.canvasx() and Canvas.canvasy() for drawing the grid:

def move_to(self, event):
    self.canvas.scan_dragto(event.x, event.y, gain=1)
    self.create_grid()
def create_grid(self, event=None):
    # get the coordinates of top-left corner
    x, y = int(self.canvas.canvasx(0)), int(self.canvas.canvasy(0))

    w = self.canvas.winfo_width()
    h = self.canvas.winfo_height()
    self.canvas.delete("grid_line")

    for i in range(x, x w, 100):
        self.canvas.create_line([(i, y), (i, y h)], tag="grid_line")

    for i in range(y, y h, 100):
        self.canvas.create_line([(x, i), (x w, i)], tag="grid_line")
  •  Tags:  
  • Related