I'm writing a program for a dice game (Pig). In the game, the player will roll a d6 until they decide to hold their score (passing to the computer) or until they roll a 1, which will automatically make it the computer's turn.
The issue I'm having is that I need the function for the computer's turn to loop ten times. I want the computer to roll the die ten times, where it will either roll a one and pass back to the player or it will hold after ten rolls. How do I get the computer to roll the die ten times without using a loop inside of Tk?
Here's the code:
from Tkinter import *
from random import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.headerFont = ("courier new", "16", "bold")
self.title("Pig, The Dice Game")
self.headers()
self.rollDie()
def headers(self):
Label(self, text = "Instructions", font = self.headerFont).grid(columnspan = 4)
Label(self, text = "Text", font = self.headerFont).grid(row = 1, columnspan = 4)
Label(self).grid(row = 1, columnspan = 4)
Label(self, text = "The Game of Pig", font = self.headerFont).grid(row = 2, columnspan = 4)
def rollDie(self):
self.btnRoll = Button(self, text = "Roll The Die")
self.btnRoll["state"] = 'active'
self.btnRoll.grid(row = 3, columnspan = 4)
self.btnRoll["command"] = self.playerTurn
self.btnHold = Button(self, text = "Hold")
self.btnHold["state"]= 'active'
self.btnHold.grid(row = 4, columnspan = 4)
self.btnHold["command"] = self.compTurn
self.btnPass = Button(self, text = "Pass")
self.btnPass.grid(row = 5, columnspan = 4)
self.btnPass["command"] = self.compTurn
Label(self, text = "You Rolled:").grid(row = 6, column = 0)
self.lblYouRolled = Label(self, bg = "#fff", anchor = "w", relief = "groove")
self.lblYouRolled.grid(row = 6, column = 1, columnspan = 1, sticky = "we")
Label(self, text = "Options:").grid(row = 7, column = 0)
self.lblOptions = Label(self, bg = "#fff", anchor = "w", relief = "groove")
self.lblOptions.grid(row = 7, column = 1, sticky = "we")
Label(self, text = "Player One Turn Score:").grid(row = 8, column = 0)
self.lblPlayerOneTurnScore = Label(self, bg = "#fff", anchor = "w", relief = "groove")
self.lblPlayerOneTurnScore.grid(row = 8, column = 1, sticky = "we")
def playerTurn(self):
self.oneTurnTotal = [0]
self.oneRoll = randint(1,6)
self.btnHold["state"] = 'active'
self.lblYouRolled["text"] = self.oneRoll
if self.oneRoll != 1:
self.oneTurnTotal.append(self.oneRoll)
self.lblOptions["text"] = "Roll again, or hold and pass the dice to Player Two."
else:
self.lblOptions["text"] = "You rolled a 1! Click 'Pass' to pass your turn to the computer."
self.oneTurnTotal = [0]
self.btnRoll["state"] = 'disabled'
self.btnHold["state"] = 'disabled'
def calculatePlayerOneTurnScore(self):
turnScore = sum(self.oneTurnTotal)
self.lblPlayerOneTurnScore["text"] = turnScore
def compTurn(self):
self.compTurnTotal = [0]
self.compRoll = randint(1,6)
self.lblYouRolled["text"] = self.compRoll
if self.compRoll != 1:
self.compTurnTotal.append(self.compRoll)
self.lblOptions["text"] = "The computer will roll again."
else:
self.lblOptions["text"] = "The computer rolled a 1! Its turn has ended."
self.compTurnTotal = [0]
self.btnRoll["state"] = 'active'
def calculatePlayerTwoTurnScore(self):
turnScore = sum(self.twoTurnTotal)
self.lblPlayerTwoTurnScore["text"] = turnScore
def main():
app = App()
app.mainloop()
if __name__ == "__main__":
main()
You could make the dicerolling a toplevel widget and then have your root "wait_window" for the rolling widget to complete. Create a function inside the toplevel widget that generates the roll and call it ten times
EXAMPLE
from Tkinter import *
yourscorevalue=12
cpuscorevalue=10
class dice:
def __init__(self,parent):
rollcount=0
top=self.top=Toplevel(parent)
while 1:
roll=self.diceroll()
rollcount+=1
if rollcount==10 or roll==1:
break
def diceroll(self):
#Random Dice Roll code
class dicegame:
def __init__(self):
self.root=Tk()
self.root.title('Dicerolling Game')
Label(self.root, text='Your Score').grid(column=0,row=0,sticky='ew')
Label(self.root, text=yourscorevalue, background='black',foreground='red').grid(column=1,row=0,sticky='ew')
Label(self.root, text='Computer Score').grid(column=0,row=1,sticky='ew')
Label(self.root, text=cpuscorevalue, background='black',foreground='red').grid(column=1,row=1,sticky='ew')
b=Button(self.root, text='Roll The Dice', command=self.roll).grid(column=0,row=2,sticky='ew')
c=Button(self.root, text="Done", command=self.root.destroy).grid(column=1,row=2,sticky='ew')
mainloop()
def roll(self):
d=dice(self.root)
self.root.wait_window(d.top)
Related
I couldnt seem to find a way to make my program realise that ive selected a button, so i changed the function of the celcius to farenheit to try to make it change a boolean value to determine what conversion the program is doing
def celcius_to_farenheit(_event = None):
c_to_f = True
f_to_c = False
the idea being later i can use if statments later in the end result function to find what conversion its doing and display results in the status bar
def end_result():
if c_to_f == True:
converted_temperature = (valid_temperature * 9/5) + 32
label_status.configure(text = converted_temperature, fg = "Orange")
currently i seem to have functions running without me pressing buttons as well, when start the program it immediatly goes to the error message ive created for input muct be numeric even if i havent pressed the celcius to farenheit button.
Any help regarding how to propely have my celcius to farenheit and farenheit to celcius buttons confirm its a float and change a value to use for determining which calculation its using would be helpfull. Knowing why the error message comes up automatically is a bonus.
Below is my code thank you for your time and help.
import sys
from tkinter import *
from tkinter.tix import *
c_to_f = True
def clear_reset(_event = None):
entry_temperature.delete(0, END)
label_status.configure(text = "All data cleared", fg = "Orange")
def end_program(_event = None):
sys.exit()
def convert_temp(_event = None):
try:
valid_temperature = float(entry_temperature.get())
except:
label_status.configure(text = "Input must be numeric", fg = "Orange")
def end_result():
if c_to_f == True:
converted_temperature = (valid_temperature * 9/5) + 32
label_status.configure(text = converted_temperature, fg = "Orange")
def celcius_to_farenheit(_event = None):
c_to_f = True
f_to_c = False
def farenheit_to_celcius(_event = None):
f_to_c = True
c_to_f = False
window = Tk()
window.geometry("550x200")
window.resizable(False, False)
window.title("Temperature Conversion")
tooltip = Balloon(window)
label_input_Temperature = Label(text = "Temperature",fg = "Green")
label_input_Temperature.grid(row= 0, column=0)
entry_temperature = Entry(window, bg = "light blue" )
entry_temperature.grid(row=0, column=1)
temp_button_c_to_f = Button(window, text = "Celcius to Farenheit", command = celcius_to_farenheit)
temp_button_c_to_f.grid(row = 1, column=0)
window.bind('<Shift-c>', celcius_to_farenheit)
tooltip.bind_widget(temp_button_c_to_f, msg = "Shift + C")
temp_button_f_to_c = Button(window, text = "Farenheit to Celcius")
temp_button_f_to_c.grid(row = 1, column = 1 )
conversion_button = Button(window, text = "Convert", command = convert_temp)
conversion_button.grid(row = 2, column = 0,padx =0 )
window.bind('<Enter>', convert_temp)
tooltip.bind_widget(conversion_button, msg = "Enter")
clear_button = Button(window, text = "Clear", command = clear_reset)
clear_button.grid(row = 2, column = 1)
window.bind('<Control-c>', clear_reset)
tooltip.bind_widget(clear_button, msg = "Ctrl + C")
exit_button = Button(window, text = "Exit")
exit_button.grid(row = 2, column = 2, padx = 20, pady = 20)
window.bind('<Control-x>', end_program)
tooltip.bind_widget(exit_button, msg = "Ctrl + X")
label_status = Label(window, width = 50, borderwidth = 2, relief= RIDGE,bg= "Grey" )
label_status.grid(row = 4, column = 1)
tooltip.bind_widget(label_status, msg = "Displays results / error messages")
label_status.configure(text = "Enter in your temperature and select your conversion", fg = "Orange")
window.mainloop()
I have a basic GUI for my app using Tkinter, the generator will show me a random word from the dictionary in Hebrew, there are two main functionalities I do not know how to create.
1) When I click next, I want to see a new vocabulary word. How do I create a function to generate a new word in the designated Tkinter frame when I click the 'next' button?
2) When I click submit, I want to check if the English (key) matches the Hebrew (value) in my dictionary. How do I create a function to verify the user input against the corresponding key to the given value for the button command?
I'm new, so appreciate the learning opportunity, and I actually want to use this app for my own studying.
Here is most of the code:
from tkinter import ttk
import random
dictionary = {'give her':'תנ לה', 'give me': 'תני לי', 'give us': 'תן לנו'
,'give him': 'תן לו', 'give them': 'תן להם'
, 'give to them (f)': 'תן להן'}
question = random.choice(list(dictionary.values()))
answer = dictionary.keys() == question
class Feedback:
def __init__(self, master):
self.frame_header = ttk.Frame(master)
self.frame_header.pack()
# self.logo = PhotoImage(file = '/Users/las/Desktop/Python/flagicon.png')
# ttk.Label(self.frame_header, image = self.logo).grid(row = 0, column = 0, rowspan = 2)
ttk.Button(self.frame_header, text = 'Quit', command = self.Quit).grid(row=0, column=3)
ttk.Label(self.frame_header, wraplength = 250, text = 'Type the English translation of the random word in Hebrew.').grid(row = 1, column = 1)
ttk.Label(self.frame_header, wraplength = 300,
text = ("Hebrew Word", question)).grid(row = 2, column = 1)
self.frame_content = ttk.Frame(master)
self.frame_content.pack()
ttk.Label(self.frame_content, text = 'English:').grid(row = 0, column = 1, padx = 5, sticky = 'sw')
self.user_input = ttk.Entry(self.frame_content, text = "Your Answer?", width = 24)
self.user_input.grid(row = 1, column = 1, padx = 5)
ttk.Button(self.frame_content, text = 'Submit', command = self.submit).grid(row = 2, column = 1, padx = 5, pady = 5, sticky = 'e')
ttk.Button(self.frame_content, text = 'Clear', command = self.clear).grid(row = 3, column = 0, padx = 5, pady = 5, sticky = 'w')
ttk.Button(self.frame_content, text = 'Next', command = self.next).grid(row = 3, column = 2, padx = 5, pady = 5, sticky = 'w')
def submit(self):
self.answer = answer
user_input = self.user_input
if user_input == answer:
print ("Correct")
else:
print ("!לא נכון ", "The answer is:", dictionary.keys(question))
def clear(self):
self.user_input.delete(0, 'end')
def next(self):
print (question)
The following two lines should be removed as they are useless for your application:
question = random.choice(list(dictionary.values()))
answer = dictionary.keys() == question
Change the following line in __init__() function:
ttk.Label(self.frame_header, wraplength = 300,
text = ("Hebrew Word", question)).grid(row = 2, column = 1)
to:
self.question = ttk.Label(self.frame_header, wraplength=300)
self.question.grid(row=2, column=1)
Then update next() and submit() functions as below:
def submit(self):
user_input = self.user_input.get().strip()
if user_input == self.answer:
print ("Correct")
else:
print ("!לא נכון ", "The answer is:", self.answer)
...
def next(self):
# select a random choice (English phrase) from dictionary as question
self.answer = random.choice(list(dictionary.keys()))
self.question.config(text="Hebrew Word: "+dictionary[self.answer])
Finally call next() at the end of __init__() to show the first question:
def __init__(self, master):
...
self.next()
I have a program running that is having issues when the timer runs. Every time "start" is hit, the application crashes. Any thoughts?
##-- Imports --##
import time
import openpyxl as xl
from Tkinter import *
##-- Classes --##
class App(Frame):
def startTimer(self):
self.check = True
self.now = time.strftime("%H:%M:%S")
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)
def initUI(self):
self.parent.title("Emma's Time Manager")
self.pack(fill = BOTH, expand = 1)
def initWidget(self):
##Create button definitions##
self.buttonwidth = 12
self.quit = Button(self, text = "Quit", comman = self.quit, width = self.buttonwidth)
self.newClient = Button(self, text = "Add Client", command = lambda:self.newClientFunc(), width = self.buttonwidth)
self.timeStart = Button(self, text = "Start", command = lambda:self.startTimer(), width = self.buttonwidth)
self.timeEnd = Button(self, text = "End", command = lambda:self.endTimer(), width = self.buttonwidth)
self.saveBut = Button(self, text = "Save", command = lambda:self.saveFunc(), width = self.buttonwidth)
self.viewClient = Button(self, text = "View Client", command = lambda:self.viewCliFunc(), width = self.buttonwidth)
##Create lable definitions##
self.timer = Label(self, text = "00:00:00") ##self.timer used as display for timer##
##Create the listbox for Client Selection##
self.wb = xl.load_workbook("clients.xlsx")
self.clientNames = self.wb.get_sheet_names()
self.clivar = StringVar(self)
self.clivar.set(self.clientNames[0])
self.clilist = apply(OptionMenu, (self, self.clivar) + tuple(self.clientNames))
##Create Entry Box to describe work information##
self.info = Entry(self, width = 50)
##Create GUI for widgets##
self.clilist.grid(row = 0, column = 0)
self.timer.grid(row = 0, column = 1)
self.timeStart.grid(row = 0, column = 2)
self.timeEnd.grid(row = 0, column = 3)
self.info.grid(row = 1, column = 0, columnspan = 4)
self.newClient.grid(row = 2, column = 0)
self.viewClient.grid(row = 2, column = 1)
self.saveBut.grid(row = 2, column = 2)
self.quit.grid(row = 2, column = 3)
def __init__(self, parent):
Frame.__init__(self, parent, background = "light blue")
self.parent = parent
self.initUI()
self.initWidget()
def main():
try:
xl.load_workbook("clients.xlsx")
except:
temp = xl.Workbook()
temp.save("clients.xlsx")
temp.remove_sheet(temp.get_sheet_by_name("Sheet"))
root = Tk()
bob = App(root)
root.mainloop()
main()
Please note that most of the program is not yet finished. I just cannot seem to get this timer to run properly.
Looks like you have no way out of your while loop. You'll either need to set self.check to False, or break out of the loop.
while self.check == True:
self.timer.configure(text = self.now)
time.sleep(1)
I have a program that calculates a persons BMI after the user gives his height and weight.
I used variable.insert() to insert a value so the program doesn't have errors.
Is there any way to have the program start 'empty' without getting errors? Basically, I need it to not do anything until the calculate button is pressed.
from Tkinter import *
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.height()
self.weigh()
self.output()
self.calculate()
def height(self):
Label(self, text = "Enter Height, feet").grid()
self.feet = Entry(self)
self.feet.grid(row = 0, column = 1)
self.feet.insert(0, "1")
Label(self, text = "Enter Height, inches").grid(row = 1, column = 0)
self.inches = Entry(self)
self.inches.grid(row = 1, column = 1)
self.inches.insert(0, "1")
def weigh(self):
Label(self, text = "Enter Weight").grid(row =2, column = 0)
self.weight = Entry(self)
self.weight.grid(row = 2, column = 1)
self.weight.insert(0, "1")
def output(self):
self.calcBMI = Button(self, text = "Calculate BMI")
self.calcBMI.grid(row = 6, columnspan = 2)
self.calcBMI["command"] = self.calculate
Label(self, text = "Body Mass Index").grid(row = 4, column = 0)
self.lblbmi = Label(self, bg = "#fff", anchor = "w", relief = "groove")
self.lblbmi.grid(row = 4, column = 1, sticky = "we")
Label(self, text = "Status").grid(row = 5, column = 0)
self.lblstat = Label(self, bg = "#fff", anchor = "w", relief = "groove")
self.lblstat.grid(row = 5, column = 1, sticky = "we")
def calculate(self):
ft = int(self.feet.get())
inch = int(self.inches.get())
ht = ft * 12 + inch
wt = int(self.weight.get())
bmi = (wt * 703) / (ht ** 2)
self.lblbmi["text"] = "%.2f" % bmi
if bmi > 30:
self.lblstat["text"] = "Obese"
elif bmi > 25:
self.lblstat["text"] = "Overweight"
elif bmi > 18.5:
self.lblstat["text"] = "Normal"
else:
self.lblstat["text"] = "Underweight"
def main():
app = App()
app.mainloop()
if __name__ == "__main__":
main()
What you are doing right now is calling the output outright(& also by pressing the calculate button). What you need to do is call it only when the button is pressed
From init() remove self.calculate().
Now just remove all the insert statements. You don't need them anymore ;)
Edit:
As Joe suggested in his comment, you could also disable the button until all the fields are populated. You could do this by setting its state to disabled,like this:
self.calcBMI.config(state='disabled')
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.