This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 5 years ago.
Hi I was trying to make simple calculator using tkinter gui with python.
But first of all I was trying to create buttons that once they are clicked, they were attached to the result shown on the screen. (Just as real-life calculator, if screen shows 12 and I click 3, the screen then shows 123)
from Tkinter import *
class Calculator(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title('Calculator')
self.pack()
self.screen=Frame(self, borderwidth=20, relief=SUNKEN)
self.screen.pack()
self.txtDisplay = Text(self.screen, width=20, height=2)
self.txtDisplay.pack()
self.screen.grid(row=0, columnspan=5) #columnspan to span multiple columns
Contents = ['1','2','3','+','4','5','6','-','7','8','9','*','C','0','=','/']
Buttons = [None]*16
j=1
count=0
for i in range(16):
Buttons[i]=Button(self, text=Contents[i], command=lambda : self.txtDisplay.insert(END, Contents[i]), height=2, width=5, borderwidth=5)
Buttons[i].grid(row=j, column=i%4)
count+=1
if count%4==0:
j+=1
count=0
Calculator().mainloop()
However, the problem is the screen only attaches / whenever I click any button and eventually results in //////////////
/ is last element of Contents list and I guess there is something wrong with
command=lambda : self.txtDisplay.insert(END, Contents[i])
Can I get explanation on why this occurs and how I can deal with it?
Very problem problem with lambda in for loop. You can find many answers.
lambda doesn't use value from i when you create button but when you press button - so all buttons use the same value i which directs to last button.
You have to assign i to argument in lambda and it will copy value from i when you create button
command=lambda x=i: self.txtDisplay.insert(END, Contents[x])
Related
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 1 year ago.
I'm using tkinter and this problem bothers me a lot.
My goal:
Create several buttons
When clicking a specific button, change the text shown on that button
I tried the codes below, but no matter what button I clicked, only the last one got changed.
import tkinter as tk
list1 = []
def update_btn_text(index):
list1[index].set('new')
for i in range(10):
btn_text = tk.StringVar()
list1.append(btn_text)
btn = tk.Button(root, textvariable=list1[i], command=lambda: update_btn_text(i))
btn_text.set('button' + str(i+1))
btn.pack()
root.mainloop()
I guess the problem is because of the i value, but how can I fix it?
Thanks a lot!
You have to create a local variable for every iteration and pass it as a value in updatd_btn_text()
command=lambda i=i: update_btn_text(i)
Also you could try this for configuring text of the button
def update_btn_text(index,button):
button.config(text=list1[index])
for i in range(10):
btn = tk.Button(root)
btn.pack()
btn.config (command=lambda i=i, btn=btn: update_btn_text(i, btn))
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)
How to pass arguments to a Button command in Tkinter?
(18 answers)
Closed 2 years ago.
I am trying to learn how to use tkinter and encountered a problem with buttons. What I think is happening is when I start the program it automatically calls the functions of the buttons whenever they are initialized.
code in question:
import tkinter as tk
test = tk.Tk()
x = lambda a: int((a - (a) % 3) / 3)
for i in range(9):
frame = tk.Frame(
master=test,
padx=1,
pady=1,
borderwidth=1
)
frame.grid(row=x(i), column=(i % 3))
button = tk.Button(
text=i,
master=frame,
width=10,
height=5,
bg="#333333",
fg="#ffffff",
command=print(i)
)
button.pack()
test.mainloop()
All functions with () will be executed immediately. Try to wrap your function print(i) into lambda function like that command=lambda _: print(i) and call it with command()
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 years ago.
I am writing a GUI using tkinter, and I have several lines consisting of a Entry widget inline with a Button widget. When I press the button, the callback opens the askdirectory dialog, and the directory choosen is written in the Entry widget, from the same line as the button clicked.
To get to a more elegant solution than just copy/paste 12 times the code, I wrote the following:
for i in range (0,12):
e[i] = Entry(master)
e[i].insert(0, "<Choose a directory...>")
e[i].config(width=70)
e[i].grid(row=i+2, column=0, sticky=NW, columnspan=2)
b[i] = Button(master, text="Choose directory", command=lambda: self.getDir(e[i]))
b[i].grid(row=i+2, column=2, sticky=N)
And the callback function:
def getDir(self, e):
'''Retrieve directory and write it on the right "Entry" widget'''
dirName = askdirectory(title='Choose a directory:')
e.delete(0, END)
e.insert(0, dirName)
When I run the script, I get the "list index out of range" error.
From my understanding, it is because i - which is used later on the code -, is not the index anymore, only in the moment I am building the buttons, right?
So, what should I do to make the callback understand which Button was clicked and to which Entry it should write?
I can only guess since you didn't post the error message:
could simply be the lenght of e and b! e and b need to be lists or tuples with at least the lenght of 12...or if you just want to process your code 12 times you dont need the [i] at all.
I would have posted this as a comment but I don't have enough points to do so :(
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 make a button that displays a label when clicked. Here was my thinking:
from Tkinter import *
def write_hello():
w = Label(root, text = "Hello. This is a label.")
w.pack()
root = Tk()
button = Button(root, text = "Hello!", command = write_hello() )
button.pack()
root.mainloop()
Can anyone be nice enough to walk me through this? I'm very new.
You're calling the write_hello function before you even create the button, so what you're probably seeing is the label showing up before the button on the UI (and without clicking it). What you want to do is pass the function to the Button constructor instead of the function's return value:
button = Button(root, text = "Hello!", command = write_hello)
This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 6 months ago.
I am writing a GUI with Python TKinter where I have a grid of some 24 buttons which I have created using a loop (not individually). Is there any way in which I can get the Text of the button that I pressed.
Since it is in loop, a callback function even with lambda is not helping me. I don't wish to write separate code for, what happens when each different button is pressed. I just need to know the text of the corresponding button so that I could initiate another generic function which works with that text only.
ps: I am able to do the same task but with List and curselection() and don't want it this way.
self.num = 11
for r in range(0,5):
for c in range(0,3):
R = r; C = c
resNum = "Ch0"+str(self.num);
self.button1_rex = tk.Button(self.frame, text = resNum,font=("Helvetica", 14), justify = CENTER,width = 20, command = self.new_window)
self.button1_rex.grid(row=R, column=C, sticky=W)
self.num = self.num+1
self.new_window is the function that opens a new window and needs to do other functionalities based on the button number (like "Ch011" etc)
The simplest way is just to, when you are constructing the button, bind the name to the command, either using functools.partial or a lambda.
Using functools.partial:
self.button1_rex = tk.Button(..., command=functools.partial(self.new_window, resNum))
Using a lambda:
self.button1_rex = tk.Button(..., command=lambda r=resNum: self.new_window(r))
For more infomation about the lambda, see What is a lambda (function)? and Python tkinter creating buttons ... arguments.