Discussion:
SOLVED: Tkinter button-motion event behaves differently under Windows and Linux
(too old to reply)
John O'Hagan
2024-11-11 10:46:16 UTC
Permalink
I'm posting this in case anyone else encounters the same problem, and
to ask for suggestions, if any, about a better way to do it.

I'm implementing a method for dragging embedded widgets on a Text
widget. When the left mouse button is held down over an embedded widget
and the mouse is dragged across other widgets embedded in the same
parent, the original widget moves to the new positions until the button
is released.

Here's some minimal code:

-------

from tkinter import *
text = Text(Tk())
text.pack()

def drag(e):
print(e.widget)
target = e.widget.winfo_containing(e.x_root, e.y_root)
if target and target not in (text, e.widget):
if text.compare(target, '>', e.widget):
target = f'{target} + 1 char'
text.window_create(target, window=e.widget)

for n in ('1', '2', '3'):
l=Label(text, text=n, width=10)
#l.bind('<Button-1>', lambda e:e.widget.grab_set())
l.bind('<B1-Motion>', drag)
#l.bind('<ButtonRelease-1>', lambda e:e.widget.grab_release())
text.window_create(END, window=l)

mainloop()

-------

This works as intended for me on Windows (11). The print statement
always shows the name of the first selected widget while the button is
held down, regardless of where the mouse is dragged to.

But on Linux (Debian testing with Gnome), for me the above code only
moves the widget to the first new position it is dragged to, any
further dragging is ineffective, and the print statement shows the
names of the subsequently-traversed widgets.

There is a further issue in the real code (not shown here) also on
Linux only, where if the cursor traverses any other widgets bound to
'<B1-Motion>', they are also triggered, which is not intended.

Just a guess, but it seems that on Linux, the focus switches to
whatever widget is under the cursor even during dragging, so any
bindings on the originally-clicked widget are no longer triggered,
whereas Windows maintains focus on the originally-clicked widget during
dragging until the button is released.

If that's the case (and I never thought I'd say this), I think Windows
is right! But for all I know it might be the window manager or
something else.

I eventually figured out that the commented lines calling grab_set and
grab_release solved the issue for me on Linux.

I haven't found this documented anywhere and I'm interested to know if
anyone can cast any light on it.

Thanks
Stefan Ram
2024-11-11 13:43:34 UTC
Permalink
Post by John O'Hagan
I'm posting this in case anyone else encounters the same problem
Thanks for dropping that knowledge bomb on me!
Post by John O'Hagan
I haven't found this documented anywhere and I'm interested to know if
anyone can cast any light on it.
By leveraging grab_set() when you kick off the drag and
grab_release() when you wrap it up, you're golden - the
widget you first clicked keeps hogging all those mouse
events throughout the whole drag shindig, no matter how
wonky the underlying system's event handling gets.

Loading...