Python 删除tkinter canvas.scan_标记对鼠标坐标的影响

Python 删除tkinter canvas.scan_标记对鼠标坐标的影响,python,canvas,tkinter,zooming,panning,Python,Canvas,Tkinter,Zooming,Panning,我正在使用tkinter用python开发GUI。我已经编写了提供缩放功能的代码,例如“放大”、“缩小”、“窗口缩放”、“上一次缩放”、“恢复完全缩放”以及最后的“平移”。对于“pan”命令,我使用canvas.scan_mark()和canvas.scan_dragto()方法。所有提到的命令都可以正常工作,除了当我按“平移”然后按“窗口缩放”或尝试绘制选择矩形时,我意外地发现窗口缩放或选择矩形从当前鼠标位置移动了平移命令的shift值,尽管我在“平移”命令结束时解除了所有鼠标事件的绑定。 我

我正在使用tkinter用python开发GUI。我已经编写了提供缩放功能的代码,例如“放大”、“缩小”、“窗口缩放”、“上一次缩放”、“恢复完全缩放”以及最后的“平移”。对于“pan”命令,我使用canvas.scan_mark()和canvas.scan_dragto()方法。所有提到的命令都可以正常工作,除了当我按“平移”然后按“窗口缩放”或尝试绘制选择矩形时,我意外地发现窗口缩放或选择矩形从当前鼠标位置移动了平移命令的shift值,尽管我在“平移”命令结束时解除了所有鼠标事件的绑定。 我尝试了类似于
xi,yi=canvas.xview()[0],canvas.yview()[0]
的方法,然后
canvas.xview\u moveto(xi),canvas.yview\u moveto(yi)
。它将画布视图恢复到其原始位置,但尚未解决问题。此外,它还会干扰“恢复完全缩放”和“以前缩放”命令。 请帮我解决这个问题

非常感谢

有关问题的简短描述,请参阅下面的代码。这不是我在程序中使用的,但它描述了问题。尝试先按缩放按钮并平移。通过先按“平移”按钮,然后按“缩放”按钮,再进行另一次尝试,并查看差异

from tkinter import *

root = Tk()
root.resizable(False, False)
frame = Frame(root)
frame.pack(expand=YES, fill=BOTH)

canv = Canvas(frame, bg='white', width=800, height=600)
canv.pack(side=TOP, expand=YES, fill=BOTH)
canv.create_rectangle(100,100,200,200, fill='red', width=3)
canv.create_oval(250,250,450,450, fill='blue', width=3)

canv.create_line(500,500,500,500, fill='white')

def pan():
    canv.bind('<Button-1>', startpan)
    canv.bind('<B1-Motion>', dragpan)
    canv.bind('<ButtonRelease-1>', endpan)
    canv.config(cursor='hand1')

def startpan(event):
    canv.scan_mark(event.x, event.y)

def dragpan(event):
    canv.scan_dragto(event.x, event.y, 1)

def endpan(event):
    unbind_events()

def unbind_events():
    canv.unbind('<Button-1>')
    canv.unbind('<B1-Motion>')
    canv.unbind('<ButtonRelease-1>')
    canv.config(cursor='arrow')

def zoom_window():
    canv.bind('<Button-1>', startzoomwindow)
    canv.bind('<B1-Motion>', dragzoomwindow)
    canv.bind('<ButtonRelease-1>', endzoomwindow)

def startzoomwindow(event):
    global x1, y1
    x1, y1 = event.x, event.y

def dragzoomwindow(event):
    global rect
    x2, y2 = event.x, event.y
    rect = canv.create_rectangle(x1, y1, x2, y2, width=2, outline='red')
    canv.delete(canv.find_below(rect))

def endzoomwindow(event):
    canv.delete(rect)
    x, y =  0.5 * (x1 + event.x), 0.5 * (y1 + event.y)
    rect_width = abs(event.x - x1)
    rect_height = abs(event.y - y1)
    canvwidth = canv.winfo_width() 
    canvheight = canv.winfo_height()
    factor = min(canvwidth / rect_width, canvheight / rect_height)
    canv.scale(ALL, x, y, factor, factor)
    unbind_events()

butnframe = Frame(frame)
butnframe.pack(side=TOP, expand=YES, fill=X)
Button(butnframe, text='Zoom Window', command=zoom_window).pack(side=LEFT)
Button(butnframe, text='Pan', command=pan).pack(side=RIGHT)
从tkinter导入*
root=Tk()
根目录。可调整大小(False,False)
帧=帧(根)
frame.pack(扩展=是,填充=两者)
canv=画布(框架,背景为白色,宽度为800,高度为600)
canv.pack(侧面=顶部,展开=是,填充=两者)
可以创建矩形(100100200200,填充为红色,宽度为3)
可以创建椭圆(250450450,填充为蓝色,宽度为3)
可以创建线(500500,填充为白色)
def pan():
canv.bind(“”,startpan)
canv.bind(“”,拖板)
canv.bind(“”,endpan)
canv.config(cursor='hand1')
def startpan(事件):
扫描标记(事件x、事件y)
def dragpan(事件):
扫描绘图(事件x,事件y,1)
def端盘(事件):
解除绑定事件()
def unbind_事件():
可以解除绑定(“”)
可以解除绑定(“”)
可以解除绑定(“”)
canv.config(cursor='arrow')
def zoom_window():
canv.bind(“”,startzoomwindow)
canv.bind(“”,dragzoomwindow)
canv.bind(“”,endzoomwindow)
def startzoomwindow(事件):
全局x1,y1
x1,y1=事件x,事件y
def dragzoomwindow(事件):
全局矩形
x2,y2=事件x,事件y
rect=canv.创建_矩形(x1,y1,x2,y2,宽度=2,轮廓='red')
canv.delete(canv.find_下方(rect))
def endzoomwindow(事件):
canv.delete(rect)
x、 y=0.5*(x1+事件x),0.5*(y1+事件y)
矩形宽度=abs(事件x-x1)
垂直高度=绝对高度(事件y-y1)
canvwidth=canv.winfo_width()
CanvHight=canv.winfo_高度()
系数=最小值(canvwidth/rect_宽度、CanvHight/rect_高度)
canv.比例(全部、x、y、因子、因子)
解除绑定事件()
但是帧=帧(帧)
butnframe.pack(侧面=顶部,展开=是,填充=X)
按钮(butnframe,text='Zoom Window',command=Zoom\u Window).pack(side=LEFT)
按钮(butnframe,text='Pan',command=Pan).pack(侧面=右侧)

当您绑定到鼠标事件时,报告的坐标是相对于窗口的。如果已滚动画布,则鼠标单击将按已滚动和/或缩放的数量关闭

要解决这个问题,需要将窗口坐标转换为画布坐标。您可以使用画布的
canvasx
canvasy
方法执行此操作:

import tkinter as tk
...
canvas = tk.Canvas(...)
canvas.bind('<3>', do_something)
...

def do_something(event):
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    ...
将tkinter作为tk导入
...
canvas=tk.canvas(…)
canvas.bind(“”,做点什么)
...
def do_某事(事件):
x=canvas.canvasx(event.x)
y=canvas.canvasy(event.y)
...

请显示一个说明问题的示例。请确保它尽可能小。我们不需要查看所有函数的代码,只需要运行程序并查看调用
scan\u mark
的负面影响即可。您的代码中有一个bug,但是对代码的简单描述不足以让我们调试它。谢谢您的回复。请参阅我在问题下方添加的代码以获得更多澄清。不过我会试试你在评论中的建议。谢谢你,布莱恩。我试过你在评论中的建议。窗口缩放效果很好。鼠标位置返回画布坐标系。但是,“上一次缩放”和“完全还原缩放”仍然无法正常工作。谢谢,Bryan。现在代码运行良好。除了您的建议之外,我还在“previous zoom”函数的代码中使用了canvas.xview_moveto()方法,以便在平移后回滚视图。