Tkinter button bound to Enter immediately executes

Question:

Answers from these questions: How to pass arguments to a Button command in Tkinter? and Why is Button parameter “command” executed when declared? are very much appreciated.

However, when I bind the button to automatically execute when the Enter key is pressed, it executes immediately. My code is:

from Tkinter import *

import ttk

from timeAttendance_show_DTR_GUI import * 


def employee_toShow(leftFrame,rightFrame):  

        empNumberToShow = '1'
        beginDateToShow = '2014-06-01'
        endDateToShow = '2014-06-31'
        requiredReport='dtr'

        emp = IntVar()  #please don't mind yet other parts as I am still working on them bit by bit.
        ttk.Label(leftFrame, text='Enter Employee Number').pack()
        emp_entry = ttk.Entry(leftFrame, textvariable=emp)
        emp_entry.pack()

        emp_DTR = ttk.Button(leftFrame, text='Calculate', command=lambda: indiv_DTR(rightFrame, empNumberToShow, beginDateToShow, endDateToShow, requiredReport))

        emp_entry.focus()
        emp_DTR.pack()
        root.bind('<Return>', indiv_DTR(rightFrame, empNumberToShow, 
            beginDateToShow, endDateToShow, requiredReport))  # This is where i get the problem


def indiv_DTR(frame, empNumberToShow, beginDateToShow, endDateToShow, requiredReport):

        dtr, absent, frequencyOfLate, frequencyOfUndertime, totalMinutesLate, totalMinutesUndertime, 
            frequencyOfGracePeriod, gracePeriodTotal = initialization(empNumberToShow, beginDateToShow, endDateToShow, requiredReport)

        tree = ttk.Treeview(frame, height=31)
        tree['show'] = 'headings'

        tree["columns"]=('Date', 'Day', 'AmIn', 'AM Out', 'PM In', 'PM Out', 'OT In', 'OT Out', 'Late', 'Early Out', 'Remarks')


        tree.column('Date', width=60)
        tree.column('Day', width=45 )
        tree.column('AmIn', width=50)
        tree.column('AM Out', width=50)
        tree.column('PM In', width=50)
        tree.column('PM Out', width=50)
        tree.column('OT In', width=50)
        tree.column('OT Out', width=50)
        tree.column('Late', width=50)
        tree.column('Early Out', width=65)
        tree.column('Remarks', width = 95)

        tree.heading('Date', text='Date')
        tree.heading("Day", text="Day")
        tree.heading("AmIn", text="AM In")
        tree.heading('AM Out', text='AM Out')
        tree.heading('PM In', text = 'PM In')
        tree.heading('PM Out', text = 'PM Out')
        tree.heading('OT In', text = 'OT In')
        tree.heading('OT Out', text='OT Out')
        tree.heading('Late', text='Late')
        tree.heading('Early Out', text='Early Out')
        tree.heading('Remarks', text='Remarks')

        for i in dtr:
            tree.insert("" , 'end', text= '', 
                values=(i[0],i[1],i[2],i[3],i[4],i[5],i[6],i[7],i[8],i[9], i[10])) 
            tree.pack()

if __name__ == '__main__':

        employee_toShow(leftFrame,rightFrame)
        root.mainloop()

Obviously, just a newbie here (not even sure if my code indented correctly)… Any help will be much appreciated.

Asked By: Inner Peace

||

Answers:

When you call root.bind for <Return> you are passing the result of calling indiv_DTR(...) as a parameter. This is equivalent to the following:

res = indiv_DTR(rightFrame, empNumberToShow, beginDateToShow, endDateToShow, requiredReport)
root.bind('<Return>', res)

which should make it clearer that the function has already been executed.

To have the bind operation actually call this function you pass the method name. eg:

def onReturn(ev):
    # call the indiv_DTR function

root.bind('<Return>', onReturn)

Or you can provide a lambda expression if you need some local variables to be captured as parameters to the event handler.

Answered By: patthoyts

Please read about posting minimal code.

In this case

    root.bind('<Return>', indiv_DTR(rightFrame, empNumberToShow,
              beginDateToShow, endDateToShow, requiredReport))

calls indiv_DTR immediately because that is what you say to do! To delay the call, you need to do the same thing you did in the Button call: prefix with lambda :. However, to actually ‘execute the Button’, which should mean to invoke its callback as if the button were pressed, you should use the .invoke method.

    root.bind('<Return>', emp_DTR.invoke)

(Notice, not emp_DTR.invoke()). This way, if you change the indiv_DTR arguments in the Button call, you will not have to remember to change them in the bind call.

A third option would be to define the callback first

def callback():
    return indiv_DTR(rightFrame, empNumberToShow,
              beginDateToShow, endDateToShow, requiredReport)

and then pass callback to both the Button and bind calls. I generally define callbacks first even when I will only pass them once to one widget constructor, unless the return expression is really short.

Answered By: Terry Jan Reedy
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.