Python says I don't have attribute when I do [duplicate] - python

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 5 years ago.
So I'm pretty new to coding and I'm trying to make a simple menu program with tkinter that would let the user click on certain food items and it will display his/her total.
When I run the program python says AttributeError: 'Menu' object has no attribute 'potato_skins'. When I take out "potato skins" it says I do not have the attribute of bread, and so on and so forth.
Can someone please help me, here is the code:
#Order Up!
#restaurant menu that lets the user pick foods, then show overall price
from tkinter import *
class Menu(Frame):
"""Menu that let's the user choose food and shows the total price."""
def __init__(self, master):
"""Initialize the frame"""
super(Menu, self).__init__(master)
self.grid()
self.menu_widgets()
def menu_widgets(self):
"""Create all the menu items."""
#Appetizer Label
Label(self,
text = "Choose your appetizers:"
).grid(row = 0, column = 0, sticky = W)
#Appetizer checkbuttons
self.motzerella_sticks = BooleanVar()
Checkbutton(self,
text = "Mozzerella sticks, $5",
variable = self.motzerella_sticks,
command = self.update_total()
).grid(row = 1, column = 1, sticky = W)
self.potato_skins = BooleanVar()
Checkbutton(self,
text = "potato skins, $7",
variable = self.potato_skins,
command = self.update_total()
).grid(row = 1, column = 1, sticky = W)
self.bread = BooleanVar()
Checkbutton(self,
text = "bread, $0",
variable = self.bread,
command = self.update_total()
).grid(row = 1, column = 2, sticky = W)
#Entree Label
Label(self,
text = "Pick your entree:"
).grid(row = 2, column = 0, sticky = W)
#Entree Checkbuttons
self.chicken = BooleanVar()
Checkbutton(self,
text = "chicken and brocolli, $10",
variable = self.chicken,
command = self.update_total()
).grid(row = 3, column = 0, sticky = W)
self.soup = BooleanVar()
Checkbutton(self,
text = "brocolli cheddar soup, $12",
variable = self.soup,
command = self.update_total()
).grid(row = 3, column = 1, sticky = W)
self.pasta = BooleanVar()
Checkbutton(self,
text = "alfredo pasta, $15",
variable = self.pasta,
command = self.update_total()
).grid(row = 3, column = 2, sticky = W)
#Dessert Label
Label(self,
text = "Choose your dessert:"
).grid(row = 4, column = 0, sticky = W)
#Dessert Checkbuttons
self.cake = BooleanVar()
Checkbutton(self,
text = "Chocolate cake, $15",
variable = self.cake,
command = self.update_total()
).grid(row = 5, column = 0, sticky = W)
self.brownie = BooleanVar()
Checkbutton(self,
text = "Brownies, $13",
variable = self.brownie,
command = self.update_total()
).grid(row = 5, column = 1, sticky = W)
#create a Text box to display total
self.total_txt = Text(self, width = 40, height = 5, wrap = WORD)
self.total_txt.grid(row = 6, column = 0, columnspan = 2, sticky = W)
def update_total(self):
"""Show the total"""
total = 0
if self.motzerella_sticks.get():
total += 5
if self.potato_skins.get():
total += 7
if self.bread.get():
total += 0
if self.chicken.get():
total += 10
if self.soup.get():
total += 12
if self.pasta.get():
total += 15
if self.cake.get():
total += 15
if self.brownie.get():
total += 13
self.total_txt.delete(0.0, END)
self.total_txt.insert(0.0, "Your total is: ", total)
#main
root = Tk()
root.title("Menu")
app = Menu(root)
root.mainloop()

The reason is that you execute your function instead of passing it as the command parameter.
command = self.update_total()
This means its executed when creating the CheckButton associated with self.motzerella_sticks. At this point self.potato_skins does not exist.
To fix it, pass the function, rather than executing it.
command = self.update_total

Related

How do i make a continue button? Python, tkinter

I want to make a button in my widget that when I press it, it proceeds to the next lines of code, and closes the existing widget of where the button is.
from tkinter import *
root = Tk()
Label(root, text = "Childs First name").grid(row = 0, sticky = W)
Label(root, text = "Childs Surname").grid(row = 1, sticky = W)
Label(root, text = "Childs Year of Birth").grid(row = 2, sticky = W)
Label(root, text = "Childs Month of Birth").grid(row = 3, sticky = W)
Label(root, text = "Childs Day of Birth").grid(row = 4, sticky = W)
Fname = Entry(root)
Sname = Entry(root)
x = Entry(root)
y = Entry(root)
z = Entry(root)
Fname.grid(row = 0, column = 1)
Sname.grid(row = 1, column = 1)
x.grid(row = 3, column = 1)
y.grid(row = 2, column = 1)
z.grid(row = 4, column = 1)
button1 = Button(root, text = "Quit", command = root.quit, bg = "Grey", fg = "White", width = 12).grid(row = 5, column = 0, sticky = W)
def save():
Fname_value = Fname.get()
Sname_value = Sname.get()
x_value = x.get()
y_value = y.get()
z_value = z.get()
save()
mainloop()
Sorry for this late answer. To make a 'continue' button:
continue_button = tk.Button(frame,text='continue',command=func)
continue_button.config(width=width_continue_button)
# set the coordinates as you want. here 2,6 for the example
continue_button.grid(row=2,column=6,padx=width_continue_grid)
then you have to write the function 'func' to fulfill your needs.
hope this helps

Can't insert a user input value because "str object has no attribute get" in tkinter

So several other people have asked similar questions, but I don't think they necessarily apply to my situation. I'm writing a program that's eventually going to take user inputs, check to make sure they correct, and then save it all to a file. Currently, I'm passing all the user inputs to a text box in tkinter so that I can just save whatever's in that one text box. I'm using a for loop to go through the 7 user entry fields I have and then insert them into the text box.
def submit(self):
""" submits user data up to input data section of GUI and checks USL vs LSL"""
e6 = IntVar(self.e6)
e7 = IntVar(self.e7)
if e6 > e7:
message = "Limits are good"
else:
message = "USL can't be less than LSL, please re-enter USL and LSL"
self.checklimits.delete(0.0, END)
self.checklimits.insert(0.0, message)
x = 1
for x in range (1, 8):
xname = "self.e" + str(x)
entry = xname.get()
if entry:
self.checktext.insert(END, entry + "\n")
x = x+1
I'm wanting to take the x value in the for loop and eventually end up with something like "self.e#.get()" since that's how i've defined the user entries, see example below:
def create_widgets(self):
"""create widgets for user inputted data"""
# creates a text widget next to the entries that displays what the user has input
Label(self, text = "Do these values you entered seem correct?").grid(row = 0, column = 4, sticky = W, padx = 5, pady = 5)
self.checktext = Text(self, width =15, height = 42, wrap = WORD)
self.checktext.grid(row = 1, rowspan = 10, column = 4, sticky = W, padx =5, pady =5)
# get name
Label(self, text = "First Name:").grid(row = 0, column = 0, sticky = W, padx=5, pady=5)
self.e1 = Entry(self)
self.e1.grid(row = 0, column = 1)
Label(self, text = "Last Name:").grid(row = 1, column = 0, sticky = W, padx=5, pady=5)
self.e2 = Entry(self)
self.e2.grid(row = 1, column = 1)
Right now though, python isn't recognizing that entry as an entry and is saying back to me that "'str' object has no attribute 'get'"
So for one, why can't I "get" a string value, and two, how can I get python to recognize my previously defined entry? Here's my entire code for context
from Tkinter import *
class Application(Frame):
""" A SPC program that takes user input and saves the file """
def __init__(self,master):
""" initializes the frame """
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
"""create widgets for user inputted data"""
# creates a text widget next to the entries that displays what the user has input
Label(self, text = "Do these values you entered seem correct?").grid(row = 0, column = 4, sticky = W, padx = 5, pady = 5)
self.checktext = Text(self, width =15, height = 42, wrap = WORD)
self.checktext.grid(row = 1, rowspan = 10, column = 4, sticky = W, padx =5, pady =5)
# get name
Label(self, text = "First Name:").grid(row = 0, column = 0, sticky = W, padx=5, pady=5)
self.e1 = Entry(self)
self.e1.grid(row = 0, column = 1)
Label(self, text = "Last Name:").grid(row = 1, column = 0, sticky = W, padx=5, pady=5)
self.e2 = Entry(self)
self.e2.grid(row = 1, column = 1)
# get work order
Label(self, text = "Work Order Number:").grid(row = 2, column = 0, sticky = W, padx=5, pady=5)
self.e3 = Entry(self)
self.e3.grid(row = 2, column = 1)
# get todays date
Label(self, text = "Todays Date:").grid(row = 3, column = 0, sticky = W, padx=5, pady=5)
self.e4 = Entry(self)
self.e4.grid(row = 3, column = 1)
# get bubble number
Label(self, text = "Bubble Number:").grid(row = 4, column = 0, sticky = W, padx=5, pady=5)
self.e5 = Entry(self)
self.e5.grid(row = 4, column = 1)
# get USL and LSL
Label(self, text = "USL:").grid(row = 5, column = 0, sticky = W, padx=5, pady=5)
self.e6 = Entry(self)
self.e6.grid(row = 5, column = 1)
Label(self, text = "LSL:").grid(row = 6, column = 0, sticky = W, padx=5, pady=5)
self.e7 = Entry(self)
self.e7.grid(row = 6, column = 1)
"""# button to check USL is higher than LSL
self.button7 = Button(self)
self.button7["text"] = "Check Limits"
self.button7["command"] = self.check_limits
self.button7.grid(row = 6, column = 2, sticky = W, padx=5, pady=5)"""
# button to submit user entered values up to the input data values portion of the gui
self.button6 = Button(self)
self.button6["text"] = "Submit"
self.button6["command"] = self.submit
self.button6.grid(row = 5, column = 2, sticky = W, padx=5, pady=5)
# creates a spot to dictate whether USL and LSL are correct
self.checklimits = Text(self, width = 20, height = 2, wrap = WORD)
self.checklimits.grid(row = 6, column = 3, sticky = W, padx = 5)
""" #adds a scroll bar to the data input text box
scrollbar = Scrollbar(self)
scrollbar.pack(side=RIGHT, fill=Y) """
# get User Input Data values
Label(self, text = "Enter Results:").grid(row = 7, column = 0, sticky = W, padx=5, pady=5)
self.e8 = Text(self, width = 15, height = 30)
self.e8.grid(row = 7, column = 1)
""" def check_limits(self):
checks to see if the USL is greater than the LSL
e6 = IntVar(self.e6)
e7 = IntVar(self.e7)
if e6 > e7:
message = "Limits are good"
else:
message = "USL can't be less than LSL, please re-enter USL and LSL"
self.checklimits.delete(0.0, END)
self.checklimits.insert(0.0, message)"""
def submit(self):
""" submits user data up to input data section of GUI and checks USL vs LSL"""
e6 = IntVar(self.e6)
e7 = IntVar(self.e7)
if e6 > e7:
message = "Limits are good"
else:
message = "USL can't be less than LSL, please re-enter USL and LSL"
self.checklimits.delete(0.0, END)
self.checklimits.insert(0.0, message)
x = 1
for x in range (1, 8):
xname = "self.e" + str(x)
entry = xname.get()
if entry:
self.checktext.insert(END, entry + "\n")
x = x+1
root = Tk()
root.title("SPC Input Program")
root.geometry("700x750")
app = Application(root)
root.mainloop()
It appears that you are assuming that e6 = IntVar(self.e6) is getting the value of an entry widget and converting it to an int. That's not what it's doing. It is creating a new variable named e6 that is initialized to zero, and has self.e6 as the "master".
If you want to get the values from an entry widget and convert it to an integer, use the get method on the entry widget.
e6 = int(self.e6.get())
e7 = int(self.e7.get())
The problem that is causing the "str object has no method get" is this code:
xname = "self.e" + str(x)
entry = xname.get()
This is a terrible way to code. As you can see, it won't work. You can make it work with tricks, but a good rule of thumb is to never try to dynamically create variables like this.
If you want to loop over the entry widgets, put them in a list or tuple. For example:
for widget in (self.e1, self.e2, self.e3, self.e4,
self.e5, self.e6, self.e7, self.e8):
entry = widget.get()
if entry:
self.checktext.insert(END, entry + "\n")
...

How can I order a file line?

I have to make a code that will produce a GUI that shows test results for three classes and all the information will be on a file. I have tried to do the code I can make the GUI and radio buttons and that all works, but get a button that allows you to put what ever is in the text box to be in alphabetical order.
from tkinter import *
root = Tk()
root.title("Test scores")
root.geometry("500x300")
app = Frame(root)
app.grid()
test1 = open("names.txt","r")
test2 = open("score.txt","r")
test3 = open("class.txt","r")
class Application(Frame):
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
Label(self,
text = "Which class "
).grid(row = 0,column = 0, sticky = W)
Label(self,
text = "Select a class"
).grid(row = 1, column = 0, sticky = W)
self.choose = StringVar()
self.choose.set(None)
Radiobutton(self,
text = "Class 1",
variable = self.choose,
value = test1.read(),
command = self.update_text
).grid(row = 2, column = 0, sticky = W)
self.second = BooleanVar()
Radiobutton(self,
text = "Class 2",
variable = self.choose,
value = test2.read(),
command = self.update_text
).grid(row = 3, column = 0, sticky = W)
self.third = BooleanVar()
Radiobutton(self,
text = "Class 3",
variable = self.choose,
value = test3.read(),
command = self.update_text
).grid(row = 4, column = 0, sticky = W)
self.bttn = Button(self)
self.bttn["text"] = "Alphabetical"
self.bttn["command"] = self.update_order
self.bttn.grid(row = 4, column = 1, sticky = W)
self.results_txt = Text(self, width = 40, height = 5, wrap = WORD)
self.results_txt.grid(row = 5, column = 0, columnspan = 3)
def update_text(self):
likes = ""
likes += self.choose.get()
self.results_txt.delete(0.0, END)
self.results_txt.insert(0.0, likes)
def update_order(self):
sortfile = open("sorted1.txt","w")
for line in sorted(self.results_txt, key = str.lower):
sortfile.write(line)
app = Application(root)
root.mainloop()
this is my code so far.
i tried to sort it but it doesn't work this is the code i tried as i looked online and saw this
def update_order(self):
str(sorted(self.results_txt, key=lambda student: student[2]))
how would i sort file lines
so if the first line had "name" "first score" "second score" "third score"
then on the second line "different name" "first score" "second score" "third score"
how would you sort it alphabetically by the name in the line nothing else. would i need to put each score on different files?

Python - RadioButton - assigning a value i can use?

I have a python assignment to do for University but I am having a problem.The assignment states that I need to create checkbox buttons that have a value that will total once I push a button. This I have managed to do. I have to do the same thing with radio buttons and here I am having a problem. If I create the radio buttons they work, but i do not how to assign a value to them and then use this value in my gandTotal.
`enter code here``#Help with the menu program
import random
from Tkinter import *
class App(Frame):
#GUI application
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
#Create all the widgets here
#Label to welcome the user
Label(self,text = "Welcome to Awesome Reasturant:"
).grid(row=1,column=0,sticky=W)
#..........your code goes here
#Labels to request the name of the customer
Label(self,text = "Customer Name:"
).grid(row=2,column=0,sticky=W)
self.CustomerName_ent = Entry(self)
self.CustomerName_ent.grid(row = 2, column = 1, sticky = W)
#Labels to request the telephone number
Label(self,text = "Telephone Number:"
).grid(row=3,column=0,sticky=W)
self.Telephone_ent = Entry(self)
self.Telephone_ent.grid(row = 3, column = 1, sticky = W)
#Label to generate a random order number - Nees work
Label(self,text = "Random Order Number:"
).grid(row=3,column=2,sticky=W)
#self.Random_ent = RandomNumber(random.randrange(100))
# Create a label for Starter Menue
Label(self,
text = "Select from starter Menue #R55:"
).grid(row = 7, column = 0, sticky = W)
# create variable for single starter menue item
self.Starter_menue = StringVar()
#create starter menue radio buttons
#Starter_menue = ["Prawn and Muchroom cocktail", "9 Oysters", "Goats cheese"]
#column = 0
#for part in Starter_menue:
# Radiobutton(self,
# text = part,
# variable = self.Starter_menue,
# value = part
# ).grid(row = 9, column = column, sticky = W)
#column += 1
#create radion for Starter item menue 1
Radiobutton(self,text="Prawn and Mushroom",
variable = self.Starter.menue, value = 55, command =self.determine_order
).grid(row=9, column =1, sticky = W)
# Create a label for Starter Menue
Label(self,
text = "Select from Mains Menue #R100:"
).grid(row = 10, column = 0, sticky = W)
# create variable for single mains menue item
self.Mains_menue = StringVar()
#create main course radio buttons
Mains_menue = ["250g Fillet", "Beef Schnitzel", "Chicken", "Quails", "Ravioli", "Genooch"]
column = 0
for part in Mains_menue:
Radiobutton(self,
text = part,
variable = self.Mains_menue,
value = part
).grid(row = 11, column = column, sticky = W)
column += 1
#MENU items to choose from
#I'll do one example with a check button
#the salads
Label(self,text = "Select from Salads"
).grid(row=4,column=0,sticky=W)
self.has_green = BooleanVar()
self.has_blue = BooleanVar()
self.has_ceaser = BooleanVar()
#the greens
Checkbutton(self, text="Greens",
variable=self.has_green
).grid(row=5,column=0,sticky=W)
#the blue salad
Checkbutton(self, text="Blue",
variable=self.has_blue
).grid(row=5,column=1,sticky=W)
#The Ceaser Salad
Checkbutton(self,text="Ceaser Salad",
variable=self.has_ceaser
).grid(row=5,column=2,sticky=W)
#Place the order
Button(self,text = "Place the Order",command = self.determine_order, bg="gold").grid(row = 14, column = 0, sticky = W)
#The display area
self.txt_order = Text(self, width = 75, height = 5, wrap = WORD)
self.txt_order.grid(row = 15, column = 0, columnspan = 4)
def RandomNumber():
RandomNumber = random.randrange(100)
def determine_order(self):
#Get all the information from the GUI
totalprice = 0
totalMain =0
totalStarter =0
grandTotal = totalMain + totalStarter + totalprice
greensaladprice=40
bluesaladprice=40
ceasersaladprice=40
CustomerName =self.CustomerName_ent.get()
Telephone=self.Telephone_ent.get()
Starter_menue = self.Starter_menue.get()
Starter1 = 55
Starter2= 55
Starter3=55
#Check if and which salads are selected
if self.has_green.get():
totalprice = totalprice + greensaladprice
if self.has_blue.get():
totalprice = totalprice + bluesaladprice
if self.has_ceaser.get():
totalprice = totalprice + ceasersaladprice
#Check if and which Starters are selected
if self.Starter_menue(0):
totalStarter = totalStarter + Starter1
if self.Starter_menue(1):
totalStarter = totalStarter + Starter2
if self.Starter.get():
totalStarter = totalStarter + Starter3
#Display the total
self.txt_order.delete(0.0,END)
self.txt_order.insert(0.0,float(grandTotal))
# Add the name -Needs work
self.txt_order.insert(100.50,CustomerName)
#Add telephone number - Needs Work
self.txt_order.insert(0.0,Telephone)
def main():
root = Tk()
root.title("Example - menu 1")
app = App(root)
root.mainloop()
main()
First off, are you missing some code in your example? possibly a Starter initialisation in the App class?
I get a traceback if I try to run:
Traceback (most recent call last):
File "test.py", line 165, in <module>
main()
File "test.py", line 160, in main
app = App(root)
File "test.py", line 10, in __init__
self.create_widgets()
File "test.py", line 57, in create_widgets
variable = self.Starter.menue, value = 55, command =self.determine_order
AttributeError: App instance has no attribute 'Starter'

How to trap ValueError generated from empty Entry Widget in tkinter

In the code below I have managed to partially validate the data entered into the self.e2 entry widget, unfortunately if the entry widget is empty and the Submit button is pressed then a ValueError is generated" ValueError: invalid literal for int() with base 10: '' "
I would like to have the program recognise that the e2 entry widget is empty, have the ValueError trapped and return focus back to the entry widget.
I have attempted to do this using the is_valid_int and invalid_int methods but this is not working.
from tkinter import *
from tkinter import ttk
from tkinter.scrolledtext import *
class DailyOrderGUI:
def __init__(self, parent):
#Data entring frame
self.frame = Frame(parent, bg = "grey")
self.frame.grid(row=0)
self.label1 = Label(self.frame, text = "Mrs CackleBerry's Egg Ordering System", wraplength = 200, bg="grey", font=("Comic Sans MS", "14", "bold"))
self.label1.grid(row = 0, columnspan = 3, padx = 5, pady = 5)
self.superegg_img = PhotoImage(file = "images/superegg.gif")
self.label5 = Label(self.frame, bg="grey", image = self.superegg_img)
self.label5.grid(row = 0, column= 3, padx = 5, pady = 5)
self.v = StringVar()
self.v.set("Monday")
self.label2 = Label(self.frame, text = "Which day are you ordering for?", bg="grey", font=("Arial", "12", "bold"))
self.label2.grid(row = 1, columnspan = 4, sticky = W)
self.rb1 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Monday", text = "Monday")
self.rb1.grid(row = 2, column = 0, sticky = W)
self.rb2 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Tuesday", text = "Tuesday")
self.rb2.grid(row = 2, column = 1, sticky = W)
self.rb3 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Wednesday", text = "Wednesday")
self.rb3.grid(row = 2, column = 2, sticky = W)
self.rb4 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Thursday", text = "Thursday")
self.rb4.grid(row = 2, column = 3, sticky = W)
self.rb5 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Friday", text = "Friday")
self.rb5.grid(row = 3, column = 0, sticky = W)
self.rb6 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Saturday", text = "Saturday")
self.rb6.grid(row = 3, column = 1, sticky = W)
self.rb7 = Radiobutton(self.frame, variable = self.v, bg="grey", value = "Sunday", text = "Sunday")
self.rb7.grid(row = 3, column = 2, sticky = W)
self.label3 = Label(self.frame, text = "Customer's Name:?(Press \"Orders Complete\" to finish)", bg="grey", font=("Arial", "12", "bold"))
self.label3.grid(row = 4, columnspan = 4,padx = 5, sticky = W)
self.e1 = Entry(self.frame, width = 30)
self.e1.grid(row = 5, columnspan = 4, sticky = W, padx = 5, pady=3)
self.e1.focus()
self.label4 = Label(self.frame, text = "How many eggs being ordered:?", bg="grey", font=("Arial", "12", "bold"))
self.label4.grid(row = 6, columnspan = 4,padx = 5,sticky = W)
integer = self.create_integer_widget()
self.btn1 = Button(self.frame, text = "Submit")
self.btn1.grid(row = 8, padx = 5, pady = 3, sticky = E+W)
self.btn1.bind("<Button-1>", self.get_orders)
self.btn2 = Button(self.frame, text = "Orders Complete", command = self.show_summary_result)
self.btn2.grid(row = 8, column = 3, padx = 5, pady = 3, sticky = E+W)
#Summary Frame
self.summ_frame = Frame(parent, bg = "grey")
self.summ_frame.grid(row=0)
self.summ_label1 = Label(self.summ_frame, text = "Mrs CackleBerry's Egg Ordering System", bg="grey", font=("Comic Sans MS", "14", "bold"))
self.summ_label1.grid(row = 0, columnspan = 4, padx = 5, pady = 5)
self.scrolled_display = ScrolledText(self.summ_frame, width = 50, height = 10, bg="thistle", font=("Times New Roman", "12"))
self.scrolled_display.grid(row = 1, columnspan = 2, padx = 5, pady = 20, sticky = W)
self.data_entry_btn = Button(self.summ_frame, text = "Back to Data Entry", command = self.show_data_entry_frame)
self.data_entry_btn.grid(row = 2, column = 0, sticky = SE, padx = 5, pady = 20)
self.egg_orders=[]
self.show_data_entry_frame()
def create_integer_widget(self):
self.e2 = ttk.Entry(self.frame, width = 10, validate='key')
self.e2['validatecommand'] = (self.frame.register(self.is_valid_int), '%P')
self.e2['invalidcommand'] = (self.frame.register(self.invalid_int), '%W')
self.e2.grid(row = 7, columnspan = 4, sticky = W, padx = 5, pady=3)
self.e2.bind("<Return>", self.get_orders)
return self.e2
def is_valid_int(self, txt):
# txt - value in %P
if not txt: # do not accept empty string
return False
try:
int(txt)
return True # accept integer
except ValueError: # not an integer
return False
def invalid_int(self, widgetName):
# called automatically when the
# validation command returns 'False'
# get entry widget
widget = self.frame.nametowidget(widgetName)
# clear entry
widget.delete(0, END)
# return focus to integer entry
widget.focus_set()
widget.bell()
def show_data_entry_frame(self):
self.summ_frame.grid_remove()
self.frame.grid()
root.update_idletasks()
def show_summary_result(self):
self.frame.grid_remove()
self.summ_frame.grid()
root.update_idletasks()
self.scrolled_display.delete('1.0', END)
if len(self.egg_orders) == 0:
self.scrolled_display.insert(END, "No Orders")
else:
total = 0
self.scrolled_display.insert(END, "Orders for " + self.v.get() + "\n")
for i in range(len(self.egg_orders)):
total += self.egg_orders[i].num_eggs
self.scrolled_display.insert(END, str(self.egg_orders[i]) + "\n")
self.scrolled_display.insert(END, "" + "\n")
self.scrolled_display.insert(END, "Summary for " + self.v.get() + "\n")
self.scrolled_display.insert(END, "" + "\n")
self.scrolled_display.insert(END, "Total eggs: " + str(total) + "\n")
self.scrolled_display.insert(END, "Dozens required: " + str(self.get_dozens(total)) + "\n")
average = 0
if len(self.egg_orders) > 0:
average = total / len(self.egg_orders)
self.scrolled_display.insert(END, "Average number of eggs per customer: {0:.1f}".format(average) + "\n")
def get_orders(self, event):
"""
Collects order information - name, number of eggs in a loop
"""
self.name = self.e1.get()
self.no_eggs = self.e2.get()
self.e1.delete(0, END)
self.e2.delete(0, END)
self.e1.focus()
self.egg_orders.append(EggOrder(self.name, self.no_eggs))
def get_dozens (self, total):
"""
returns whole number of dozens required to meet required number of eggs
"""
num_dozens = total//12
if total%12 != 0:
num_dozens += 1
return num_dozens
class EggOrder:
price_per_doz = 6.5
def __init__(self, name, num_eggs):
self.name = name
self.num_eggs = int(num_eggs)
def calc_price(self):
self.price = EggOrder.price_per_doz/12 * self.num_eggs
return self.price
def __str__(self):
return("{} ordered {} eggs. The price is ${:.2f}".format(self.name, self.num_eggs , self.calc_price()))
#main routine
if __name__== "__main__":
root = Tk()
root.title("Mrs Cackleberry's Egg Ordering Program")
frames = DailyOrderGUI(root)
root.mainloop()
Let's trace through what happens when you click "Submit".
First:
self.btn1 = Button(self.frame, text = "Submit")
self.btn1.grid(row = 8, padx = 5, pady = 3, sticky = E+W)
self.btn1.bind("<Button-1>", self.get_orders)
So, it calls self.get_orders. And the last line in that function is:
self.no_eggs = self.e2.get()
# ...
self.egg_orders.append(EggOrder(self.name, self.no_eggs))
And inside the EggOrder.__init__ function you've got this:
self.num_eggs = int(num_eggs)
That's presumably where the error happens.
(Note that all of that work would have been unnecessary if you'd posted the traceback instead of just the error string.)
So, when self.e2.get() returns an empty string, you end up calling int(''), and that raises a ValueError.
Unless you want to try to check for that possibility in advance (which is rarely a good idea), you will need a try:/except ValueError: somewhere. The question is, where? Well, that depends on what you want to do.
If you want to create an empty EggOrder, you'd do it inside EggOrder.__init__:
try:
self.num_eggs = int(num_eggs)
except ValueError:
self.num_eggs = 0
On the other hand, if you want to not create an EggOrder at all, you'd do it inside self.get_orders:
try:
order = EggOrder(self.name, self.no_eggs)
except ValueError:
# pop up an error message, log something, call self.invalid_int, whatever
else:
self.egg_orders.append(order)
Of course you probably want to do this before all the destructive stuff (self.e1.delete(0, END), etc.).
Also, if you wanted to call invalid_int as-is, you'd need to pass it the name of the self.e2 widget. Since you didn't give it one, it'll be something dynamic and unpredictable like .1234567890, which you'll have to ask the widget for, just so you can look the widget back up by that name. It would be simpler to factor out the core functionality, something like this:
def invalid_int(self, widgetName):
widget = self.frame.nametowidget(widgetName)
self.handle_invalid_int(widget)
def handle_invalid_int(self, widget):
widget.delete(0, END)
# etc.
Then you can just call handle_invalid_int(self.e2).
Meanwhile, you've tried adding a validation function to check for a valid int input.
is_valid_int should work fine. However, the if not txt part is completely unnecessary—int(txt) will already handle an empty string the same way it handles a non-numeric string.
And the way you've hooked it up should work too.
However, a validation function runs on each edit to the Entry widget, not when you click some other random widget elsewhere. For example, if you try typing letters into the Entry, it should erase them as soon as you can type them. (Note that if you type 123a456 you'll end up with 456, not 123456, which may or may not be what you want…)
So, you've done that right, but it's not what you wanted to do.

Categories