Hi there, I am making a program in python based on a Subway operation. All
descriptions and comments codes are provided in the attached program. I've
been using IDLE to run the program and it seems to work the way I want it
too.
However I have heard from many, that global variables are bad practise in
any programming language, and so I want to basically replace all my global
variables with another variable that does the same thing.
The problem is, some data is entered and then when the data is submitted,
the GUI window terminates and automatically re-opens for another data
entry. I need some sort of variable to store some data while the program
resetts itself.
Sorry is this sounds really broad, and I am happy to clarify and points.
''' Author: Elliott Andrews, Date: 22/09/16, Programmed with: IDLE 3.5.1 via
Python 3.5.1 and Tk 8.6.4
Program Title: "Phone Ordering System for SUBS R US."
Program Description: This program is designed for a company (e.g Subway) to
enter in an order for a sub as a phone operator. The phone operater will ask for
customer infomation, ingedients to be put in the sub and then order it. The
phone operator will then ask the customer if they wish to cancel the order and
if they
want to order another sub. After all subs have been ordered, the phone operator
will tell the customer the total cost of all the subs orders and then exit the
prgoram'''
from tkinter import * #Importing GUI from tkinter library to display in window.
import time #Importing time controls for diagnostic messages (readability).
#Global variables (major) have to be used to pass 'cancelled' and 'total_cost'
through whole application.
#This will ensure when window is told to destory, variables will be kept for a
maximum of 5 future orders and will avoid any 'Traceback' errors.
#Not using global variables or other means such as (.get) will not work because
when window is told to destory, a new window instance is created, defaulting
and resetting orig variables.
global cancelled
global total_cost
total_cost = 0 #Setting total cost (for all orders - max 5) to 0.
class Startup: #First class 'Startup' which will run diagnostic checks to make
sure program will run.
print("Current python version: 3.5.1 \nTk version: 8.6.4") #Recommended
version to run python and tkinter in.
if total_cost != 0: #If total_cost is not equal to 0, then... This
variable has to be set to 0, as it will be added too, each time a order is
added.
print("Sorry, the application failed to start. Please make sure
you have the latest python version and try again. \n Error: Failed to reset top
variabe (Total_Cost)")
else: #Otherwise, continue as normal.
print("Application starting...")
time.sleep (2) #Giving time for operator/user to see the status for
application startup.
class GUI(Frame, Startup): #Second class 'GUI' which will run the main display
(visual elements and will continue from first class).
def __init__(self, master): #Initalising the GUI application.
super(GUI, self).__init__(master) #For use with lots of mutiple
instances of __init__. Future use for parenting within 'master'.
self.grid() #Defining layout for GUI (grid method), could also use pack.
self.clear_variables() #Stating all functions with 'def'...
self.cancel()
self.create_widgets()
self.check_address()
self.show_order()
self.close_window()
def close_window(self): #Defining the process to close the window, reset
cancelled variable equal to 0, then quit.
global cancelled
cancelled = 0
root.quit() #Will prepare a quit function if needed.
def clear_variables(self): #Defining the process to set all other minor
variables to proper type and to set them to None or 0 where appropriate.
self.ordertype = StringVar() #StringVar is ideal for letters...
self.cusName = StringVar()
self.cusAddress = StringVar()
self.cusPhone = StringVar() #Can use IntVar for phone number, but for
textbox display only, StringVar is ok.
self.breads_var = StringVar()
self.cheeses_var = StringVar()
self.sauces_var = StringVar()
self.ordertype.set (None) #Has a 'None' value, similar to 'Null'.
self.cusName.set (None)
self.cusAddress.set (None)
self.cusPhone.set (None)
self.breads_var.set (None)
self.cheeses_var.set (None)
self.sauces_var.set (None)
self.cost = 0 #Since self.cost is to be a integer, integer operations
must be used
root.quit() #Will prepare a quit function if needed.
def cancel(self): #Defining the process for cancelling the order, to set
all other minor variables to proper type and to set them to None or 0 where
appropriate.
#This process allows orders that have been cancelled to continue the
program.
self.ordertype = StringVar()
self.cusName = StringVar()
self.cusAddress = StringVar()
self.cusPhone = StringVar()
self.breads_var = StringVar()
self.cheeses_var = StringVar()
self.sauces_var = StringVar()
self.ordertype.set (None)
self.cusName.set (None)
self.cusAddress.set (None)
self.cusPhone.set (None)
self.breads_var.set (None)
self.cheeses_var.set (None)
self.sauces_var.set (None)
global total_cost
total_cost-=self.cost #Following on from when order is cancelled,
subtract the cost of that order (since cancelled).
self.cost = 0
global cancelled #If cancel is true (when equal to 1) then do cancel
cancelled = 1
root.quit() #A quit function if needed
def create_widgets(self): #Define the function for 'create_widgets'.
Widgets are all the objects/clickables that appear on the GUI.
#Stored lists. These store the ingredients for the subs and will be
listed on the GUI for the user to choose from.
breads = ["White", "Wheat"] #"Multigrain" - Can be added to list, to
see GUI respond to new ingredients.
cheeses = ["Swiss", "Cheddar"] #Edam - Can be added...
sauces = ["Mayo"] #Honey Mustard - Can be added...
#Create a simple label (text format) which will display 'text=...' in a
certain table position. Text will align to the left (W = West).
Label(self,
text = "Welcome! Please enter customer's infomation and
sub-sandwich ingredients/choices. Note: All fields and required below..."
).grid(row = 0, column = 0, columnspan = 3, sticky = W)
Label(self,
text = "How will the customer receive the order?",
).grid(row = 1, column = 0, sticky = W)
#Using the radiobutton operation to create a radio button. Only two
options needed so list display is not really needed.
Radiobutton(self,
text = "Delivery ($3)", #Text for radio button to display.
variable = self.ordertype, #Variable to assign value to.
value = "Delivery", #Value to be used as variable.
command = self.check_address, #When radio button is
presses, go to module 'check_address'.
).grid(row = 2, column = 0, sticky = W)
Radiobutton(self,
text = "Pickup",
variable = self.ordertype,
value = "Pickup",
command = self.check_address,
).grid(row = 3, column = 0, sticky = W)
Label(self,
text = "Customer's Name:",
).grid(row = 4, column = 0, sticky = W)
self.cusName = Entry(self)
self.cusName.grid(row = 5,column = 0, sticky = W)
Label(self,
text = "Customer's Address:",
).grid(row = 6, column = 0, sticky = W)
self.cusAddress = Entry(self)
self.cusAddress.grid(row = 7,column = 0, sticky = W)
Label(self,
text = "Customer's Phone Number:",
).grid(row = 8, column = 0, sticky = W)
self.cusPhone = Entry(self)
self.cusPhone.grid(row = 9, column = 0, sticky = W)
Label(self,
text = "Bread Type ($2):",
).grid(row = 10, column = 0, sticky = W)
self.breads_btns = [] #Stating a empty list to be filled with
ingredients from lists above (i.e bread = ...)
for i in range(len(breads)): #Let 'i' be the variable to find the range
within the length of the list 'breads'. Essentialy will gather all bread
options.
rb = Radiobutton(self, variable = self.breads_var, value =
breads[i] , anchor = W, text = breads[i]) #List all breads as radiobuttons with
their assigned names in terms of 'i'.
self.breads_btns.append(rb) #Attach these radiobuttons to 'rb'.
rb.grid(row =(i + 11), column = 0, sticky = W) #State 'rb' to use
grid. Start from row 11 and list breads downwards and align to left.
Label(self,
text = "Cheeses ($1):", #Copy and pasted from above as cheese's
will use same code as bread's.
).grid(row = 10, column = 1, sticky = W)
self.cheeses_btns = []
for i in range(len(cheeses)):
rb = Radiobutton(self, variable = self.cheeses_var, value =
cheeses[i], anchor = W, text = cheeses[i])
self.cheeses_btns.append(rb)
rb.grid(row =(i + 11), column = 1, sticky = W)
Label(self,
text = "Salads ($2):",
).grid(row = 33, column = 0, sticky = W)
self.salads_lettuce = BooleanVar() #Defining salads as a BooleanVar
(True or False) since checkbuttons are needed to be setup differetly to
radiobuttons.
#Checkbuttons can store many values opposed to radiobuttons which only
store one, hence why my checkbuttons are listed seperatly.
Checkbutton(self,
text = "Lettuce", #Text to display next to checkbutton.
variable = self.salads_lettuce, #Variable to be true when
checkbutton is clicked.
).grid(row = 34, column = 0, sticky = W) #To be placed on
grid at row 34, aligned left.
self.salads_tomato = BooleanVar()
Checkbutton(self,
text = "Tomato",
variable = self.salads_tomato,
).grid(row = 35, column = 0, sticky = W)
self.salads_capsicum = BooleanVar()
Checkbutton(self,
text = "Capsicum",
variable = self.salads_capsicum,
).grid(row = 36, column = 0, sticky = W)
Label(self,
text = "Meats ($4):",
).grid(row = 33, column = 1, sticky = W)
self.meats_bacon = BooleanVar() #Same method for meats so copy and
pasted, as meats are checkboxes as well.
Checkbutton(self,
text = "Bacon",
variable = self.meats_bacon,
).grid(row = 34, column = 1, sticky = W)
self.meats_salami = BooleanVar()
Checkbutton(self,
text = "Salami",
variable = self.meats_salami,
).grid(row = 35, column = 1, sticky = W)
self.meats_ham = BooleanVar()
Checkbutton(self,
text = "Ham",
variable = self.meats_ham,
).grid(row = 36, column = 1, sticky = W)
Label(self,
text = "Sauces ($1):",
).grid(row = 55, column = 0, sticky = W)
self.sauces_btns = []
for i in range(len(sauces)): #Copy and pasted from above as sauces's
will use same code as cheese's.
rb = Radiobutton(self, variable = self.sauces_var, value =
sauces[i], anchor = W, text = sauces[i])
self.sauces_btns.append(rb)
rb.grid(row =(i + 56), column = 0, sticky = W)
Button(self, #Create a button by defing a button function.
text = "Show Order", #Text to display on the button
command = self.show_order, #When button is pressed, go to
show_order module (to display summary message by refresh).
).grid(row = 60, column = 0, sticky = W) #To be placed on grid
at row 60, aligned to left.
Button(self,
text = "Cancel Order",
command = self.cancel, #When button is pressed, go to cancel
module (to close current window and start again for another order).
).grid(row = 60, column = 1, sticky = W)
Button(self,
text = "Save and order another sub! (Max 5)",
command = self.close_window, #When button is pressed, go to
close_window module (to close window, save cost of sub and to order another
sub).
).grid(row = 60, column = 2, sticky = W)
Button(self,
text = "Finished? Exit program",
command = exit, #When button is pressed, simply exit program
(prompt will appear asking user to confirm to kill program).
).grid(row = 60, column = 3, sticky = W)
def check_address(self): #Defining the process to grey out option for
filling in address, when 'pickup' is selected.
orderselection[item_ct] = self.ordertype.get() #Get the value for
variable self.ordertype to determine if pickup or delivery is selected.
#Then put that value in another variable 'orderselection', to be
appended to the orderselction list (item count).
if orderselection[item_ct] == "Pickup": #If statement, if orderselction
has been choosen as 'pickup' then...
self.cusAddress['state'] = DISABLED #Set self.cusAddress (entry box
for address) to be disabled (grey out).
Label(self, state = DISABLED, #Also set the label for Customer's
Address to be disabled (grey out).
text = "Customer's Address:",
).grid(row = 6, column = 0, sticky = W) #Overwrite exisitng label
at grid location row 6, column 0 to grey out.
elif orderselection[item_ct] == "Delivery": #Else-if statement, elif
orderselction has been choosen as 'delivery' then...
self.cusAddress['state'] = NORMAL #Set self.cusAddress (entry box
for address) to be back to normal (un-grey).
Label(self, state = NORMAL, #Also set the label for Customer's
Address to be back to normal (un-grey).
text = "Customer's Address:",
).grid(row = 6, column = 0, sticky = W) #Overwrite exisitng label
at grid location row 6, column 0 to un-grey.
else: #Else statement, if none of these if statments are true, then
display the message in IDLE...
print("A orderselection has not been choosen")
def show_order(self): #Defining the process to display all order infomation
in text box and to calculate prices of all items selected.
saladschoice[item_ct] = "" #Setting items, if they have selected a
ceratin ingredient/item then assign to saladschoice list with relation to item
count [item_ct].
if self.salads_lettuce.get(): #If self.salads_lettuce has the value
lettuce selected, then add lettuce to the list to be displayed.
saladschoice[item_ct] += "Lettuce, " #Text to be added on, with a
comma and space for other salads (tidy formating).
if self.salads_tomato.get():
saladschoice[item_ct] += "Tomato, " #Copy and paste above
if self.salads_capsicum.get():
saladschoice[item_ct] += "Capsicum, " #Copy and paste above
meatschoice[item_ct] = "" #Setting items, if they have selected a
ceratin ingredient/item then assign to meatschoice list with relation to item
count [item_ct].
if self.meats_bacon.get(): #If self.meats_bacon has the value bacon
selected, then add bacon to the list to be displayed.
meatschoice[item_ct] += "Bacon, "
if self.meats_salami.get():
meatschoice[item_ct] += "Salami, " #Copy and paste above
if self.meats_ham.get():
meatschoice[item_ct] += "Ham, " #Copy and paste above
orderselection[item_ct] = self.ordertype.get() #Process of actual
assignment from ingredients selected to their defined list with relation to
item count (item_ct).
cname[item_ct] = self.cusName.get()
caddress[item_ct] = self.cusAddress.get()
cphone[item_ct] = self.cusPhone.get()
breadchoice[item_ct] = self.breads_var.get()
cheesechoice[item_ct] = self.cheeses_var.get()
#Note that salads and meats do not need a get operation as they have
already been performed above. Salads and meats once again need to get each value
#individually, to display mutiple values selected, meaning several
checks are needed for each.
saucechoice[item_ct] = self.sauces_var.get()
orderselection.append('')
#Append functions for storing ingredients in lists. Currently program
only displays total cost of all orders who have ordered many subs (max of 5).
#However extra code below will make it easy if this program was to be
extended in the future to show ingredients of all subs ordered for one summary
page.
#For this particuar program, this functionality is not needed, but the
code here would be able to add extra functionality if desired.
cname.append('') #These append function are telling all values using
that are using the 'get' operation to assign to the variable with relation to
[item_ct]
#Therefor enabling these statments here to attach/add all value into
the list.
caddress.append('')
cphone.append('')
breadchoice.append('')
cheesechoice.append('')
saladschoice.append('')
meatschoice.append('')
saucechoice.append('')
self.results_txt = Text(self, width = 60, height = 15, wrap = WORD)
#Creating the textbox as self.results_txt.
#Setting height and width and wraping text so text will appear on next
line instead of going off the page.
self.results_txt.grid (row = 61, column = 0) #Placment of text box will
be on grid, on row 61, column 0.
message = "Transportation of sub-sandwich to customer: " #Display first
statment of message text...
message += orderselection[item_ct] #Add onto previous statement, the
ordertype to display.
message += "\nCustomer's Name: " #And so on...
message += cname[item_ct]
message += "\nCustomer's Address: "
message += caddress[item_ct]
message += "\nCustomer's Phone: "
message += cphone[item_ct]
message += "\nBread Type: "
message += breadchoice[item_ct]
message += "\nCheese Type: "
message += cheesechoice[item_ct]
message += "\nSalads Selected: "
message += saladschoice[item_ct]
message += "\nMeats Selected: "
message += meatschoice[item_ct]
message += "\nSauces Selected: "
message += saucechoice[item_ct]
#Messages above will only display if...
#This long if statment has been created for purposes of validation,
meaning the customer must at least enter a certain amount of infomation (seen
below)
#TO be able to actually order (e.g It is no good someone ordering a sub
if no ingredients are selected!)
#!= stament mean if something is not equal to something then... (i.e
meaning the customer has to select something) and 'and' is to add another
condition
#instead of having mutiple if staments.
if orderselection[item_ct] != "None" and cname[item_ct] != "None" and
breadchoice[item_ct] != "None" and cheesechoice[item_ct] != "None" and
saucechoice[item_ct] != "None":
if orderselection[item_ct] == "Delivery": #Add a delivery cost
of 3 to self.cost variable if delivery is selected.
#self.cost is the cost of each sub ordered, it will be
used later on to add all costs together.
self.cost += 3
else:
print("Delivery/Pickup has not been selected, add a
cost of $3 if Delivery")
# In theory it is not possible for the program to run
this statment because of the validation statment, but is good practise to have
a secondary
#validation statment, if somehow the first one shall
fail.
if breadchoice[item_ct] != "None":
self.cost += 2
#If breadchoice does not equal none (i.e a bread has
been selected) then add a cost of 2.
else:
print("A bread is not selected and costs $2")
if cheesechoice[item_ct] != "None":
self.cost += 1
#And so on...
else:
print("A cheese is not selected and costs $1")
if saladschoice[item_ct] != "":
self.cost += 2
#Any type of salads will cost $2, meaning the salad
cost is one of the few options which will have (each cost charge)
#(e.g If I order just lettuce, the cost is $2, if order
lettuce, tomato and capsicum the cost is still $2)
else:
print("A salad/s is not selected, all selected cost $2")
if meatschoice[item_ct] != "":
self.cost += 4
else:
print("A meat/s is selected, all selected cost $4")
if saucechoice[item_ct] != "None":
self.cost += 1
message += "\n\nCost of this sub: $"
#Since if all staments have added all costs sucessfully
then we can display the total below:
message += str(self.cost) #Converting integer into
stringvar to display in text box.
message += "\n\nTotal overall cost: $"
global total_cost #Stating the total_cost variable
total_cost=total_cost+self.cost #Adding the single cost
to the total cost(which will keep value as next sub is ordered).
message += str(total_cost) #Converting integer into
stringvar to display in text box.
else:
print("A sauce has not been selected, and costs $1")
else:
print("Program is running through validation checks...
Complete!")
self.results_txt.delete(0.0, END) #Stating the end of the textbox
message
self.results_txt.insert(0.0, message) #Will insert all variables stated
as 'message' into text box.
choice = 0 #This is the number of subs ordered (will start as 0, since this is
the deafult order for counting (e.g 0,1,2,3))
item_ct=0 #This is also the number of subs ordered but will be used for
counting till a maximum of 5.
orderselection = [] #Again these statments need to be listed as all items and
ingredients need to be saved before another sub is to be ordered.
orderselection.append('') #Confirming that the above items have been ordered,
they are put in the list once more.
cname = []
cname.append('')
caddress = []
caddress.append('')
cphone = []
cphone.append('')
breadchoice = []
breadchoice.append('')
cheesechoice = []
cheesechoice.append('')
saladschoice = []
saladschoice.append('')
meatschoice = []
meatschoice.append('')
saucechoice = []
saucechoice.append('')
while item_ct < 5: #To run program one again, the ending and restarting
functions have all been put in a while item_ct (subs order count is less than 5)
#(i.e 4,3,2,1 and 0)
global cancelled #Stating the cancelled variable, to reset itself, so
when the program restarts, it won't keep cancelling in on itself, repeating.
cancelled=0
root = Tk() #The actual statment which opens the window, providing
there is an import statment for 'tkinter'.
toplevel = root.winfo_toplevel()#Setting window header to attach to top
of screen (first half of making window maximised)
toplevel.wm_state('zoomed')#Setting inner window to stretch window to
all sides of screen (second half of making window maximised)
#Two above statments automatically will maximise window when program
starts.
root.title("Phone Ordering System - SUBS R US") #Text title for the
window to display.
app = GUI(root) #Main application is defined in class, GUI with respect
to 'root'.
root.mainloop() #Enables program to repeat in a loop.
root.destroy() #Will close the window.
print("You have finished order number",str(item_ct))
if cancelled==0:
item_ct+=1 #Adding 1 to item_ct (sub order number)
if item_ct > 4: #When tem_ct is more than 4, then a maximum of 5 subs
has been ordered, the program should have been closed and a summary statement
#Will appear in IDLE.
print("The program has ended automatically as the maximum of 5
subs has been ordered")
print("Please wait, we are calculating your order total...")
time.sleep (5) #Giving a time of 5 seconds or the user to read.
print("You have ordered a maximum of 5 subs.")
print("The total cost of all subs ordered is $",str(total_cost))
else: #If item_cut is not > 4 the 'continue' the program (begin program
again).
print("Ready for a new order!")
continue
_______________________________________________
Tutor maillist - [email protected]
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor