I'm writing a simple code for a GUI using tkinter. My problem is that I want to have a number, printed in the Label named t1, always updated as the sum of the two entries given. Of course I cannot use the .get method on the entries, since I would fix the values when calling the method, but I don't know hot to build a new (always updated) IntVar using other Intvar.
from tkinter import *
window=Tk()
p1_in=StringVar()
p1=Entry(window,textvariable=p1_in)
p2_in=StringVar()
p2=Entry(window,textvariable=p2_in)
t1=Label(window,textvariable=(p1_in+p2_in)) # of course this doesn't work
t1.grid(row=7,column=2)
window.mainloop()
How can I make the label t1 being always updated with the sum of p1_in+p2_in?
I know that they are StringVar, but the output is nicer for my intents this way, plus I don't think this is the main issue
You can use trace method of StringVar. It is called right after the value changes.
from tkinter import *
window=Tk()
def calculate(*args):
if p1_in.get() and p2_in.get(): #checks if both are not empty
try:
ans = int(p1_in.get()) + int(p2_in.get())
t1_out.set(str(ans))
except ValueError:
t1_out.set("Enter integers!")
p1_in=StringVar()
p1=Entry(window,textvariable=p1_in)
p1_in.trace("w", calculate)
p2_in=StringVar()
p2=Entry(window,textvariable=p2_in)
p2_in.trace("w", calculate)
t1_out=StringVar()
t1=Label(window,textvariable=t1_out) #also note that used another variable for output
t1.grid(row=7,column=2)
p1.grid(row=5,column=2)
p2.grid(row=6,column=2)
window.mainloop()
Related
from tkinter import *
from tkinter.ttk import *
root = Tk()
first_run = True
def update(txt):
global first_run
text1 = Label(root, text='')
if first_run:
text1.pack()
text1['text'] = txt
first_run = False
update('1')
update('2')
update('3')
root.mainloop()
When I run this, the text stays at '1', and the following 2 function calls are ignored. I find out that only if I use pack() again then it will be updated, but it creates a duplicate label and I do not want that.
Of course, I know that I am supposed to use a StringVar, but I have been using this method for all other widgets (buttons, label frames etc) and all of them works. I do not know why this particular case does not work.
Running on Python 3.9.9 on Windows 11
You aren't updating the label, you are creating a new label each time the function is called. To update any widget, use the configure method. For that, you need to create the label outside of the function (or, leave it in the function but add logic so that it's only created once). Usually it's best to create it outside the function so that the function is only responsible for the update.
from tkinter import *
from tkinter.ttk import *
root = Tk()
def update(txt):
text1.configure(text=txt)
text1 = Label(root, text='')
text1.pack()
update('1')
update('2')
update('3')
root.mainloop()
Note: since you call your function multiple times before the window is drawn you'll only see the final value. There are plenty of solutions to that on this site. Without knowing more about what your real program looks like it's hard to recommend the best solution to that problem.
This question already has an answer here:
Python tkinter listbox bind on <Button-1> only works on second click
(1 answer)
Closed 1 year ago.
I was creating simple listbox containing numbers from 0 to 9. I wanted to print number when it get clicked so i bind list box with Button-1. Im facing problem that is when ever i select any number and try to get its location using list_box.curselection() it does not print any thing(return empty tuple), if i click on on any other number then it print previous selected number. I want to get current selected number.
from tkinter import *
root = Tk()
root.title("test listbox")
list_box = Listbox(root)
list_box.pack()
for i in range(0,10):
list_box.insert("end",i)
def def_fun(event):
print(list_box.curselection())
list_box.bind("<Button-1>",def_fun)
root.mainloop()
You don't have to bind to <Button-1> or anything, there is a virtual event with Listbox that you can use here:
def def_fun(event):
print(event.widget.curselection()) # The widget that triggers the event is event.widget
list_box.bind("<<ListboxSelect>>",def_fun) # Gets triggered each time something is selected
Just in case you are wondering why the Button-1 did not work, it is because there is a delay, the delay might be due to binding order, you can read more about it here but here is a gist:
In the default case, your binding on <Key> happens before the class binding, and it is the class binding where the text is actually inserted into the widget. That is why your binding always seems to be one character behind.
Change the binding to releasing mouse button, this will also be more user friendly (for example if they accidentally clicked on a selection they didn't want to select, they can move their mouse to a one they want and only releasing will call the function):
from tkinter import Tk, Listbox
def def_fun(event=None):
print(list_box.curselection())
root = Tk()
root.title("test listbox")
list_box = Listbox(root)
list_box.pack()
for i in range(0, 10):
list_box.insert("end", i)
list_box.bind("<ButtonRelease-1>", def_fun)
root.mainloop()
Another option if you want to call the function on select is either use #CoolCloud answer or you can also set a delay like this (although it will most certainly work in 99.9% of cases, there might be a case where it doesn't):
list_box.bind("<Button-1>", lambda e: root.after(10, def_fun))
The reason is that .curselection() gets the current selection but Button-1 is triggered before anything gets selected so it will print the previous selection because that is what was selected before and where the current selection is now, and then immediately after this, it will move the current selection to the item you clicked.
Important (because it may cause hard-to-debug issues):
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
Also:
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Don't have space around = if it is used as a part of keyword argument (func(arg='value')) but use if it is used for assigning a value (variable = 'some value'). Have two blank lines around function and class declarations.
here is the code
import Tkinter
import Tkinter as Tk
from Tkinter import *
calc_window=Tk()
calc_window.title("Calculator")
# the function is the problem
text=StringVar()
def click(nums):
text.set(nums)
frame=Frame(calc_window)
frame.grid()
entry=Entry(frame, textvariable="text")
entry.grid(column=0,row=0)
#creating buttons
nums="C+-/789*4561230="
b=0
buttonList=[]
for r in range(2,6):
for c in range(2,6):
buttonList.append(Button(frame, text=nums[b],command=click))
buttonList[b].grid(row=r, column=c)
b+=1
calc_window.mainloop()
There are a few mistakes, but nothing too major.
Only one import of Tkinter is necessary, importing it as tk is common practice.
In the click function, you'll probably want to add the string value to what is currently in the StringVar.
For the Entry the StringVar variable text is desired, not the string "text".
The main problem, when storing variables in the loop, the command will need a lambda that stores the value currently in the variable during that iteration of the loop.
Example:
import Tkinter as tk # only one Tkinter import is needed.
calc_window=tk.Tk()
calc_window.title("Calculator")
# the function is the problem
text=tk.StringVar()
def click(nums):
#text.set(nums)
text.set(text.get() + nums) # Need to get what is currently in the variable, then add to it.
frame=tk.Frame(calc_window)
frame.grid()
entry=tk.Entry(frame, textvariable=text) # the variable text, not the string "text".
entry.grid(column=0,row=0)
#creating buttons
nums="C+-/789*4561230="
b=0
buttonList=[]
for r in range(2,6):
for c in range(2,6):
buttonList.append(tk.Button(frame,
text=nums[b],
# A lambda is needed here to store the current iteration value.
command=lambda n = nums[b]: click(n)))
buttonList[b].grid(row=r, column=c)
b+=1
calc_window.mainloop()
There is still a bunch of work to go, but this should get you back on track.
I am new to tkinter. I want to write two numbers in two different entries in GUI and see their updated subtraction result on the display. here is my code:
from tkinter import *
window = Tk()
lb1 = Label(window,text="variable 1")
lb1.pack()
name1=IntVar()
en1=Entry(window, textvariable=name1)
en1.pack()
lb2 = Label(window,text="variable 2")
lb2.pack()
name2=IntVar()
en2=Entry(window, textvariable=name2)
en2.pack()
subt=IntVar()
subt=name1.get()-name2.get()
label_subt=Label(window, text=subt).pack()
how can I update label_subt?
You change the subt variable to the result of the subtraction before actually setting it to the label. Don't do that! Also, you set it as the text, not as the textvariable.
subt = IntVar()
Label(window, textvariable=subt).pack()
(Note that the result of pack() is not the Label, but None, so either move it to a separate line, as you did before, or just don't bind it to a variable that you never need anyway.)
Next, you can define a callback function for updating the value of the subt variable using the set method and bind that callback to any key press. You might want to narrow this down a bit, though.
def update(event):
subt.set(name1.get() - name2.get())
window.bind_all("<Key>", update)
You could try calling the config method on the label after every subtraction. You’ll have to use the entry.get() method to get the string of each entry. And don’t forget to use int() to convert it to an integer so you can do your subtraction otherwise you’ll get an error
label_subt.config(text=result)
How to create multi-lines in an entry widget in tkinter and use those inputs to create something?
For example, I want a textbox widget to come up and ask the user:
How many squares do you want? (ex: 4x4, 5x5)
What color do you want them?
And with the users input, I would like to create that many x-amount of squares in that specific height/width and specify the colors etc.
I am totally new to tkinter and I'm not really sure how to approach this.
I tried using this, but i'm not really sure how to add more lines and to use the values inputted.
import tkinter
from tkinter import *
class Squares:
root = Tk()
root.title('Random')
x = Label(text='How many squares? (ex: 4x4, 5x3)').pack(side=TOP,padx=10,pady=10)
Entry(root, width=10).pack(side=TOP,padx=10,pady=10)
Button(root, text='OK').pack(side= LEFT)
Button(root, text='CLOSE').pack(side= RIGHT)
You have a number of problems here.
I'm not sure what the Squares class is supposed to be doing, but it's basically not doing anything. You have a bunch of code that runs when you define the class, creating a few variables (which will end up as class attributes, shared by all instances of the class), and… that's it. Rather than try to figure out what you're intending here, I'm just going to scrap the class and make it all module-level code.
You never call root.mainloop(), so your program will just define a GUI and then never run it.
You don't bind your buttons to anything, so there's no way they can have any effect. You need to create some kind of function that does something, then pass it as the command argument, or .bind it later.
You don't store references for any of your controls, so there's no way to access them later. If you want to get the value out of the entry, you need some way to refer to it. (The exception is your x variable, but that's going to be None, because you're setting it to the result of calling pack on the Label, not the Label itself.)
Once you've done that, you just need to parse the value, which is pretty easy.
Putting it all together:
import tkinter
from tkinter import *
root = Tk()
root.title('Random')
Label(text='How many squares? (ex: 4x4, 5x3)').pack(side=TOP,padx=10,pady=10)
entry = Entry(root, width=10)
entry.pack(side=TOP,padx=10,pady=10)
def onok():
x, y = entry.get().split('x')
for row in range(int(y)):
for col in range(int(x)):
print((col, row))
Button(root, text='OK', command=onok).pack(side=LEFT)
Button(root, text='CLOSE').pack(side= RIGHT)
root.mainloop()
You just have to change that print to do something useful, like creating the squares.
If you don't need an outline for the text box, create_text would be the easiest thing, even though it doesn't have a wrap text feature(at least, in python 3 you can do this):
from tkinter import *
tk = Tk()
canvas = Canvas(tk, 1000, 1000)
canvas.pack()
canvas.create_text(200, 200, text="Example Text")
Try it!