tkinter multiple buttons using lambda function [duplicate] - python

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()

Related

tkinter Menu, multiple entries always calling the same callback [duplicate]

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.

how to change values ​in buttons done in a loop Tkinter [duplicate]

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
How to pass arguments to a Button command in Tkinter?
(18 answers)
Closed 6 months ago.
how to change values ​​in buttons done in a loop?
I mean a different text, a command for each of the resulting buttons.
the program counts files in a given folder, then this value is passed to the loop that displays the appropriate number of buttons.
here is the sample text:
files = os.listdir(".")
files_len = len(files) - 1
print(files_len)
def add():
return
for i in range(files_len):
b = Button(win, text="", command=add).grid()
You don't really need to change the Button's text, because you can just put the filename in when they're created (since you can determine this while doing so).
To change other aspects of an existing Button widget, like its command callback function, you can use the universal config() method on one after it's created. See Basic Widget Methods.
With code below, doing something like that would require a call similar to this: buttons[i].config(command=new_callback).
To avoid having to change the callback function after a Button is created, you might be able to define its callback function inside the same loop that creates the widget itself.
However it's also possible to define a single generic callback function outside the creation loop that's passed an argument that tells it which Button is causing it to be called — which is what the code below (now) does.
from pathlib import Path
import tkinter as tk
win = tk.Tk()
dirpath = Path("./datafiles") # Change as needed.
buttons = []
def callback(index):
text = buttons[index].cget("text")
print(f"buttons[{index}] with text {text!r} clicked")
for entry in dirpath.iterdir():
if entry.is_file():
button = tk.Button(win, text=entry.stem,
command=lambda index=len(buttons): callback(index))
button.grid()
buttons.append(button)
win.mainloop()
Try this. By making a list.
files = os.listdir(".")
files_len = len(files) - 1
print(files_len)
def add():
return
b=[]
for i in range(files_len):
b[i] = Button(win, text="", command=add).grid(row=i)

python delay function call [duplicate]

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"))

How can I pass an argument to a Tkinter `bind` callback? [duplicate]

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"))

Python 3.4.2 tkinter Menu Auto Calling Function [duplicate]

This question already has answers here:
All tkinter functions run when program starts
(2 answers)
Closed 7 years ago.
I'm very new to python's tkinter gui and I am trying to use it to build a basic test.
I created the menu where one of the menu items must call a function although when I run the program I can see the output from the function before the menu item has been clicked and when the menu item is clicked it does not call the function.
My code is as follows
from tkinter import *
class cl_main():
def __init__(self, master):
lo_mainmenu = Menu(master)
lo_mainmenu.option_add('*tearOff', FALSE)
master.config(menu=lo_mainmenu)
lo_menugroup = Menu(lo_mainmenu)
lo_mainmenu.add_cascade(label="MenuGroup")
lo_menugroup.add_command(label="Command", command=f_message())
def f_message():
print ("This Function Has Been Called")
root = Tk()
co_main = cl_main(root)
root.mainloop()
I can't see what is wrong with it but I'm sure there is something horribly wrong here
lo_menugroup.add_command(label="Command", command=f_message())
callbacks shouldn't have parentheses. As it is, f_message is called right away and its return value is assigned to command, rather than the function object itself.
lo_menugroup.add_command(label="Command", command=f_message)

Categories

Resources