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")
