So the issue is when I click the calc_button, which calls the calculate_tip method, I get a NameError saying 'subtotal_entry' is not defined. I have tried a bunch of different things to get this working but nothing has worked so far. I am trying to get the users input from the Entry I created called subtotal_entry.
Here is my code:
from tkinter import *
from tkinter import messagebox
class TipGUI:
def __init__(self,master):
self.master = master
master.title('Tip Calculator')
#create the frames
subtotal_frame = Frame(master).grid(row=0, column=0)
percent_frame = Frame(master).grid(row=1, column=0)
tip_frame = Frame(master).grid(row=2, column=0)
button_frame = Frame(master).grid(row=3, column=0)
total_frame = Frame(master).grid(row=4, column=0)
#create the labels
subtotal_label = Label(subtotal_frame, text="Enter the amount of the ticket:").grid(row=0, column=0)
percent_label = Label(percent_frame, text="Enter the tip as a percent:").grid(row=1, column=0)
tip_label = Label(tip_frame, text="Tip Amount: $").grid(row=2, column=0)
tipAmt_label = Label(tip_frame, text="").grid(row=2, column=1)
total_label = Label(total_frame, text="Total Amount: $").grid(row=3, column=0)
totalAmt_label = Label(total_frame, text="").grid(row=3, column=1)
#create entry boxes
subtotal_entry = Entry(subtotal_frame).grid(row=0, column=1)
percent_entry = Entry(percent_frame).grid(row=1, column=1)
#create buttons
calc_button = Button(button_frame, text="Calculate", command= self.calculate_tip).grid(row=4, column=0)
exit_button = Button(button_frame, text="exit", command= master.quit).grid(row=4, column=1)
def calculate_tip(self):
subtotal = Entry.get(subtotal_entry)
percent = percent_entry.get()
if subtotal <= 0:
messagebox.showinfo("Error","Please enter a subtotal greater than 0")
if percent <= 0:
messagebox.showinfo("Error","Please enter a tip percentage greater than 0")
else:
tip = subtotal * percent
total = tip + subtotal
tipAmt_label.config(text = format(tip, ',.2f'))
totalAmt_label.config(text = format(total, ',.2f'))
subtotal_entry.delete(0, END)
percent_entry.delete(0,END)
#Create the window
window = Tk()
tipGUI = TipGUI(window)
window.mainloop()
You have few common problems.
subtotal_entry is local variable which can't be accessed in other functions. You have to use self.subtotal_entry to create variable which can be accessed in other functions in class.
variable = Widget.grid() assigns None to variable because grid(), pack(), place() returns None. You have to do it in two steps
variable = Widget()
variable.grid()
And if later you don't have to access some widget (some Labels) then you can skip variable
Widget().grid()
To get value you need subtotal_entry.get() instead of Entry.get(subtotal_entry)
Entry gives string so you have to use int() or float() to convert string to value.
Full working code.
I assumed that percent will be value 0 ... 100, not 0.00 ... 1.00
I removed most of Frames because they were useless.
import tkinter as tk # PEP8: `import *` is not preferred
from tkinter import messagebox
class TipGUI:
def __init__(self, master):
self.master = master
master.title('Tip Calculator')
#create the labels
subtotal_label = tk.Label(master, text="Enter the amount of the ticket:")
subtotal_label.grid(row=0, column=0, stick='e')
percent_label = tk.Label(master, text="Enter the tip as a percent:")
percent_label.grid(row=1, column=0, stick='e')
self.tip_label = tk.Label(master, text="Tip Amount: $")
self.tip_label.grid(row=2, column=0, columnspan=2)
self.total_label = tk.Label(master, text="Total Amount: $")
self.total_label.grid(row=3, column=0, columnspan=2)
#create entry boxes
self.subtotal_entry = tk.Entry(master)
self.subtotal_entry.grid(row=0, column=1)
self.percent_entry = tk.Entry(master)
self.percent_entry.grid(row=1, column=1)
#create buttons
frame_buttons = tk.Frame(master)
frame_buttons.grid(row=4, column=0, columnspan=2)
calc_button = tk.Button(frame_buttons, text="Calculate", command=self.calculate_tip)
calc_button.grid(row=0, column=0)
exit_button = tk.Button(frame_buttons, text="Exit", command=master.quit)
exit_button.grid(row=0, column=1)
def calculate_tip(self):
subtotal = float(self.subtotal_entry.get())
percent = float(self.percent_entry.get()) / 100
if subtotal <= 0:
messagebox.showinfo("Error","Please enter a subtotal greater than 0")
if percent <= 0:
messagebox.showinfo("Error","Please enter a tip percentage greater than 0")
else:
tip = subtotal * percent
total = tip + subtotal
self.tip_label.config(text=f"Tip Amount: ${tip:,.2f}")
self.total_label.config(text=f"Total Amount: ${total:,.2f}")
self.subtotal_entry.delete(0, 'end')
self.percent_entry.delete(0, 'end')
#Create the window
window = tk.Tk()
tipGUI = TipGUI(window)
window.mainloop()
Related
I have tried StringVar, I've tried set().
Note: The program successfully calculates the value and returns the answer in a seperate pop-up, but I would like it to display the answer in the readonly "yards" entry field as well.
import tkinter as tk
import tkinter.ttk as ttk
#Formats all of the visible elements of the tkninter GUI.Connects the buttons to their functions.
class MetersToYardsApp:
def __init__(self, parent):
topLevel = ttk.Frame(parent, padding=10)
topLevel.grid(column=0, row=0)
headerLabel = ttk.Label(topLevel, text="Meters to Yards Calculator", font="{Arial} 16 {bold}")
headerLabel.grid(column=0, row=0, sticky="nsew")
inputFrame = ttk.Frame(topLevel, padding=10)
inputFrame.grid(column=0, row=1, sticky="nsew")
metersLabel = ttk.Label(inputFrame, text="Meters:")
yardsLabel = ttk.Label(inputFrame, text="Yards:")
metersEntry = ttk.Entry(inputFrame)
yardsEntry = ttk.Entry(inputFrame, state="readonly")
metersLabel.grid(column=0, row=0, sticky="e")
yardsLabel.grid(column=0, row=1, sticky="e")
metersEntry.grid(column=1, row=0, pady=3)
yardsEntry.grid(column=1, row=1, pady=3)
buttonFrame = ttk.Frame(topLevel)
buttonFrame.grid(column=0, row=2, sticky='nsew')
clearButton = ttk.Button(buttonFrame, text="Clear", command=self.clear)
okayButton = ttk.Button(buttonFrame, text="Calculate", command=self.calculate)
clearButton.grid(column=0, row=0, padx=3)
okayButton.grid(column=1, row=0)
self.mainWindow = topLevel
self.metersEntry = metersEntry
self.yardsEntry = yardsEntry
#Clear Button
def clear(self):
# print("Clear")
self.metersEntry.delete(0, tk.END)
#Formats the Pop-up that displays the answer
def showAnswer(self, parent, text):
rootFrame = ttk.Frame(parent, padding=10)
rootFrame.grid(column=0, row=0)
headerLabel = ttk.Label(rootFrame, text="The Answer", font="{Arial} 14 {bold}")
headerLabel.grid(column=0, row=0)
answerLabel = ttk.Label(rootFrame, text=text, justify=tk.CENTER)
answerLabel.grid(column=0, row=1)
#Performs Calculations if input is valid.
def calculate(self):
# print("Calculate Meters: ", self.metersEntry.get())
try:
meters = float(self.metersEntry.get())
yards = meters * 1.094
except:
top2 = tk.Toplevel(self.mainWindow)
self.showAnswer(top2, "There was an Error.\n" + "Please Enter a Valid Number for Meters")
# print("Please Enter a Valid Number for Meters.")
return
print(meters, "Meters is ", yards, "Yards. ")
top2 = tk.Toplevel(self.mainWindow)
self.showAnswer(top2, str(meters) + " Meters is equivalent to " + "{:.2f} Yards.".format(yards))
return float(yards)
You do have to use DoubleVar to update the Entry element:
yards_entry_var = tk.DoubleVar(inputFrame, 0)
Then you should place it inside the Entry element 'textvariable':
yardsEntry = ttk.Entry(inputFrame, textvariable=yards_entry_var, state="readonly")
to use it outside the init function:
self.yards_entry_var = yards_entry_var
in the calculate function, after calculating the yards:
self.yards_entry_var.set(yards)
And finally, when you want to clear the value in the clear function:
self.yards_entry_var.set(0)
I want to get an integer from an entry field and create new entry boxes below that. I have written a code to do that using a button. However, I want to make it happen automatically without a button as I entered the number, the rows update.
I saw one way to automate it is using the callback.
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = IntVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
def update():
for i in range(1, n_para.get()+1):
entryX = Entry(root)
entryX.grid(row=i+1, column=0)
entryY = Entry(root)
entryY.grid(row=i+1, column=1)
entryZ = Entry(root)
entryZ.grid(row=i+1, column=2)
button1 = Button(root, text="update", command=update)
button1.grid(row=1, column=0)
root.mainloop()
So, I changed the code to the below one using callback.
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = IntVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
def update(*args):
try:
for i in range(1, n_para.get()+1):
entryX = Entry(root)
entryX.grid(row=i+1, column=0)
entryY = Entry(root)
entryY.grid(row=i+1, column=1)
entryZ = Entry(root)
entryZ.grid(row=i+1, column=2)
except ValueError:
return
n_para.trace_add('write', update)
root.mainloop()
When I enter a number, it works and an error raises: _tkinter.TclError: expected floating-point number but got "" which I don't know what is that for.
Also, the code only works when I put numbers in ascending format. forexample, if I first enter 5, then change it to 3 it doesn't work.
You should use StringVar to associate with the Entry, as the entry contains text.
There is a method in StringVar to trace any changes: StringVar().trace(). Se example code below:
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = StringVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
def update(*args):
print('update', n_para.get())
n_para.trace('w', update) # Trace changes in n_para and run update if detected
root.mainloop()
The error you get is because the Entry contains text. You will have to convert it to int before you use it.
New example
You could do this in many ways, but here is one example:
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = StringVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
row_list = [] # List of all currently displayed entry rows
# and each row is a list of entrys within this list
def update(*args):
try:
para = int(n_para.get())
except ValueError:
return # Return without changes if ValueError occurs
rows = len(row_list)
diff = para - rows # Compare old number of rows with entry value
if diff == 0:
return # Return without changes
elif diff > 0: # Add rows of entrys and remember them
for row in range(rows+1, rows+diff+1):
entry_list = [] # Local list for entrys on this row
for col in range(3):
e = Entry(root)
e.grid(row=row, column=col)
entry_list.append(e) # Add entry to list
row_list.append(entry_list) # Add entry list to row
elif diff < 0: # Remove rows of entrys and froget them
for row in range(rows-1, rows-1+diff, -1):
for widget in row_list[row]:
widget.grid_forget()
widget.destroy()
del row_list[-1]
n_para.trace('w', update) # Trace changes in n_para
root.mainloop()
Is that what you had in mind?
for one of my first little projects I coded a fitnesscalculator last evening. Which has 3 functionalities at the start: Calculating the 1RM, BMI and FFMI. I'm running into the problem of overlapping labels when I first calculate the 1RM and then the FFMI. I have already googled and looked for similar threads but I couldn't find an answer to my solutions as my Gui is and its functionalities are based around a combobox. Please excuse the rather messy code, but I have already tried several methods to deal with this problem
from tkinter import *
from PIL import Image, ImageTk
from tkinter import ttk
root = Tk()
root.geometry("400x400")
root.title("Fitness Calculator")
#dropdown
Options = ["1RM Calculator", "BMI Calculator", "FFMI Calculator"]
#functions
def picker(input):
global calculate_button
global bf_box
global bf_label
global result_label
global result2_label
global result3_label
calculate_button.destroy()
result_label.pack_forget()
result2_label.destroy()
result3_label.destroy()
bf_label.destroy()
bf_box.destroy()
selected = drop.get() #holt sich wert vom dropdown
####################RM CALCULATOR####################
if selected == Options[0]:
#labels
weight_label = Label(root,text="Enter your training weight here: ", padx=10, pady=10)
weight_label.grid(row=2, column=0, sticky=W)
reps_label = Label(root,text="Enter your repetitions here: ", padx=10)
reps_label.grid(row=3, column=0, sticky=W)
def calculate():
weight = int(weight_box.get())
reps = int(reps_box.get())
one_rm = round(weight*(36/(37-reps)), 2)
#Result
result_label = Label(root, text="Your 1RM is: " + str(one_rm) + " kg")
result_label.grid(row=4)
weight_box.delete(0,END)
reps_box.delete(0,END)
#Entryfields
weight_box = Entry(root)
weight_box.grid(row=2, column=1)
reps_box = Entry(root)
reps_box.grid(row=3, column=1)
#Calculate button
calculate_button = Button(root,text="Calculate",command=calculate, width=16 )
calculate_button.grid(row=4,column=1,pady=10)
####################BMI CALC####################
if selected == Options[1]:
#LABELS
weight_label = Label(root,text="Enter your weight in kg here: ", padx=10, pady=10)
weight_label.grid(row=2, column=0, sticky=W)
height_label = Label(root,text="Enter your height in cm here: ", padx=10)
height_label.grid(row=3, column=0, sticky=W)
#ENTRY BOXES
weight_box = Entry(root)
weight_box.grid(row=2, column=1)
height_box = Entry(root)
height_box.grid(row=3, column=1)
def calculate():
weight = float(weight_box.get())
height = float(height_box.get())/100
bmi = round(weight/(height**2),0)
#Result
result_label = Label(root, text="Your BMI is: " + str(bmi))
result_label.grid(row=4)
weight_box.delete(0,END)
height_box.delete(0,END)
#Calculate button
calculate_button = Button(root,text="Calculate",command=calculate, width=16 )
calculate_button.grid(row=4,column=1,pady=10)
####################FFMI####################
if selected == Options[2]:
calculate_button.destroy()
#LABELS
weight_label = Label(root,text="Enter your weight in kg here: ", padx=10,pady=5)
weight_label.grid(row=2, column=0, sticky=W)
height_label = Label(root,text="Enter your height in cm here: ", padx=10,pady=5)
height_label.grid(row=3, column=0, sticky=W)
bf_label = Label(root,text="Enter your estimated bodyfat % here: ", padx=10,pady=5)
bf_label.grid(row=4, column=0, sticky=W)
#ENTRY BOXES
weight_box = Entry(root)
weight_box.grid(row=2, column=1)
height_box = Entry(root)
height_box.grid(row=3, column=1)
bf_box = Entry(root)
bf_box.grid(row=4, column=1)
def calculate():
weight = float(weight_box.get())
height = float(height_box.get())/100
bf = float(bf_box.get())
total_bf = weight*(bf)
lean_weight = weight*(1-(bf/100))
ffmi = round((lean_weight/height**2),2)
adjusted_ffmi = ffmi + 6.1 * (1.8 - height)
#Result
result_label = Label(root, text="Lean Mass: " + str(lean_weight) + " kg")
result_label.grid(row=6, sticky=W, padx=5)
result_label2 = Label(root, text="FFMI: " + str(ffmi))
result_label2.grid(row=7, sticky=W, padx=5)
result_label3 = Label(root, text="adjusted-FFMI: " + str(adjusted_ffmi))
result_label3.grid(row=8,sticky=W, padx=5)
weight_box.delete(0,END)
height_box.delete(0,END)
bf_box.delete(0,END)
#Calculate button
calculate_button = Button(root,text="Calculate",command=calculate, width=16 )
calculate_button.grid(row=5,column=1,pady=10)
calculate_button = Button(root,text="Calculate", width=16 )
calculate_button.grid(row=4,column=1,pady=10)
calculate_button.destroy()
#Dropdownbox
drop = ttk.Combobox(root, value=Options, state="readonly")
drop.current(0)
drop.grid(row=0)
drop.bind("<<ComboboxSelected>>", picker)
result_label = Label(root,text="test")
result_label.grid(row=4)
result2_label = Label(root,text="")
result2_label.grid(row=4)
result3_label = Label(root,text="")
result3_label.grid(row=4)
bf_label = Label(root)
bf_box = Entry(root)
picker(drop.current(0))
root.mainloop()
Here a Screenshot of the problem I'm referencing to:
You keep creating a new Label with name result_label in your calculate methods.
This piles the labels on top of each other, since the result_label.destroy() does not remove them from the grid.
Instead of destroying them, keep just one instance (which you already defined global) and then configure them with the new text:
result_label.config(text="Your BMI is: " + str(bmi))
My Program doesnt set the output result from the function.
I have a GUI where they can pick from 3 foods, they then put how much weight they want. they hit Calculate button and it calls a function where is grabs the weight and * by a stored prices.
I get error: TypeError: can't multiply sequence by non-int of type 'list'
Program Code:
from tkinter import *
#libary of feed cost and size
Pellets = ['Pellets', 22.75, 100.00] #name, 10kg cost, 50kg cost
Mash = ['Mash', 20.50, 90.00] #name, 10kg cost, 50kg cost
Enhanced = ['Enhanced', 25.50, 125.50] #name, 10kg cost, 50kg cost
Size = ['10KG', '50KG'] #weight 1, weight 1
def close_window():
window.destroy()
def totalcost1():
weightneeded = [float(weight.get())]
pellets10kg = [float(Pellets[1])]
totalCost = (weightneeded*pellets10kg)
resultTotalCost.set("${0:,.2f}".format(totalCost))
window = Tk()
window.geometry("1000x500")
window.resizable(0, 0)
window.title("Chook Food Calcuator")
lblFoodList = Label(window, text='Please Select a food type:')
lblFoodList.grid(column=1, row=0, padx=12, pady=25)
#Food Selection Dropdown Menu
FoodSelection = StringVar(window)
FoodSelection.set("Pellets") # default value
#list of foods
x = OptionMenu(window, FoodSelection, Pellets[0], Mash[0], Enhanced[0])
x.grid(column=2, row=0, sticky="e")
lblFoodList = Label(window, text='Please Select the total weight of food you wish to buy:')
lblFoodList.grid(column=1, row=1, padx=25, pady=50, sticky='n')
weight = StringVar()
entWeight = Entry(window, width=20, textvariable=weight)
entWeight.grid(column=2, row=1, padx=25, pady=50, sticky='n')
btnCalculate = Button(window, text='Calculate', command=totalcost1)
btnCalculate.grid(column=3, row=1, padx=25, pady=25, sticky='e')
lblTotalCost = Label(window, text='Total Cost:')
lblTotalCost.grid(column=1, row=3, padx=25, pady=25)
resultTotalCost = StringVar()
entTotalCost = Entry(window, width=20, textvariable=resultTotalCost, state="readonly")
entTotalCost.grid(column=2, row=3, padx=25, pady=50, sticky='n')
btnQuit = Button(window, text='Quit', command=close_window)
btnQuit.grid(column=6, row=4, sticky='se')
window.mainloop( )
With the current code I neet the output feild to be (input*storedprice)
the error is here
weightneeded = [float(weight.get())]
pellets10kg = [float(Pellets[1])]
try to print
print(weightneeded)
print(pellets10kg)
it seems that you are trying to multiply two list object, as python say to you...
get error: TypeError: can't multiply sequence by non-int of type 'list'
try so
def totalcost1():
weightneeded = float(weight.get())
pellets10kg = float(Pellets[1])
print(weightneeded)
print(pellets10kg)
totalCost = (weightneeded*pellets10kg)
resultTotalCost.set("${0:,.2f}".format(totalCost))
I didnt make Weight a int. It is know
def totalcost1():
inputwieght = int(weight.get())
totalCost = inputwieght*Pellets[1]
resultTotalCost.set("${0:,.2f}".format(totalCost))
I am trying to write a program where you enter a number in to an entry box click a button and it prints the number you entered and it prints the number you entered multiplied by 0.008.
Then stores the numbers so the next time you enter a number it adds it to the previous number and prints it and so on so forth. I have written the first bit of code and it works well. But no matter how much research I do I cannot find out how to do the second bit. This is my code so far.
from tkinter import *
def calculatemoney():
done = float(Lines1.get())
salary3 = done * 0.08
salary4 = done * 1
labelresult = Label(root, text='%.0f' % salary4).grid(row=3, column=2)
labelresult = Label(root, text=' £ %.2f' % salary3).grid(row=4, column=2)
root = Tk()
root.title('Dict8 Calc')
root.geometry('250x200+800+100')
Lines1 = StringVar()
var1 = Label(root, text='Enter Lines').grid(row=0, column=1)
var2 = Label(root, text='Lines Today').grid(row=3, column=1)
var3 = Label(root, text='Money Today').grid(row=4, column=1)
var4 = Label(root, text='Lines Total').grid(row=6, column=1)
var5 = Label(root, text='Money Total').grid(row=7, column=1)
myLines = Entry(root, textvariable=Lines1).grid(row=0, column=2)
button1 = Button(root, text=' Calculate ', command=calculatemoney).grid(row=8, column=2)
root.mainloop()
What's stopping you from using a regular variable ?
from tkinter import *
def calculatemoney():
global oldValue # Making it global so you can set it's value
done = float(Lines1.get())
salary3 = done * 0.08
salary4 = done
salary5 = (done + oldValue) * 0.8 # Adding the old value to the new one
salary6 = done + oldValue
Label(root, text='%.0f' % salary4).grid(row=3, column=2) # I don't recommend this method of putting a label over another every time the user activates this function
Label(root, text=' f %.2f' % salary3).grid(row=4, column=2)
Label(root, text='%.0f' % salary6).grid(row=6, column=2)
Label(root, text=' f %.2f' % salary5).grid(row=7, column=2)
oldValue += done # Adding the current value to the old value
root = Tk()
oldValue = 0.0 # Define variable that will represent an old value
root.title('Dict8 Calc')
root.geometry('250x200+800+100')
Lines1 = StringVar()
var1 = Label(root, text='Enter Lines').grid(row=0, column=1) # .grid() method returns 'None' so you dont have any use for 'var1'.
var2 = Label(root, text='Lines Today').grid(row=3, column=1)
var3 = Label(root, text='Money Today').grid(row=4, column=1)
var4 = Label(root, text='Lines Total').grid(row=6, column=1) # Shouldn't it be 'row=5' ?
var5 = Label(root, text='Money Total').grid(row=7, column=1)
myLines = Entry(root, textvariable=Lines1).grid(row=0, column=2)
button1 = Button(root, text=' Calculate ', command=calculatemoney).grid(row=8, column=2)
root.mainloop()