• SOLVED: Tkinter button-motion event behaves differently underWindows and Linux

    From John O'Hagan@research@johnohagan.com to comp.lang.python on Mon Nov 11 21:46:16 2024
    From Newsgroup: comp.lang.python

    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
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From ram@ram@zedat.fu-berlin.de (Stefan Ram) to comp.lang.python on Mon Nov 11 13:43:34 2024
    From Newsgroup: comp.lang.python

    John O'Hagan <research@johnohagan.com> wrote or quoted:
    I'm posting this in case anyone else encounters the same problem

    Thanks for dropping that knowledge bomb on me!

    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.


    --- Synchronet 3.20a-Linux NewsLink 1.114