I'm trying to add two integers dynamically with tkinter. My idea is to get the sum after typing in a integer.
My idea:
import tkinter as tk
root = tk.Tk()
root.geometry("600x100")
v = tk.IntVar()
w = tk.IntVar()
eingabefeld_0 = tk.Entry(master=root, textvariable = v)
eingabefeld_0.place(x=80, y=40)
eingabefeld_1 = tk.Entry(master=root, textvariable = w)
eingabefeld_1.place(x=320, y=40)
label = tk.Label(master=root, textvariable = v)
label.place(x=80, y=80)
label = tk.Label(master=root, textvariable = str(int(v.get())+int(w.get())))
label.place(x=320, y=80)
root.mainloop()
If start the program the result of the addition is not presented in the label. What is missing here?
You aren't using the textvariable option correct. The value passed to that option needs to be an instance of one of the tkinter variable objects (StringVar, IntVar, etc). You can't just pass an expression to it.
You need to write a function that calculates the result and sets the value of this variable. The function can then be called from a button, or you can set a trace on the variable and update it every time the value changes. You'll have to make sure to take care of the case where one of the values you're trying to add is not a valid integer.
For example, start by creating a new variable for the result, and using it in your label. In this case I recommend StringVar so that we can display something like "???" if we can't compute the value.
resultvar = tk.StringVar()
label = tk.Label(master=root, textvariable = resultvar)
Next, create a function that computes the result and stores it in this variable. You need to use try/except since it's possible for the user to delete everything in the entry widget in which case the call to .get() will fail.
def compute_result():
try:
result = v.get() + w.get()
resultvar.set(result)
except Exception as e:
resultvar.set("???")
Now you just need to add code that calls this function. You can do it several ways:
you can bind to the return key so that the result is updated when the user presses that key
you can bind to any key release so that the result is updated when the user presses and releases any key
you can add a button that the user has to click to update the value
you can add a trace to the input variables, which will cause a function to be called whenever the values change
To use a trace, call trace_variable telling it when to do the trace ("w" means whenever the value is written/changed), and what function to call. The trace will append some arguments to the function that we don't use, so we'll have to change the function to accept these arguments
def compute_result(*args):
try:
result = v.get() + w.get()
resultvar.set(result)
except Exception as e:
resultvar.set(f"???")
v.trace_add("write", compute_result)
w.trace_add("write", compute_result)
With that, whenever the value in the variable v or w changes, the function will be called and the result variable will be updated.
sair_grid = self.root.ids["sairler_screen"].ids["sair_grid"]
for a in dler:
with open(a,"r",encoding="utf-8") as file1:
for b in file1:
btn = Button(text=b[:-5],on_press=lambda y:self.sairi(b))
sair_grid.add_widget(btn)
def sairi(self,x):
pass
No matter which button I clicked to calling the function, it is giving the same result. I want every button to take different b values and call the function with different arguments. All buttons created with different names but each of them calls with same last argument of the file. I am new in python and kivy so every help is really appreciated.
There is only one variable named b here and it changes its value until the last one remains last time the loop executes:
for b in file1:
btn = Button(text=b[:-5],on_press=lambda y:self.sairi(b))
sair_grid.add_widget(btn)
So, you are creating many lambdas, but they all use the same b from the closure.
A typical simple workaround for that problem is to bind the current value of b to a default value of the lambda argument:
for b in file1:
btn = Button(text=b[:-5],on_press=lambda y, b=b:self.sairi(b))
sair_grid.add_widget(btn)
Now each lambda you create has a locally bound b argument which has its own default value. The default value of b is now different for each lambda you create.
I am trying to write some code that will send the value of an Entry box to a function based on a binding. I can technically get the behavior I want in the code below, but I'm a) not sure why it works and b) am quite sure I am not doing this in the most pythonic way. I'm pretty sure I'm misunderstanding the event or lambda or both.
In the code below the Entry box called input_box1 when the binding triggers, the inp_var1.get() code only gets the default value, not anything that has been entered into the box. In other words, the test1 function will print...
Test1 Foo
... no matter what you type into the entry.
The binding on input_box2 works exactly as I expect. I type anything in there and click somewhere else and it prints the new entry. However I don't understand why my lambda doesn't want an event or why I need to repeat the inp_var2.get() call.
If anyone knows what's going under the hood I'd love to hear it! Here's the code:
from tkinter import *
from tkinter import ttk
def test1(event, i):
print('Test1', i)
def test2(event, i):
print('Test2', i)
root = Tk()
title_label = Label(root, text='This does not work!')
title_label.grid(column=0, row=0)
inp_var1 = StringVar(value='Foo')
input_box1 = Entry(root, textvariable=inp_var1)
input_box1.grid(column=0, row=1)
inp_var2 = StringVar(value='Bar')
input_box2 = Entry(root, textvariable=inp_var2)
input_box2.grid(column=0, row=2)
input_box1.bind('<FocusOut>', lambda event, i=inp_var1.get(): test1(event, i))
input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))
root.mainloop()
This has very little to do with Tkinter itself. It's also not so much connected to lambda as it is to Python in general.
Take both of those out of the equation, and consider the following Python program:
def f(x=3, y=4):
print('x =', x, 'y =', y)
f(0, 0)
f(0)
f()
Assuming Python 3 (or from __future__ import print_function), when run, this prints:
x = 0 y = 0
x = 0 y = 4
x = 3 y = 4
That's because the first call to f passes 0, 0, so x and y are both bound to zero. The second call to f passes just 0 so x is bound to 0 and y is bound to its default value of 4. The third call passes nothing at all and x and y are both bound to their default values.
(So far, this should all be clear enough.)
Now let's fuss with this a bit. I'll continue to assume Python 3 so that input means what in Python 2 we have to use raw_input to achieve:
def f(x=input('enter default x: '), y=input('enter default y: ')):
print('x =', x, 'y =', y)
print('about to call f three times')
f(0, 0)
f(0)
f()
Before you run this sample program, think about what you expect it to do. Then run it—here's my result:
$ python3 t.py
enter default x: hello
enter default y: world
about to call f three times
x = 0 y = 0
x = 0 y = world
x = hello y = world
Why did this read the default values for x and y before we even called it the first time? Why didn't it read new default values for x and y on each call?
Think about that for a bit, then read on
Really, do that. Ask why the inputs happened at these odd times.
Now that you've thought about it...
It did do that, though. That's the key here. The default values for your arguments are captured at the time the def statement is executed. The def statement, which binds the name f to our function, is actually run when Python loads the file, after seeing the body of the function. The function itself is run later, after Python has gotten to the print call and then to the first f call.
A lambda in Python is just a sort of anonymous function definition. Instead of:
def square(x):
return x * x
we can write:
square = lambda x: x * x
The lambda expression defines a new function-like item, a lambda function—you can't use if/else type statements inside one of these, just expressions, and they automatically return the value of their expression. In this case our lambda function has one argument named x. The function returns x * x.
The outer assignment, square =, binds this lambda function to the name square, just as if we'd done def square(x) instead. So it's mostly just a syntactic trick: we can have a real function, like square, with arguments, or a limited lambda function that can only use expressions, like this anonymous function that we almost immediately bind to the name square.
The arguments part works the same either way though. Just as with f and input, if we bind x:
square = lambda x=3: x * x
or:
square = lambda x=int(input('choose a default for x now> ')): x * x
that happens just once, when the lambda expression itself executes. The function now has a variable x with a default value.
When, later, we call the function, we can provide a value for x, or let it default. If we don't provide a value, Python uses the default that it captured earlier, when we executed the line with lambda in it, rather than now, when we call the lambda function.
This all holds for Tkinter as well. You wrote:
input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))
but that's pretty much the same as writing:
def f(i=inp_var2.get()):
test2(i, inp_var2.get())
input_box2.bind('<FocusOut>', f)
except that you don't have to come up with the function-name f here. The lambda variant of the function has no name, but it's still just a function. (For that matter, when we do square = lambda ..., the lambda function doesn't have a name. The name square is the name of a variable, not the name of the function. The variable is merely bound to the function, so that square(10) calls it.)
Anyway, later, when Tkinter has an event that matches <FocusOut>, Tkinter calls f ... or, if you used lambda, calls your unnamed lambda function. Tkinter provides one argument to that function; the one argument it provides is the event. So your default value for i in f above is irrelevant—you could do:
def f(i=None):
test2(i, inp_var2.get())
or:
def f(i='hello world'):
test2(i, inp_var2.get())
because when Tkinter calls f, it always provides an actual argument for i.
First of all, I'm a begginer in Python, so get ready for a nooby question ))
In one of the topics on this site I've found quite a useful piece of advice concerning the use of lambda function.
Here's my code before correcting:
def entree1(self): #function that is supposed to change text in
self.configure(text = "X") the label block from whatever it is to 'X'
fen = Tk()
pole1 = Label(fen, text = '|_|')
pole1.bind("<Button-1>", lambda: entree1(pole1)) #event handler reffering to the function above
Here's my code after correcting:
def entree1(self): #function that is supposed to change text in
self.configure(text = "X") the label block from whatever it is to 'X'
fen = Tk()
pole1 = Label(fen, text = '|_|')
pole1.bind("<Button-1>", lambda x: entree1(pole1)) #event handler reffering to the function above
In a nutshell I changed lambda: some func to lambda x: some func.
And it worked, which is great, although I can't figure out the difference between this two variants.
Could you please tell me what exactly changed after I added x?
Thank you for your time!
Let me translate lambda expressions to function definitions that you are probably more used to:
lambda : entree1(pole1)
is the same as
def lambdaFunc():
global pole1
return entree1(pole1)
Your corrected function is
lambda x : entree1(pole1)
which is the same as
def lambdaFunc(x):
global pole1
return entree1(pole1)
You need the extra argument because Tk buttons call the function they are bound to with a variable (I forget what the variable is, exactly), therefore calling a function with an input variable when it doesn't take one causes errors.
I have a function, say foo and it's supposed to bind an event
def foo:
for x in widget_list:
widget.config(command = lambda: self.function(widget_list.index(x))
def function(index):
do something here....
print index
The widget list contains buttons, and whenever I click corresponding buttons, they're supposed to print to the IDLE their index, but what happens is that all the buttons I click end up printing the last index of widget_list. I'm guessing that while for iterates, the argument of the function also changes thus only the last index is preserved. Is there anyway to bind the previous indexes to the button?
The simplest fix is to make sure that your lambda is using the current value of x:
widget.config(command = lambda x=x: self.function(widget_list.index(x))
This forces x to be a default parameter to the function, and the default value will be the value of x at the time the lambda is defined.