tkinterdrag-and-droptreeview

Tkinter treeview - get the item under the cursor on <ButtonPress-1>


I am writing a drag and drop app where I drag data from a tree view to an entry widget. I want to be able to use the item from the tree that is under the cursor when the mouse button is pressed.

I can see how to obtain the selected item, but that is not returned at the moment of the event

Is there any way to return the item?

import tkinter as tk
from tkinter import ttk

def main() -> None:
    root = tk.Tk()
    root.title('Tree coords')

    tree = ttk.Treeview(root, height=30, show='headings',)
    tree.grid(row=0, column=0, sticky=tk.NSEW)
    tree.bind("<ButtonPress-1>", on_start)

    col_list = ('Names', 'Names', 30)
    tree['columns'] = col_list

    for name in ['ab', 'ef', 'mn']:
        values = (name)
        tree.insert('', 'end', values=values)

    root.mainloop()

def on_start(event=None):
    widget = event.widget
    selected_items = widget.selection()
    x, y = event.widget.winfo_pointerxy()
    print(x, y, selected_items)

if __name__ == '__main__':
    main()

Solution

  • The problem is that your binding is called before the treeview is updated. The treeview class bindings happen after the widget-specific bindings, so the selection() function will return the previously selected item. One way to fix this is to create the binding after the class bindings (see here for more information).

    You can also make the binding to "<<TreeviewSelect>>" instead of "<ButtonPress-1>", but it can be triggered not only by the mouse button (for example, by the "Up" or "Down" keys)).

    A third way is to use a function that can work almost independently of the treeview class binding. For example, you can use the identify_row method to get the item at some Y position. In your case, just replace selected_items = widget.selection() with selected_items = widget.identify_row(event.y). Although this is not an exact replacement, since the identify_row() method always returns only one element, while the selection() method can return multiple elements.