Устранение влияния tkinter canvas.scan_mark на координаты мыши

Я разрабатываю графический интерфейс с Python с использованием tkinter. Я написал код для обеспечения функций масштабирования, таких как "увеличение", "уменьшение", "увеличение окна", "предыдущее увеличение", "восстановление полного увеличения" и, наконец, "панорамирование". Для команды 'pan' я использовал методы canvas.scan_mark() и canvas.scan_dragto(). Все упомянутые команды работают нормально, за исключением того, что когда я нажимаю "панорамирование" и затем "масштабирование окна" или пытаюсь нарисовать прямоугольник выделения, где неожиданно обнаруживается, что масштабирование окна или прямоугольники выделения сдвигаются из текущей позиции мыши с помощью сдвига значение команды pan, хотя я открепляю все события мыши в конце команды 'pan'. Я пробовал что-то вроде xi, yi = canvas.xview()[0], canvas.yview()[0] а потом canvas.xview_moveto(xi), canvas.yview_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)

1 ответ

Решение

При привязке к событиям мыши сообщаемые координаты относятся к окну. Если вы прокрутили холст, щелчки мыши будут отключены на сумму, которую вы прокрутили и / или увеличили.

Чтобы обойти это, вам нужно преобразовать координату окна в координату холста. Вы делаете это с 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)
    ...
Другие вопросы по тегам