在 tkinter canvas 中拖动时如何阻止网格移动?

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

目前,我的代码可以完美地显示和缩放图像,但我想创建一个网格,就像您在 Windows 10 上看到的那样。问题是,代码移动 canvas 中的所有内容,包括网格。我怎样才能防止这种情况发生?问题可能出在 move_from 和 move_to 函数中。另外,免责声明我没有为大多数缩放功能编写代码。

代码如下:

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

如果想让网格不移动,需要在move_to()里面调用create_grid()重绘网格。

由于拖动后左上角不再是 (0, 0),因此需要使用 Canvas.canvasx()Canvas.canvasy() 获取新的 (x, y) 来绘制网格:

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