This question already has answers here:
Tkinter binding a function with arguments to a widget
(2 answers)
Closed 8 months ago.
I have this code:
#!/usr/bin/python3
from Tkinter import *
def keypress(key):
print key, "pressed"
if __name__ == '__main__':
root = Tk()
root.bind('<Return>', keypress(key="enter"))
root.bind('a', keypress(key="a"))
root.mainloop()
I realize the function is being called as soon as the program starts; how can I make it pass the arguments to the keypress function without invoking it immediately?
In your bind function calls, you are actually calling the functions and then binding the result of the function (which is None) . You need to directly bind the functions. On solution is to lambda for that.
Example -
root.bind('<Return>', lambda event: keypress(key="enter"))
root.bind('a', lambda event: keypress(key="a"))
If you want to propagate the event parameter to the keypress() function, you would need to define the parameter in the function and then pass it. Example -
def keypress(event, key):
print key, "pressed"
...
root.bind("<Return>", lambda event: keypress(event, key="enter"))
root.bind("a", lambda event: keypress(event, key="a"))
Related
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 2 years ago.
I am configuring a tkinter Menu option on the fly, with a passed in list of menu items and callback functions.
However, whichever menu item is actually selected the same callback is called. I think it is to do with the cmd_func var used to build the lambda changing as the loop that builds the menu options iterates. The value in the lambda ends up changing as the loop iterates so all the lambda's end up pointing ot the last value cmd_func takes? However I can't see how to solve the problem? - I did try enumerating menu_cmds when building the menu, and using the index var to index directly into the array menu_cmds to get at the function value rather than having the intermediate cmd_func var, but that didnt help.
This code replicates the issue;
from tkinter import *
class a:
def __init__(self):
self.root=Tk()
self.m=[ ('name_a',self.command_a),
('name_b',self.command_b) ]
self.control_frame=Frame(self.root)
self.control_frame.pack(side=TOP,fill=X)
self.control=control(self.control_frame,self.m)
self.control.pack()
def command_a(self,data):
print("command_a %s,%s" % data)
def command_b(self,data):
print("command_b %s,%s" % data)
class control(Frame):
def __init__(self,parent,menu_cmds):
Frame.__init__(self,parent)
self.menu_cmds=menu_cmds
self.canvas=Canvas(self,width=800,height=400)
self.canvas.pack(side=LEFT,anchor=W)
self.canvas.create_rectangle(100,100,300,300,fill="#FF0000")
self.canvas.bind("<Button-3>", self.canvas_context_popup)
self.build_canvas_context_menu()
def build_canvas_context_menu(self):
self.canvas_context_menu=Menu(self.canvas, tearoff=0)
for menu_text,cmd_func in self.menu_cmds:
print("menu %s" % menu_text)
cmd=lambda : self.canvas_context_menu_select(cmd_func)
self.canvas_context_menu.add_command(label=menu_text,
command=cmd)
def canvas_context_popup(self, event):
try:
self.popup_at_x=event.x_root-self.canvas.winfo_rootx()
self.popup_at_y=event.y_root-self.canvas.winfo_rooty()
self.canvas_context_menu.tk_popup(event.x_root, event.y_root, 0)
finally:
self.canvas_context_menu.grab_release()
def canvas_context_menu_select(self,cmd_func):
x=self.popup_at_x
y=self.popup_at_y
cmd_func((x,y))
GUI = a()
GUI.root.geometry('1000x600')
GUI.root.update()
GUI.root.mainloop()
Any help much appreciated!
manveti and furas above had it right away, thankyou!
The solution is to change the lambda to be
cmd=lambda x=cmd_func : self.canvas_context_menu_select(x)
For a better explanation than I can give; refer to the comment and info linked by manveti above.
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 2 years ago.
I have been trying to create multiple buttons in tkinter library (with numbers as text), using for loop. And i want to print the text of each button every time i click one. Like a calculator. But the problem is that it prints only the last line in the loop.
win=tk.Tk()
def butt(x):
print(x)
for i in range(1,4):
bt=tk.Button(win,text=i,command=lambda: butt(i));bt.pack()
win.mainloop()
One solution of this that i found is to write lambda i=i : ... but i dont understand what this is.
Thank you for your time!!
Lambda expression here returns the method that prints the i value.
import tkinter as tk
win=tk.Tk()
def get_print_method(x):
def print_i():
print(x)
return print_i
for i in range(1,4):
bt=tk.Button(win, text=i, command=get_print_method(i))
bt.pack()
win.mainloop()
Lambda in your case is an equivalent of the get_print_method function.
This a well-known behavior in Python for lambda
This happens because i is not local to the lambdas, but is defined in the outer scope, and it is accessed when the lambda is called — not when it is defined
Python FAQ on lambdas
Their solution, is to give lambda a default parameter
import tkinter as tk
win=tk.Tk()
def butt(x):
print(x)
for i in range(1,4):
bt=tk.Button(win,text=i,command=lambda j=i: butt(j))
bt.pack()
#i = 'This is now a string'
win.mainloop()
I'm fairly new to python and tkinter. I'm working with python 2.7 and tkinter 8.5.
I'm trying to exit my app when the escape key is pressed, and I ran into some odd behaviour:
When I pass root.destoy as the argument to bind(), the app does nothing:
root = Tk()
...
root.bind('<Escape>', root.destroy)
But if I define a function that calls root.destroy() and pass that as an argument to bind, everything works as expected:
def exit_app():
root.destroy()
root.bind('<Escape>', exit_app)
It also works if I pass a lambda like this:
root.bind('<Escape>', lambda f: root.destroy())
Can anyone explain what's happening here?
Thanks
When you bind a command to an event, that command is passed an argument which is is an object that represents the event. root.destroy does not accept any arguments, therefore it is throwing an error instead of running.
That is why your lambda works: your lambda accepts an argument (oddly, named f).
You claim in your question that it works with this exact function definition:
def exit_app():
root.destroy()
I find that impossible to believe, for the same reason described above.
This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 7 years ago.
I want to delay a function call. (Or in my mind: python is executing the function in the wrong order). In the below example i could write instead of bf(arg) the two functions bf1 and bf2 and it does work as expected: The function is called whenever the button is pressed. But if i include the arguments in the function the function call is executed only once. Returning the function itself doesn't change the behaviour.
Can you please take a look at it and give me a hint where my logic or understanding of python is wrong.
from tkinter import *
def bf(str):
print(str)
# return print(str) #alternative(same behaviour)
main=Tk(screenName='screen',baseName='base')
button1=Button(master=main,text='b1',command=bf("b1"))
button2=Button(master=main,text='b2',command=bf("b2")) # runs once (and runs here)
button1.pack()
button2.pack()
mainloop()
print('end')
--google and stackoverflow search only return things like delay function call for 1 a specific time interval This is not what i am searching for. :-(
The issue is that you are calling the function when you create the buttons, instead of passing a callable that TK will call when the button is clicked. Normally you would just pass the function itself:
button1=Button(master=main, text='b1', command=bf)
but since you want to pass arguments to bf you will need to wrap it in a lambda:
button1=Button(master=main, text='b1', command=lambda: bf('b1'))
What you do in that line is that you don't pass the function but execute it:
button1=Button(master=main,text='b1',command=bf("b1"))
You could either include only the function name, but then you can't pass a parameter to the function:
button1=Button(master=main,text='b1',command=bf)
or you could make use of lambda:
button1=Button(master=main,text='b1',command=lambda:bf("B1"))
I am somewhat new to python still, and am working on a project including a GUI. I using with Tkinter and buttons a lot, and am curious if there was a way to run a function with a bind because I want one thing to happen when it is presses and something else when it is released.
s = str(x+1) + ":" + str(y+1)
img = ImageTk.PhotoImage(Image.open('button.png'))
b = Tkinter.Button(field_hid, image=img, borderwidth=0, highlightthickness=0, background='grey')
b.bind("<ButtonPress-1>", lambda s=s, button=b: location_down(event,s,button))
b.bind("<ButtonRelease-1>", lambda s=s, button=b: location_up(event,s,button))
b.img = img
b.pack()
b.grid(row=x, column=y)
I don't understand how I am to do this, as the only thing that can be passed to the function is the event, but my program requires the arguments.
The only way to use bind is to have it call a function. When you use lambda you are just creating an anonymous function. You can easily do:
b.bind("<ButtonPress-1>", self.SomeOtherFunction)
lambda is useful when you want to pass additional arguments to the function. Unlike when using the command option, with bindings you get an event object with lots of useful information so you may not need to pass any additional information.
For example, you could do this:
def OnPress(event):
print "widget %s was pressed" % event.widget
def OnRelease(event):
print "widget %s was released" % event.widget
b = Button(...)
b.bind("<ButtonPress-1>", OnPress)
b.bind("<ButtonRelease-1>", OnRelease)
For a good introduction to binding see Events and Bindings on effbot.org.