简体   繁体   中英

How to trigger tkinter's “<Enter>” event with mouse down?

In tkinter with Python 3.7, the default behavior for an event binding is that an "<Enter>" event will not be triggered after the mouse has been clicked down before it has been released. I was intending to implement a scrollable table such that it detects "<Button-1>" (Mouse left-click down) and "<ButtonRelease-1>" (Mouse left-click up) events as well as having each table-row's widgets "<Enter>" event bound to detect when the mouse pointer enters a different table row. In this way I could scroll my table by clicking an row and dragging through a table. My assumption was that "<Enter>" events would be triggered even while the mouse button is held down, which was incorrect. So, my entire scrolling implementation hit a brick wall. I need these events to be triggered while the mouse is down or it just won't work. I'm doing something like:

from tkinter import *

class App:
    def __init__(self):
        self.root = Tk()
        # The name kwarg is used to infer the index of the row in the event handlers.
        self.labels = [Label(text=f"Label #{i}", name=f"row-{i}") for i in range(5)]
        for row, label in enumerate(self.labels):
            label.bind("<Button-1>", self.mouse_down)
            label.bind("<ButtonRelease-1>", self.mouse_up)
            label.bind("<Enter>", self.mouse_enter)
            label.grid(row=row, column=0)
        mainloop()

    def mouse_up(self, event):
        idx = self.index_from_event(event)
        # Do some with the row with the passed index

    def mouse_down(self, event):
        idx = self.index_from_event(event)
        # Do some with the row with the passed index

    def mouse_enter(self, event):
        # I would like for this to be triggered even when the mouse is pressed down.
        # However, by default tkinter doesn't allow this.
        pass

    def index_from_event(self, event):
        # Get the index of the row from the labels name string.
        return str(event.widget).split('-')[-1]

Any way to enable mouse enter events while the mouse button 1 is held down in tkinter? All the docs on effbot ( http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm ) say about the enter event is:

<Enter>
    The mouse pointer entered the widget (this event doesn’t mean that the user pressed the Enter key!).

No, there is no direct way to bind to the enter and leave events when the button is down, except for the widget which first gets the click. It is fairly easy to add that ability, however,

You can bind <B1-Motion> to all widgets which will call a function whenever the mouse moves. You can then use the winfo_containing method to figure out which widget is under the cursor. With that information you could generate a virtual event which you can bind to (or you can skip the virtual events and add your code in the handler for the motion event).

Here's a bit of a contrived example. When you click and move the mouse, show_info will be called. It keeps track of the current widget and compares it to the widget under the cursor. If it's different, it generates a <<B1-Leave>> to the previous widget and a <<B1-Enter>> on the new widget. Those bindings will display "Hello, cursor!" when the cursor is over the label.

import tkinter as tk

current_widget = None
def show_info(event):
    global current_widget
    widget = event.widget.winfo_containing(event.x_root, event.y_root)
    if current_widget != widget:
        if current_widget:
            current_widget.event_generate("<<B1-Leave>>")
        current_widget = widget
        current_widget.event_generate("<<B1-Enter>>")

def on_enter(event):
    event.widget.configure(text="Hello, cursor!")

def on_leave(event):
    event.widget.configure(text="")

root = tk.Tk()
label = tk.Label(root, bd=1, relief="raised")
l1 = tk.Label(root, text="", width=20, bd=1, relief="raised")
l2 = tk.Label(root, text="", width=20, bd=1, relief="raised")

label.pack(side="top", fill="x")
l1.pack(fill="both", expand=True, padx=20, pady=20)
l2.pack(fill="both", expand=True, padx=20, pady=20)

root.bind_all("<B1-Motion>", show_info)
l1.bind("<<B1-Enter>>", on_enter)
l1.bind("<<B1-Leave>>", on_leave)
l2.bind("<<B1-Enter>>", on_enter)
l2.bind("<<B1-Leave>>", on_leave)

tk.mainloop()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM