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  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to