Changing text of labels defined in a loop - python

I'm trying to make a scalable counter.
In the first window you enter how many counters you want.
In the second window there are labels and buttons used to add one to the label.
Here is my code:
from tkinter import *
root = Tk()
def newWindow():
window = Toplevel()
for i in range(int(textbox.get())):
exec("global label"+ str(i))
exec("label" + str(i) + " = Label(window, text = '0')")
exec("label" + str(i) + ".grid(row = 0, column = i)")
exec("global button"+ str(i))
exec("button" + str(i) + " = Button(window, text = 'Add', command = lambda: setText(label" + str(i) + "))")
exec("button" + str(i) + ".grid(row = 1, column = i)")
def setText(label):
label.config(text = str(int(label.cget("text")) + 1))
textbox = Entry(root)
textbox.grid(row = 0)
submitButton = Button(root, text = "Submit", command = newWindow)
submitButton.grid(row = 0, column = 1)
root.mainloop()
However this is the error I get:
name 'label_' is not defined
where _ is i.
Making them global didn't fix this either.
Help please!

If you're using exec in this way, you're doing something very wrong.
The simple solution is to add widgets to a list or dictionary. Though, in this specific case you don't need that because you're never referencing the label anywhere but in the button command.
Here's a working example:
from tkinter import *
root = Tk()
def newWindow():
global labels
window = Toplevel()
labels = {}
for i in range(int(textbox.get())):
label = Label(window, text='0')
button = Button(window, text='Add', command = lambda l=label: setText(l))
label.grid(row=0, column=i)
button.grid(row=1, column=i)
# this allows you to access any label later with something
# like labels[3].configure(...)
labels[i] = label
def setText(label):
label.config(text = str(int(label.cget("text")) + 1))
textbox = Entry(root)
textbox.grid(row = 0)
submitButton = Button(root, text = "Submit", command = newWindow)
submitButton.grid(row = 0, column = 1)
root.mainloop()
If you wanted to make use of labels, you could have your button pass in the index, and let setText get the widget from the dictionary:
def setText(i):
label = labels[i]
label.configure(...)
...
button = Button(..., command=lambda i=i: setText(i))

Related

Tkinter Stop Frame from Overlapping

I am trying to display the data from a file and a button and then once the button is clicked, display the new data from a new file along with a button. You can see my print statements in my attempt to debug this. When I run the program, there is output, and it correctly displays a # and a button. However, the # that is displayed is from the last file I have (file #3) instead of from file #1. I believe that file #1 was covered by file #2 which then got covered by file #3. All of this happened without any button being clicked. How can I make the program wait until the button is clicked before displaying the new # and button?
window = Tk()
def clicked():
top = Toplevel(window)
top.geometry('300x300')
popLabel = Label(top, text = "E")
popLabel.place(relx = 0.5, rely = 0.5, anchor = 'center')
for widgets in frame1.winfo_children():
widgets.destroy()
for x in range(1,4):
fileName = "file" + str(x) + ".json"
print(fileName)
frame1 = LabelFrame(window, width = 300, height = 300, padx=10,pady=5)
frame1.grid(row= 0,column=0)
with open(fileName) as f:
data = json.load(f)
#print(data)
num = "#" + data.get("id")
print(num)
numLabel = Label(
frame1,
text = num
).grid(row = 1, column = 1)
firstButton = Button(
frame1,
text = "A",
command = clicked
).grid(row = 2, column = 1, sticky = 's')
window.mainloop()
I guess your real problem was that you overwrite the frame every time in the loop. So define your frame before the loop and set the column number as a variable.
import tkinter as tk
window = tk.Tk()
def clicked(number):
top = tk.Toplevel(window)
top.geometry('300x300')
fileName = "file" + str(number)+ ".json"
# with open(fileName) as f:
# data = json.load(f)
data = "data"
num = "#" # data.get("id")
popLabel = tk.Label(top, text = fileName)
popLabel.place(relx = 0.5, rely = 0.5, anchor = 'center')
#for widgets in frame1.winfo_children():
# widgets.destroy()
frame1 = tk.LabelFrame(window, width = 300, height = 300, padx=10,pady=5)
frame1.place(relwidth = 1, relheight= 1)
for x in range(1,4):
num = "#"
numLabel = tk.Label(
frame1,
text = num
).grid(row = 1, column = x)
firstButton = tk.Button(
frame1,
text = "A_{}".format(x),
command= lambda x =x: clicked(x)
).grid(row = 2, column = x, sticky = 's')
window.mainloop()
EDIT:
In this case, I would put the open command in the checked function and tell them the number of parameters to load.

Clearing a list when tkinter button is clicked

I have a piece of code below, I have a button (prnt_button) that must empty a list and a text box, but unfortunately, it empties only the text box, how could I change the code, so that the button empties also the list?
# This is in a for loop, you press a button to go to the next step,
# every step you comes along the next piece of code:
if k == 3:
doc = DocxTemplate("template.docx")
context = { # Fill in some template variables
}
doc.render(context)
docin = f"{numlist[0]}_{numlist[1]}_{numlist[2]}"
locdoc = f'{text}\\' + docin
doc.save(f"{locdoc}.docx")
prntng_l.append(docin)
# and more things, which work fine.
k = 1
else:
k = k + 1
maken_files_not_printed = Text(mult_prnt_frame1, width=column_width, height=10, state='disabled')
maken_files_not_printed.grid(row=1, column=0)
def print_all_files_in_list(prntng_l):
maken_files_not_printed.config(state="normal")
maken_files_not_printed.delete(1.0, 'end')
maken_files_not_printed.config(state="disabled")
prnt_button.config(text="Print: 0", state='disabled')
prntng_l = []
return (prntng_l)
prnt_button = Button(mult_prnt_frame1, text="Print 0", width=column_width + 2, height=1,
command=lambda: print_all_files_in_list(prntng_l), state='normal')
prnt_button.grid(row=0, column=0)
maken_files_not_printed.config(state='normal')
maken_files_not_printed.delete(1.0, 'end')
for q in range(len(prntng_l)):
prnt_button.config(text="Print: " + str(q + 1), state="normal")
maken_files_not_printed.insert('end', "Naam bestand " + str(q + 1) + ":\n" + prntng_l[q] + "\n")
maken_files_not_printed.config(state='disabled')
Could someone please help me? Thanks in advance.

How to view and unview a password in tkinter

I have a program (below) that creates an entry box that shows only '*' when you type in it and it creates a button next to the text box. I want the button to show what has been typed in the entry box while it is being pressed. I have tried to use <Button-1> and <ButtonRelease-1> but to no avail.
import tkinter as tk
def myFunc() :
#Something#
pass
root = tk.Tk()
root.title('Password')
password = tk.Entry(root, show = '*')
password.grid(row = 0, column = 1)
password_label = tk.Label(text = 'Password:')
password_label.grid(row = 0, column = 0)
button = tk.Button(text = '.', command = myFunc)
button.grid(row = 0, column = 2)
Try this:
import tkinter as tk
def show_stars(event):
password.config(show="*")
def show_chars(event):
password.config(show="")
root = tk.Tk()
root.title("Password")
password = tk.Entry(root, show="*")
password.grid(row=0, column=1)
password_label = tk.Label(text="Password:")
password_label.grid(row = 0, column=0)
button = tk.Button(text="Show password")
button.grid(row=0, column=2)
button.bind("<ButtonPress-1>", show_chars)
button.bind("<ButtonRelease-1>", show_stars)
Using the bindings that #BryanOakley suggested
Only if button is pressed it changes the text from * to the text entered.
import tkinter as tk
def myFunc():
#Something#
if password.cget("show")=="*":
password.configure(show="")
elif password.cget("show") =="":
password.configure(show="*")
root = tk.Tk()
root.title('Password')
password = tk.Entry(root, show = '*')
p = password
password.grid(row = 0, column = 1)
password_label = tk.Label(text = 'Password:')
password_label.grid(row = 0, column = 0)
button = tk.Button(text = '.', command = myFunc)
button.grid(row = 0, column = 2)
root.mainloop()

Get variable value created inside a function

I want to get a variable from a function to use this variable in another function.
Simple example:
from tkinter import *
def test():
intro = "I am "
name = "Phil"
text = intro + name
def printresult():
print(text)
root = Tk()
root.title("Test")
testbutton = Button(root, text="Test", command = test)
printbutton = Button(root, text="print Test", command = printresult)
testbutton.grid(row = 1, column = 0)
printbutton.grid(row = 1, column = 1)
mainloop()
If I press the testbutton and afterwards the printbutton, then I get the error "name 'text' is not defined".
So how am I able to get the text variable from the def test() to use it in the def printresult()?
You need to save the value somewhere well known:
from tkinter import *
def test():
intro = "I am "
name = "Phil"
text = intro + name
test.text = text # save the variable on the function itself
def printresult():
print(test.text)
root = Tk()
root.title("Test")
testbutton = Button(root, text="Test", command = test)
printbutton = Button(root, text="print Test", command = printresult)
testbutton.grid(row = 1, column = 0)
printbutton.grid(row = 1, column = 1)
mainloop()
Since you are using tkinter, I'd use a StringVar to store the result. Using a string var makes it easy for other tkinter widgets to use the value.
from tkinter import *
def test():
intro = "I am "
name = "Phil"
text.set(intro + name)
def printresult():
print(text.get())
root = Tk()
root.title("Test")
text = StringVar()
testbutton = Button(root, text="Test", command = test)
printbutton = Button(root, text="print Test", command = printresult)
testbutton.grid(row = 1, column = 0)
printbutton.grid(row = 1, column = 1)
mainloop()

Random tkinter window opening on if/else statement

I'm wondering if I got my if else statement wrong or if its a tkinter issue. I want it so that if a 0 is left in any or all boxes, it gives an error message. But after the error message is closed, it opens a random blank window. This is my code. The specific area is the if else statement within the function valueget()
import tkinter as tk
def mainwindow():
mainwindow = tk.Tk()
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
tk.Label(mainwindow, text = 'Enter a', font = ('verdana'), bg='#aaf0d1').grid(row=0)
tk.Label(mainwindow, text = 'Enter b', font = ('verdana'), bg='#aaf0d1').grid(row=1)
tk.Label(mainwindow, text = 'Enter c', font = ('verdana'), bg='#aaf0d1').grid(row=2)
getA = tk.IntVar()
aBox = tk.Entry(mainwindow, textvariable = getA, width=3, bg='#aaf0d1')
aBox.grid(row=0, column=1)
aBox.config(highlightbackground='#aaf0d1')
getB = tk.IntVar()
bBox = tk.Entry(mainwindow, textvariable = getB, width=3, bg='#aaf0d1')
bBox.grid(row=1, column=1)
bBox.config(highlightbackground='#aaf0d1')
getC = tk.IntVar()
cBox = tk.Entry(mainwindow, textvariable = getC, width=3, bg='#aaf0d1')
cBox.grid(row=2, column=1)
cBox.config(highlightbackground='#aaf0d1')
button = tk.Button(mainwindow, text='Obtain roots', command = lambda: valueget(), font = ('verdana'), highlightbackground='#aaf0d1')
button.grid(row=4)
button.config(bg='#aaf0d1')
def valueget():
readA = getA.get()
readB = getB.get()
readC = getC.get()
intA = int(readA)
intB = int(readB)
intC = int(readC)
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
messagewindow = tk.Tk()
messagewindow.geometry('290x50')
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
if readA == 0 or readB==0 or readC==0 or (readA==0 and readB==0 and readC==0):
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
messagewindow.mainloop()
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
mainwindow.mainloop()
def startup():
startpage = tk.Tk()
startpage.title('Solver')
photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage, image = photo, command = lambda: [startpage.destroy(), mainwindow()])
coverbutton.pack()
coverbutton.configure(highlightbackground='#aaf0d1')
startpage.mainloop()
startup()
Here's a basic idea of what I would do:
import tkinter as tk
from tkinter import messagebox
def mainwindow(root):
# Creates a toplevel window
mainwindow = tk.Toplevel()
mainwindow.protocol("WM_DELETE_WINDOW", root.destroy) # This overrides the "X" being clicked to also destroy the root window.
root.withdraw() # "Hides" the root window, leaving it (and mainloop) running in the background.
mainwindow.title('Enter values')
mainwindow.geometry('160x110')
mainwindow.config(bg='#aaf0d1')
# Since all three of the labels/entries are the same
# we can save space by generating them in a loop
entry_items = ('Enter a', 'Enter b', 'Enter c')
values = []
for x, item in enumerate(entry_items): # Using enumerate and x to assign rows
tk.Label(mainwindow, text = item,
font = ('verdana'), bg='#aaf0d1').grid(row=x) # Row assigned to x.
values.append(tk.StringVar()) # Appended StringVar to list.
tk.Entry(mainwindow,
textvariable = values[-1], # Uses the last value appended to the values list.
highlightbackground='#aaf0d1',
width=3,
bg='#aaf0d1').grid(row=x, column=1) # Row assigned to x.
tk.Button(mainwindow,
text='Obtain roots',
command = lambda vals = values: valueget(vals), # Here the button command is assigned with the values list
font = ('verdana'), bg='#aaf0d1',
highlightbackground='#aaf0d1').grid(row=3) # we know there are 3 items before this.
mainwindow.lift() # This is a method of bringing a window to the front
def valueget(vals):
# This line gets the values from the StringVars, converts them to ints,
# and returns them to their respective variables.
try:
readA, readB, readC = [int(val.get()) for val in vals]
except ValueError:
messagebox.showerror(title="Number Error", message='Values must be numbers')
return
# Here the variables are checked to see if they are 0
# Since each one is being checked if it is 0, there is no need to check if they are all 0.
for val in (readA, readB, readC):
if val == 0:
# If they are 0, shows an error message
messagebox.showerror(title="Zero Error", message='Values must not be zero')
return
# Creates a toplevel to display the results
messagewindow = tk.Toplevel()
messagewindow.title('Roots of the equation')
messagewindow.config(bg='#aaf0d1')
negroot = (readB**2)-(4*readA*readC)
quadformulaplus = (-readB + (pow(negroot,0.5)))/(2*readA) #quad forumla
quadformulaminus = (-readB - (pow(negroot,0.5)))/(2*readA) #quad forumla
tk.Label(messagewindow,
text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}',
bg='#aaf0d1',
font = ('verdana')).pack(padx = 5, pady = 2)
tk.Button(messagewindow,
text='Close',
command = messagewindow.destroy, # There is no need for a lambda for this.
font = ('verdana'),
bg = '#aaf0d1',
highlightbackground='#aaf0d1').pack(padx = 5, pady = 2)
# print(f'the roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}')
messagewindow.lift() # This is a method of bringing a window to the front
def startup():
startpage = tk.Tk()
startpage.title('Solver')
# COMMENTED OUT FOR TESTING
#photo = tk.PhotoImage(file = r"/Users/isa/Desktop/DiffEqns/cover.png") #image load
coverbutton = tk.Button(startpage,
# COMMENTED OUT FOR TESTING
#image = photo,
text = "TESTING", # HERE FOR TESTING
highlightbackground='#aaf0d1',
command = lambda root = startpage: mainwindow(root)).pack() # Passes the startpage to the mainwindow function.
startpage.mainloop() # The only mainloop you need.
startup()
I would recommend to improve the readability of the if-else statement for a start.
coefficients = [readA, readB, readC]
if sum(coefficients): # If they all are all zeros this will be False
if min(coefficients): # If any one is zero, this will be False
label = tk.Label(messagewindow, text = f'The roots are {quadformulaplus:.1f} and {quadformulaminus:.1f}', bg='#aaf0d1', font = ('verdana'))
label.grid(row=1)
closebutton = tk.Button(messagewindow, text='Close', command = lambda: messagewindow.destroy(), font = ('verdana'), highlightbackground='#aaf0d1')
closebutton.grid(row=2)
closebutton.config(bg='#aaf0d1')
else:
errorwindow = tk.messagebox.showerror(message='none').pack()
else:
errorwindow = tk.messagebox.showerror(message='none').pack()

Categories