Why do I need lambda func to have bind to call a method in Tkinter root window

I have this code (sorry not really minimal):

import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        # tk.Frame.__init__(self, parent, *args, **kwargs)
        super().__init__(parent, *args, **kwargs)
        self.reader = parent
        
        # self.reader = tk.Toplevel(self)
        self.reader.title("XXX")
        self.reader.resizable(False, True)
        self.reader.geometry("650x5")
        self.reader.minsize(650,150)
        
        self.panel = tk.Text(self.reader, wrap="word", width=12, font=('Courier',14))
        self.viewer = tk.Text(self.reader, wrap="word", font=('Courier',14))
        
        self.viewer.insert(tk.INSERT,'hnmnbbbnttnnbbb')
        
        self.panel.insert(tk.INSERT,'hggggngggmnbbbnnbbbbttnnbbb')
        
        self.labelv1 = tk.IntVar()
        # self.labelv1.set(int(self.panel.index('end-1c').split('.')[0]))
        
        self.labelv2 = tk.IntVar()
        # self.labelv2.set(int(self.viewer.index('end-1c').split('.')[0]))
        
        self.pippo = tk.StringVar()
        # self.pippo.set(("Panel : "+str(self.labelv1)+" Viewer : "+str(self.labelv2)))
        
        self.label = tk.Label(self.reader, textvariable = self.pippo)
        self.label.pack(side='top')
        # self.update_labels()
        
        self.counter = tk.IntVar()
        self.counter.set(0)
                        
        # self.reader.bind_class('Text', '<Key>', self.update_labels())   #winfo_toplevel()
        # self.winfo_toplevel().bind("<Key>", self.update_labels())

        # self.reader.bind("<Key>", self.update_labels())
               
        self.reader.bind("<Key>",  lambda event: self.update_labels())
                
        self.scrollbar = tk.Scrollbar(self.reader, command=(self.on_scrollbar))
        
        self.scrollbar.pack(side="right", fill="y")
        self.panel.pack(side="left", fill="both", expand=True)
        self.viewer.pack(side="right", fill="both", expand=True)
                
        # Changing the settings to make the scrolling work
        # self.scrollbar['command'] = self.on_scrollbar
        self.panel['yscrollcommand'] = self.on_textscroll
        self.viewer['yscrollcommand'] = self.on_textscroll
        
    def update_labels(self, *args):

        print('OKK')

        self.labelv1.set(int(self.panel.index('end-1c').split('.')[0]))
        
        self.labelv2.set(int(self.viewer.index('end-1c').split('.')[0]))
        
        self.pippo.set("Panel : "+str(self.labelv1.get())+" Viewer : "+str(self.labelv2.get()))

        self.counter.set(self.counter.get() + 1)
      
        print('updating labels : ' , self.counter.get(),'   /  ', self.pippo.get())
        return
                
    def on_scrollbar(self, *args):
        """
        Scrolls both text widgets when the scrollbar is moved
        """
        self.panel.yview(*args)
        self.viewer.yview(*args)
            
    def on_textscroll(self, *args):
        '''Moves the scrollbar and scrolls text widgets when the mousewheel
        is moved on a text widget'''
        self.scrollbar.set(*args)
        self.on_scrollbar('moveto', args[0])


if __name__ == "__main__":
    master = tk.Tk()
   
    MainApplication(master)  
    master.mainloop()  

I don’t understand why does:

self.reader.bind("<Key>", lambda event: self.update_labels())

that binds any pressed key inside the text windows to a function update_labels() that reports the number of line text inside the window itself

works, while

self.reader.bind("<Key>", self.update_labels())

doesn’t. I read a lot of docs and examples, here on SO too, but wasn’t able to figure-out why the latter doesn’t work as it should.

Answer

The issue you have is because you are giving the output of the function and not the reference of it. To fix your issue you should do:

self.reader.bind("<Key>", self.update_labels)

and somewhere define:

def update_labels(self, event):
    ...