在 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")
目前,我的代码可以完美地显示和缩放图像,但我想创建一个网格,就像您在 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")