I have a problem with my mouse pointer in Python Tkinter.
I have the following code:
import tkinter as tk
root = tk.Tk()
def motion(event):
x, y = window_canvas.canvasx(event.x), window_canvas.canvasy(event.y)
print('{}, {}'.format(x, y))
window_canvas = tk.Canvas(root, borderwidth=0, background="white", width = 300, height = 300, highlightthickness=0)
window_canvas.pack(fill='both')
window_frame = tk.Frame(window_canvas, background='red', borderwidth=0, width = 300, height = 300)
window_frame.pack()
button = tk.Button(window_frame, text=' ', borderwidth=1, highlightbackground='#9c9c9c', bg='black')
button.place(x=50, y=50)
root.bind('<Motion>', motion)
root.mainloop()
No I want, that the correct coordinates of my mouse in regard to the red frame are printed. However, when I hover over the button, the coordinates get altered and don't represent the real coordinates in accordance to the red window_frame anymore.
Does someone have a solution?
CodePudding user response:
Binding Motion
with Root vs Other Widgets:
After experimenting with your code, I made the following observations:
- When the
Motion
event is bound to root,(event.x, event.y)
returns the coordinates of any pixel in the window relative to the widget in which the pixel lies. The top left corner of the corresponding widget (notroot
) is taken to be (0, 0). - If you bind the
Motion
event to a particular widget,(event.x, event.y)
returns the coordinates of a pixel (relative to the widget) only if the pixel is present directly inside the widget. If you hover your mouse over a child widget, nothing gets printed.
Solution:
Now, coming to your problem, you cannot directly calculate the canvas coordinates from (event.x, event.y)
when the mouse hovers over the button. You will have to do the following conversion.
window_coords = topleft_button_coordinates (event.x, event.y)
canvas_coords = canvas.canvasx(window_coords.x), canvas.canvasy(window_coords.y)
The above conversion must be performed only if the coordinates are relative to the button. You can use the event.widget
attribute to check if the event was triggered by the button.
The coordinates of the top left corner of the button (relative to the canvas) can be obtained using .winfo_x()
and .winfo_y()
.
Working Code:
import tkinter as tk
root = tk.Tk()
def motion(event):
global button
convx, convy = event.x, event.y
if event.widget == button:
convx, convy = button.winfo_x() event.x, button.winfo_y() event.y
x, y = window_canvas.canvasx(convx), window_canvas.canvasy(convy)
print('{}, {}'.format(x, y))
window_canvas = tk.Canvas(root, borderwidth=0, background="white", width = 300, height = 300, highlightthickness=0)
window_canvas.pack(fill='both')
window_frame = tk.Frame(window_canvas, background='red', borderwidth=0, width = 300, height = 300)
window_frame.pack()
button = tk.Button(window_frame, text=' ', borderwidth=1, highlightbackground='#9c9c9c', bg='black')
button.place(x=50, y=50)
root.bind('<Motion>', motion)
root.mainloop()