2

I would like to pass both event and an additional argument to a handler function. The purpose of this is to display a context menu using the x_root and y_root arguments of the event object.

I have tried the two techniques shown below. For simplicity, I prefer the one that uses the lambda function, but it doesn't work.

Is it possible to fix it? Any idea or explanation?

import tkinter as tk
    
class Example:

    def __init__(self, window):
        base = tk.Frame(window).pack()

        for i in range(3):
            l = tk.Label(base, text='label '+str(i))
            l.pack()

            def interhandler(event, self=self, i=i):
                return self._handler_one(event, i)

            l.bind("<Button-1>", interhandler)
            l.bind("<Button-3>", lambda event, i=i: self._handler_two(i))

    def _handler_one(self, event, k):
        print(event) # works
        print('left click on label', k)

    def _handler_two(self, k):
        print(event) # does not work
        print('right click on label ', k)

main = tk.Tk()
example = Example(main)
main.mainloop()
e_moro
  • 141
  • 3
  • if you want to send both then `_handler_two` should get two values `def _handler_two(self, event, k):` – furas Sep 08 '22 at 17:26
  • 1
    It does not work, because you just parse `i` and no `event`. `self._handler_two(event,i)` and `def _handler_two(self, event, k): ` should work – Thingamabobs Sep 08 '22 at 17:29
  • @Thingamabobs, that's right! Yo can post it as an answer if you want. – e_moro Sep 08 '22 at 17:31
  • Thank you, but @furas was bit quicker. – Thingamabobs Sep 08 '22 at 17:32
  • By the way, it seems strange to me: I assumed that handler functions only take one argument... – e_moro Sep 08 '22 at 17:33
  • 1
    An event driven function has no difference from an ordinary function, despite the fact that tkinter parses an event object through the interface. If you are interested, I have wrote something about [lambda](https://stackoverflow.com/a/62742314/13629335) and the [event parameter](https://stackoverflow.com/a/73433401/13629335) – Thingamabobs Sep 08 '22 at 17:36
  • Thanks, It will be usefull foro me! – e_moro Sep 08 '22 at 17:38

2 Answers2

2

If you want to send both values then function should get both values

def _handler_two(self, event, k):  # <--- event
    print(event)  # now it works
    print('right click on label ', k)

And you should send both values

lambda event, i=i: self._handler_two(event, i)  # <--- event

Full working code

import tkinter as tk
    
class Example:

    def __init__(self, window):
        base = tk.Frame(window).pack()

        for i in range(3):
            l = tk.Label(base, text='label '+str(i))
            l.pack()

            def interhandler(event, self=self, i=i):
                return self._handler_one(event, i)

            l.bind("<Button-1>", interhandler)
            l.bind("<Button-3>", lambda event, i=i: self._handler_two(event, i))  # <--- event

    def _handler_one(self, event, k):
        print(event) # works
        print('left click on label', k)

    def _handler_two(self, event, k):  # <--- event
        print(event)  # now it works
        print('right click on label ', k)

main = tk.Tk()
example = Example(main)
main.mainloop()
furas
  • 134,197
  • 12
  • 106
  • 148
1

Issues: You have not passed the event argument when you invoke your _handler_two callback function. Your callback function should also take two arguments, the event and the additional value.

Solution: Your code with the corrections can be found below:

import tkinter as tk


class Example:

    def __init__(self, window):
        base = tk.Frame(window).pack()

        for i in range(3):
            l = tk.Label(base, text='label ' + str(i))
            l.pack()

            def interhandler(event, self=self, i=i):
                return self._handler_one(event, i)

            l.bind("<Button-1>", interhandler)
            l.bind("<Button-3>", lambda event, i=i: self._handler_two(event, i))

    def _handler_one(self, event, k):
        print(event)
        print('left click on label', k)

    def _handler_two(self, event, k):
        print(event)
        print('right click on label ', k)


main = tk.Tk()
example = Example(main)
main.mainloop()

Sample Output: Below is the resulting window:

enter image description here

Below is the output after clicking the right mouse button on label 0:

<ButtonPress event state=Mod1 num=3 x=29 y=16>
right click on label  0

Alternatively: You can define your function to take a variable number of arguments:

def _handler_two(self, *args):
    print(args[0])
    print('right click on label ', args[1])
Prins
  • 1,051
  • 1
  • 6
  • 9