Home > Software engineering >  How to bind <Enter> and <Leave> to a looping declaration in tkinter python
How to bind <Enter> and <Leave> to a looping declaration in tkinter python

Time:09-29

So I've got this simple demo for my problem:

from functools import partial
import tkinter as tk

root = tk.Tk()
root.title("Test")
root.geometry("400x400")

colors = ["red", "green", "blue"]

for x in range(3):
    firstFrame = tk.Frame(root, bg=colors[x])
    firstFrame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
    firstFrame.bind("<Enter>", lambda e: print(f"Enter: {x}"))
    firstFrame.bind("<Leave>", lambda e: partial(print, f"Leave: {x}")())

root.mainloop()

When I run the code and hover over the red section, then the green one and finally over the blue one before closing the window, I get this output:

Enter: 2
Leave: 2
Enter: 2
Leave: 2
Enter: 2
Leave: 2

What I need is:

Enter: 0
Leave: 0
Enter: 1
Leave: 1
Enter: 2
Leave: 2

As you can see I've tried multiple approaches to get the desired output, like using partial to create a whole new function instead of simply calling print, but nothing yielded any results.

CodePudding user response:

This is a common problem in Python lambdas. A for loop variable only exists once, so if a lambda is created inside of a for loop, then the lambda will capture the changing, mutable value of the variable.

The trick to get around this is to exploit Python's default arguments. When a default argument is created for a function, it effectively creates a new property on that function object storing the current value of the variable. This is a separate variable, so future reassignments of the loop variable will not affect the default argument's value.

firstFrame.bind("<Enter>", lambda e, arg=x: print(f"Enter: {arg}"))

You'll see people name the default argument the same thing as the enclosing variable. The following snippet is exactly equivalent to the former; the x argument to the lambda shadows the x argument from the for loop.

firstFrame.bind("<Enter>", lambda e, x=x: print(f"Enter: {x}"))

This just reduces the cognitive load for the programmer to switch between two variable names for what is, conceptually, the same variable.

  • Related