When and why do I need to change a window coordinate to a canvas coordinate?

Viewed 96

I tried to see the difference between a window coordinate and a canvas coordinate by giving a canvas a callback that prints the coordinates of a mouse click in both types. I expected the two types of coordinates to have different values, but they didn't. Also, I tried to pass window coordinates to the find_closest method which, as stated in the documentation, accepts canvas coordinates, and there was no error.

import tkinter as tk

def callback(event):
    canvas = event.widget
    print()
    print("Window x-coordinate:", event.x)
    print("Window y-coordinate:", event.y)
    x = canvas.canvasx(event.x)
    y = canvas.canvasy(event.y)
    print("Canvas x-coordinate:", x)
    print("Canvas y-coordinate:", y)
    print("closest:", canvas.find_closest(x, y))

master = tk.Tk()

frame = tk.Frame(master, width=50, height=50, bd=5, bg="green", relief="groove")
frame.pack_propagate(0)
frame.pack()

button = tk.Button(frame)
button.pack(fill="none")

canvas = tk.Canvas(master, bd=5, width=50, height=50, bg="red", relief="ridge")
canvas.bind("<Button-1>", callback)
canvas.pack_propagate(0)
canvas.pack()

master.mainloop()
1 Answers

You only need to do the conversion if your canvas is able to scroll. Otherwise there's a 1:1 mapping between window and canvas coordinates.

You can see this by adding the following code to your example, at some point after creating the canvas:

canvas.configure(scrollregion=(0,0,1000,1000))
canvas.yview_moveto(.5)
canvas.xview_moveto(.5)

There's also another behavior that is not commonly used, which is that canvasx and canvasy can round the coordinate to the size of a gridspacing unit.

Here's the canonical description of the canvasx method:

Given a window x-coordinate in the canvas screenx, this command returns the canvas x-coordinate that is displayed at that location. If gridspacing is specified, then the canvas coordinate is rounded to the nearest multiple of gridspacing units.

You can observe that by changing your calls to canvasx and canvasy to be something like the following, which will return coordinates that are always a multiple of 2.

x = canvas.canvasx(event.x, gridspacing=2)
y = canvas.canvasy(event.y, gridspacing=2)
Related