'Activate other widgets while mouse dragging

I have multiple Tkinter.labels in a row and i would like the user to be able to click and drag their mouse over each one, activating them in the process.

I know about bindings, but i need multiple events in a single binding. Ive been messing around with <Button-1> and <Enter>, however i need a callback to be called only when both are true.

I know l.bind('<Button-1>,<Enter>', ...) is not valid.

Anyone with more Tkinter experience know of a way to chain binding, or make a multi-bind??



Solution 1:[1]

I encountered this same problem today and thanks to @Bryan Oakley's answer I was able to code a working solution. I will share my code in the hope that it will help someone someday.

This example builds 2 tkinter TreeViews, and enables dragging-and-dropping treeItems between the 2 trees. The key point is that by binding both trees to the B1-motion event, both trees are able to respond to the events.

import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class TreeItem:
    """
    Keeps a reference to a treeItem together with its parent tree. 
    """
    def __init__(self, tree, item):
        self.tree = tree
        self.item = item
        self.itemTxt = tree.item(item,"text")
    
    def __str__(self):
        """
        Prints 'treename, itemname' upon calling str(TreeItem)
        """
        return f'{self.tree}, {self.itemTxt}'

class Mouse(metaclass=Singleton):
    """
    Handles treeitem clicking, dragging, dropping and shows feedback messages about them.
    """
    def __init__(self, root):
        self.root = root
        self.clicked_item = None
        self.is_dragging = False
        self.drag_time = 0
        self.current_hovering_widget = None

    def OnMouseDown(self, event):
        clicked_item = self.get_item_under_mouse(event)
        print("You clicked on", str(clicked_item))
        self.clicked_item = clicked_item
    
    def OnDrag(self, event):
        self.is_dragging = True
        self.show_drag_init_msg()
        self.show_hovering_item_change_msg(event)
        self.drag_time += 1
    
    def OnMouseUp(self, event):
        if self.is_dragging:
            self.finish_drag()
            self.show_drop_msg()
        self.clicked_item = None

    def finish_drag(self):
        self.is_dragging = False
        self.drag_time = 0

    def show_drag_init_msg(self):
        if self.drag_time == 0:
            print("You are now dragging item", self.clicked_item.tree, self.clicked_item.itemTxt)

    def show_hovering_item_change_msg(self, event):
        currently_hovering = self.get_item_under_mouse(event)
        if str(self.current_hovering_widget) != str(currently_hovering):
            print("Mouse is above", str(currently_hovering))
            self.current_hovering_widget = currently_hovering

    def show_drop_msg(self):
        dragged_item:TreeItem = self.clicked_item
        dragged_onto:TreeItem = self.current_hovering_widget
        print(f'You dropped {str(dragged_item)} onto {str(dragged_onto)}')
    
    def get_item_under_mouse(self, event):
        current_tree = self.root.winfo_containing(event.x_root, event.y_root)
        current_tree_item = current_tree.identify("item", event.x, event.y)
        return TreeItem(tree=current_tree, item=current_tree_item)

class Tree:
    def __init__(self, root, row, col):
        self.root: tk.Tk = root
        self.create_tree(root, row, col)

    def OnDrag(self,event):
        Mouse(self.root).OnDrag(event)

    def OnMouseDown(self, event):
        Mouse(self.root).OnMouseDown(event)
    
    def OnMouseUp(self, event):
        Mouse(self.root).OnMouseUp(event)
    
    def create_tree(self, root, row, col):
        self.tree = ttk.Treeview(root)
        self.tree.heading('#0', text='Departments', anchor='w')
        self.tree.grid(row=row, column=col, sticky='nsew')
        self.add_dummy_data()

        # add bindings
        self.tree.bind("<ButtonPress-1>", self.OnMouseDown)
        self.tree.bind("<ButtonRelease-1>", self.OnMouseUp)
        self.tree.bind("<B1-Motion>", self.OnDrag)

    def add_dummy_data(self):
        # adding data
        self.tree.insert('', tk.END, text='Administration', iid=0, open=False)
        self.tree.insert('', tk.END, text='Logistics', iid=1, open=False)
        self.tree.insert('', tk.END, text='Sales', iid=2, open=False)
        self.tree.insert('', tk.END, text='Finance', iid=3, open=False)
        self.tree.insert('', tk.END, text='IT', iid=4, open=False)

        # adding children of first node
        self.tree.insert('', tk.END, text='John Doe', iid=5, open=False)
        self.tree.insert('', tk.END, text='Jane Doe', iid=6, open=False)
        self.tree.move(5, 0, 0)
        self.tree.move(6, 0, 1)


root = tk.Tk()
root.geometry('620x200')

# make two trees
tree1 = Tree(root,0,0)
tree2 = Tree(root,0,1)

# run the app
root.mainloop()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jnz