#!/usr/bin/python #Libraries to import import wx import time import threading #This is the data for numbers in the 7-segment display. #The item is a list, so segment[0] = "1110111". Each char in this string is whether that particular segment should be on or off. segments = ( "1110111", "0010010", "1011101", "0111010", "1101011", "1101111", "1010010", "1111111", "1111101") #Various IDs for use with wxpython (elements require IDs and IDs are used for setting up event handlers and the like) ident = {"buttonSet":1, "buttonZeroPump":2, "buttonZeroTotals":3, "buttonNozzle1":4, "buttonNozzle2":5, "txtSet":6, "spinSet":7, "txtStatus":8, "txtConsolePetrol":9, "txtConsolePrice":10, "txtConsoletotalPetrolDispensed":11, "txtConsoleTotalTakings":12, "txtPumpPetrol":13, "txtPumpPrice":14, "txtPencePerLitre":15, "menuQuit":16, "menuAOT":17} #Button settings: label and enabled. button = { ident["buttonSet"]:{"enable":False,"text":"set"}, ident["buttonZeroPump"]:{"enable":False,"text":"Zero Pump"}, ident["buttonZeroTotals"]:{"enable":False,"text":"Zero Totals"}, ident["buttonNozzle1"]:{"enable":True,"text":"Take Nozzle"}, ident["buttonNozzle2"]:{"enable":False,"text":"Squeese"}} #Config to be used throughout config = {"fillrate":0.1, "spinrate":0.1, "runthread":0, "titlestart":"Peter's Phunkeh Petrol Pumps", "windowstyles":0, "segw":8, "segh":1} #The main variables that are used a lot. pencePerLitre = 95.0 pencePerLitreBox = 95.0 pencePerLitreQueue = 0 petrolDispensed=0 totalTakings=0 totalPetrolDispensed=0 status = 'Ready' #Threading is required to make the UI still usable while the pump is filling up. #Essentially, the pump-filling routine needs to loop until the "Unsqueeze pump" button is pressed #This looping would freeze the UI (as you would expect), so had to make made asyncronous. class Monitor(threading.Thread): def run(self): global config, petrolDispensed, status while config["runthread"]: #runthread is set from the main class asyncronously. petrolDispensed = petrolDispensed + 0.1 #Increment the petrol if(petrolDispensed >= 100): #We can't fill above 100 litres petrolDispensed = 100 #Disable stuff: status = "Out Of Use" button[ident["buttonNozzle1"]]["enable"] = False button[ident["buttonNozzle2"]]["enable"] = False button[ident["buttonNozzle1"]]["text"] = "Take nozzle" button[ident["buttonNozzle2"]]["text"] = "Squeese" config["runthread"] = 0 #Stop loop. frame.UpdateUI() time.sleep(config["fillrate"]) #Slow down the filling-up #This is the main class. __init__ is called when the app starts class MyFrame(wx.Frame): def __init__(self, parent, id, title): self.MakeLayout(parent, id, title); self.MakeEventHandlers(); self.UpdateUI(); def MakeLayout(self, parent, id, title): #This function is called only once, on startup. It uses the wxwidgets toolkit to set up the GUI self.ui = {} wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, (600, 400)) #Create the frame self.panel = wx.Panel(self, -1, style=wx.EXPAND) #Create the panel #Menu setup: menubar = wx.MenuBar() options = wx.Menu() self.ui["menuAOT"] = options.Append(ident["menuAOT"], '&Always On Top', 'The window will always show above other windows', kind=wx.ITEM_CHECK) options.Append(ident["menuQuit"], '&Quit', 'Exit the application') menubar.Append(options, '&Options') self.SetMenuBar(menubar) #End menu setup. heading = wx.Font(9, wx.NORMAL, wx.NORMAL, wx.BOLD) # This will be reused for subtitles. #--- TOP LEFT CONTENT --------------------------------------------------- self.ui["txtStatus"] = wx.StaticText(self.panel, ident["txtStatus"], "Ready"); self.ui["txtConsolePetrol"] = wx.StaticText(self.panel, ident["txtConsolePetrol"] , "0.0 litres") self.ui["txtConsolePrice"] = wx.StaticText(self.panel, ident["txtConsolePrice"] , "0.00") self.ui["txtConsoletotalPetrolDispensed"] = wx.StaticText(self.panel, ident["txtConsoletotalPetrolDispensed"] , "0.0 litres") self.ui["txtConsoleTotalTakings"] = wx.StaticText(self.panel, ident["txtConsoleTotalTakings"] , "0.00") a1cont = [ wx.StaticText(self.panel, -1, "Pump details"),wx.StaticText(self.panel, -1, ""), wx.StaticText(self.panel, -1, " Status:"),self.ui["txtStatus"], wx.StaticText(self.panel, -1, " Petrol dispensed:"),self.ui["txtConsolePetrol"], wx.StaticText(self.panel, -1, " Price:"),self.ui["txtConsolePrice"], wx.StaticText(self.panel, -1, "Today's totals"),wx.StaticText(self.panel, -1, ""), wx.StaticText(self.panel, -1, " Petrol:"),self.ui["txtConsoletotalPetrolDispensed"], wx.StaticText(self.panel, -1, " Takings:"),self.ui["txtConsoleTotalTakings"] ] #--- BOTTOM LEFT CONTENT ------------------------------------------------ self.ui["buttonSet"] = wx.Button(self.panel, ident["buttonSet"], "set",style=wx.EXPAND) self.ui["buttonSet"].SetMinSize((40, 26)) self.ui["txtSet"] = wx.TextCtrl(self.panel, ident["txtSet"], "%.1f" % pencePerLitre,style=wx.EXPAND) b1acont = [ wx.StaticText(self.panel, -1, "Pence per litre:",style=wx.EXPAND), self.ui["txtSet"], wx.SpinButton(self.panel, ident["spinSet"],style=wx.EXPAND), self.ui["buttonSet"] ] b1bcont = [ wx.Button(self.panel, ident["buttonZeroPump"], "Zero pump",style=wx.EXPAND), wx.Button(self.panel, ident["buttonZeroTotals"], "Zero today's totals",style=wx.EXPAND) ] #--- TOP RIGHT CONTENT -------------------------------------------------- #Currently, the seven segment display is disabled until I can resolve some windows-specific problems #self.ui["txtPumpPrice"] = self.MakeLCD(3,1,"7sPumpPrice") #self.ui["txtPumpPetrol"] = self.MakeLCD(2,1,"7sPumpPetrol") #self.ui["txtPencePerLitre"] = self.MakeLCD(5,3,"7sPencePerLitre") self.ui["txtPumpPrice"] = wx.StaticText(self.panel, ident["txtPumpPrice"], "0.00") self.ui["txtPumpPetrol"] = wx.StaticText(self.panel, ident["txtPumpPetrol"], "0.0 litres") self.ui["txtPencePerLitre"]=wx.StaticText(self.panel, ident["txtPencePerLitre"], "%.1f" % pencePerLitre) c1cont = [ wx.StaticText(self.panel, -1, " Amount to pay:"), (self.ui["txtPumpPrice"], 1, wx.SHAPED, 0), wx.StaticText(self.panel, -1, " Petrol"), self.ui["txtPumpPetrol"], wx.StaticText(self.panel, -1, " Pence per litre"),self.ui["txtPencePerLitre"] ] #--- BOTTOM RIGHT CONTENT ----------------------------------------------- self.ui["buttonNozzle1"] = wx.ToggleButton(self.panel, ident["buttonNozzle1"], 'Take Nozzle',style=wx.EXPAND) self.ui["buttonNozzle1"].SetMinSize((128,32)) self.ui["buttonNozzle2"] = wx.ToggleButton(self.panel, ident["buttonNozzle2"], "Squeese",style=wx.EXPAND) self.ui["buttonNozzle2"].SetMinSize((128,32)) self.ui["buttonNozzle2"].Enable(False) d1cont = [self.ui["buttonNozzle1"],self.ui["buttonNozzle2"]] #--- MAIN LAYOUT -------------------------------------------------------- #This is where all the pieces created above are put together # Set up root sizer sizer0 = wx.GridSizer(2, 2, 0, 8) # Set up quadrants temp = wx.StaticBox( self.panel, id, "Console display" ) temp.SetFont(heading) sizera = wx.StaticBoxSizer( temp, wx.VERTICAL ) temp = wx.StaticBox( self.panel, id, "Console controls" ) temp.SetFont(heading) sizerb = wx.StaticBoxSizer( temp, wx.VERTICAL ) temp = wx.StaticBox( self.panel, id, "Pump display" ) temp.SetFont(heading) sizerc = wx.StaticBoxSizer( temp, wx.VERTICAL ) temp = wx.StaticBox( self.panel, id, "Pump controls" ) temp.SetFont(heading) sizerd = wx.StaticBoxSizer( temp, wx.VERTICAL ) #Set up lower-level sizers sizera1 = wx.GridSizer(9, 2, 3, 3) sizerb1 = wx.BoxSizer(wx.VERTICAL) sizerb1a = wx.BoxSizer(wx.HORIZONTAL) sizerb1b = wx.BoxSizer(wx.HORIZONTAL) sizerc1 = wx.GridSizer(3, 2, 3, 3) sizerd1 = wx.BoxSizer(wx.HORIZONTAL) #Add content to lower-level sizers sizera1.AddMany(a1cont) sizerb1.AddMany([sizerb1a, (sizerb1b,0,wx.ALIGN_CENTER_HORIZONTAL)]) sizerb1a.AddMany(b1acont) sizerb1b.AddMany(b1bcont) sizerc1.AddMany(c1cont) sizerd1.AddMany(d1cont) #Add lower-level sizers to quadrants sizera.Add(sizera1) sizerb.Add(sizerb1, flag=wx.EXPAND) sizerc.Add(sizerc1, 1, wx.EXPAND, 0) sizerd.Add(sizerd1, flag=wx.ALIGN_CENTER_HORIZONTAL) #Add quadrants to root sizer sizer0.Add(sizera, flag=wx.EXPAND) sizer0.Add(sizerc, flag=wx.EXPAND) sizer0.Add(sizerb, flag=wx.EXPAND) sizer0.Add(sizerd, flag=wx.EXPAND) #Setup main frame self.panel.SetSize(self.panel.GetEffectiveMinSize()) self.panel.SetSizer(sizer0) self.Centre() config["windowstyles"] = self.GetWindowStyleFlag() def UpdateLCD(self, value, key): global segments print segments[int(str(value)[0])] #Finish this! def MakeLCD(self, digits, point, key): width=(digits*4)+2 #This is the number of columns in the gridsizer self.ui[key] = {"ref":wx.GridSizer(5, width, 5, 5),0:{},1:{},2:{},3:{},4:{}} #Basic setup for the gridsizer for x in range(0,5): #Loop through rows switch = x % 2 # Are we on an odd or an even row? #Each sement is given a key, 0 through 7. These references below calculate this reference based on the row. ref1 = (3/2)*x #segments 0, 3, 6 ref2 = (3/2)*x-(1/2) #segments 1, 4 ref3 = (3/2)*x+(1/2) #segments 2, 5 for y in range(0,digits): #Loop through each digit if(y==point): #Should we insert a decimal point? if(x<4): self.ui[key]["ref"].Add(wx.Panel(self.panel, -1)) #space self.ui[key]["ref"].Add(wx.Panel(self.panel, -1)) #space print " ", else: self.ui[key]["point"] = wx.Panel(self.panel, -1, size=(config["segh"],config["segh"])) self.ui[key]["point"] .SetBackgroundColour(wx.BLACK) self.ui[key]["ref"].Add(self.ui[key]["point"] ) #point self.ui[key]["ref"].Add(wx.Panel(self.panel, -1)) #space print ".", if(switch==0): #An even row (meaning space|segment|space|space|) self.ui[key]["ref"].Add(wx.Panel(self.panel, -1, size=(config["segh"],config["segh"]), style=wx.EXPAND)) #blank self.ui[key][y][ref1] = wx.Panel(self.panel, -1, size=(config["segw"],config["segh"]), style=wx.EXPAND) self.ui[key][y][ref1].SetBackgroundColour(wx.BLACK) self.ui[key]["ref"].Add(self.ui[key][y][ref1]) #seg self.ui[key]["ref"].Add(wx.Panel(self.panel, -1, size=(config["segh"],config["segh"]), style=wx.EXPAND)) #blank self.ui[key]["ref"].Add(wx.Panel(self.panel, -1, size=(config["segh"],config["segh"]), style=wx.EXPAND)) #space print " - ", else: #An odd row (meaning segment|space|segment|space|) self.ui[key][y][ref2] = wx.Panel(self.panel, -1, size=(config["segh"],config["segw"]), style=wx.EXPAND) self.ui[key][y][ref2].SetBackgroundColour(wx.BLACK) self.ui[key]["ref"].Add(self.ui[key][y][ref2]) #seg self.ui[key]["ref"].Add(wx.Panel(self.panel, -1, size=(config["segw"],config["segw"]), style=wx.EXPAND)) #blank self.ui[key][y][ref3] = wx.Panel(self.panel, -1, size=(config["segh"],config["segw"]), style=wx.EXPAND) self.ui[key][y][ref3].SetBackgroundColour(wx.BLACK) self.ui[key]["ref"].Add(self.ui[key][y][ref3]) #seg self.ui[key]["ref"].Add(wx.Panel(self.panel, -1, size=(config["segh"],config["segw"]), style=wx.EXPAND)) #space print "| |", print return self.ui[key]["ref"] #return the created ui object def MakeEventHandlers(self): #This sets up the event handlers for various UI elements self.Bind(wx.EVT_BUTTON, self.OnSet, id=ident["buttonSet"]) self.Bind(wx.EVT_SPIN_UP, self.OnSetSpinUp, id=ident["spinSet"]) self.Bind(wx.EVT_SPIN_DOWN, self.OnSetSpinDown, id=ident["spinSet"]) self.Bind(wx.EVT_BUTTON, self.OnZeroPump, id=ident["buttonZeroPump"]) self.Bind(wx.EVT_BUTTON, self.OnZeroTotals, id=ident["buttonZeroTotals"]) self.Bind(wx.EVT_TOGGLEBUTTON, self.OnbuttonNozzle1, id=ident["buttonNozzle1"]) self.Bind(wx.EVT_TOGGLEBUTTON, self.OnbuttonNozzle2, id=ident["buttonNozzle2"]) self.Bind(wx.EVT_MENU, self.OnQuit, id=ident["menuQuit"]) self.Bind(wx.EVT_MENU, self.OnAOT, id=ident["menuAOT"]) def UpdateUI(self): #As suggested by the title, this function updates things in the UI (text contents, button labels, etc) temp_price = petrolDispensed * pencePerLitre / 100 if(petrolDispensed < 0.5 and config["runthread"]): temp_price = 0.5 * pencePerLitre / 100 #--- Console Display ---# self.ui["txtStatus"].SetLabel(status) self.ui["txtConsolePetrol"].SetLabel("%.1f litres" % petrolDispensed) self.ui["txtConsolePrice"].SetLabel("%.2f" % temp_price) self.ui["txtConsoletotalPetrolDispensed"].SetLabel("%.1f litres" % totalPetrolDispensed) self.ui["txtConsoleTotalTakings"].SetLabel("%.2f" % totalTakings) #--- Console Controls ---# self.ui["txtSet"].SetValue(str(pencePerLitreBox)) #--- Pump Display ---# self.ui["txtPumpPrice"].SetLabel("%.2f" % temp_price) self.ui["txtPumpPetrol"].SetLabel("%.1f litres" % petrolDispensed) self.ui["txtPencePerLitre"].SetLabel("%.1f" % pencePerLitre) #--- Pump Controls ---# self.ui['buttonNozzle1'].Enable(button[ident["buttonNozzle1"]]["enable"]) self.ui['buttonNozzle2'].Enable(button[ident["buttonNozzle2"]]["enable"]) self.ui['buttonNozzle1'].SetLabel(button[ident["buttonNozzle1"]]["text"]) self.ui['buttonNozzle2'].SetLabel(button[ident["buttonNozzle2"]]["text"]) #---- Title ---# self.SetTitle(config["titlestart"]+" :: "+status) def OnQuit(self, event): #Still need to figure this one out def OnAOT(self,event): #"Always On Top" was checked or unchecked on the menu. Act accordingly. if self.ui["menuAOT"].IsChecked(): self.SetWindowStyleFlag(config["windowstyles"]|wx.STAY_ON_TOP) else: self.SetWindowStyleFlag(config["windowstyles"]) def OnSet(self, event): #The "set" button was clicked to set the pence-per-litre global pencePerLitre, pencePerLitreQueue, pencePerLitreBox pencePerLitreBox = float(self.ui["txtSet"].GetValue()) #First get the float value of the input box if(pencePerLitreBox >= 0 and pencePerLitreBox < 199.9): #Is it in range? if status == "Ready": #PPL can only be changed if the pump is ready (to avoid ripping people off) pencePerLitre = float("%.1f" % float(pencePerLitreBox)) # Change it! (be sure to format it too) else: pencePerLitreQueue = "%.1f" % float(pencePerLitreBox) # Add the new value to the queue #Display a handy dialog box notifying pete of his incompetence. d = wx.MessageDialog(self, "As the pump isn't ready, the change in pence-per-litre will take effect when you next zero the pump", "Notice", wx.OK) d.ShowModal() d.Destroy() else: pencePerLitreBox = pencePerLitre #Revert input box value #Display a handy dialog box notifying pete of his incompetence. d = wx.MessageDialog(self, "Pence Per Litre can only be set to values between 0 and 199.9", "Notice", wx.OK) d.ShowModal() d.Destroy() self.UpdateUI(); def OnSetSpinUp(self, event): #Increment the PPL input box value global pencePerLitreBox pencePerLitreBox = pencePerLitreBox+config["spinrate"] self.UpdateUI(); def OnSetSpinDown(self, event): #Decrement the PPL input box value global pencePerLitreBox pencePerLitreBox = pencePerLitreBox-config["spinrate"] self.UpdateUI(); def OnZeroPump(self, event): #Zero the amount of petrol dispensed and add it to today's total global petrolDispensed, pencePerLitre, pencePerLitreQueue, totalPetrolDispensed, totalTakings, status if status == "In Use": d = wx.MessageDialog(self, "Pump cannot be zeroed until Nozzle is replaced", "Error", wx.OK | wx.ICON_ERROR) d.ShowModal() d.Destroy() else: button[ident["buttonNozzle1"]]["enable"] = True #Re-enable use of the pump totalPetrolDispensed = totalPetrolDispensed + petrolDispensed #Add petrol to total totalTakings = petrolDispensed * pencePerLitre / 100 #Add moneyz to total petrolDispensed = 0 #Reset pump petrol status = "Ready" #Reset status if pencePerLitreQueue: #Adjust PPL if queued. pencePerLitre = pencePerLitreQueue pencePerLitreQueue = 0; self.UpdateUI() def OnZeroTotals(self, event): #Zero today's totals #Really we should have a confirmation here. global totalPetrolDispensed, totalTakings totalPetrolDispensed = 0 totalTakings = 0 self.UpdateUI() def OnbuttonNozzle1(self, event): #Handler for the "take nozel" / "replace nozel" button global status if status == "In Use": #Nozel is being replaced button[ident["buttonNozzle2"]]["enable"] = False #Disallow squeesing button[ident["buttonNozzle1"]]["text"] = "Take Nozzle" #Set label accordingly if petrolDispensed == 0: status = "Ready" else: status = "Out of Use" button[ident["buttonNozzle1"]]["enable"] = False #As petrol has been dispensed, the pump can no longer be used else: #Nozel is being taken button[ident["buttonNozzle1"]]["text"] = "Replace Nozzle" button[ident["buttonNozzle2"]]["enable"] = True status = "In Use" self.UpdateUI() def OnbuttonNozzle2(self, event): global config if config["runthread"]: # Is the pump already being filled? config["runthread"]=0 # Stop the filling button[ident["buttonNozzle2"]]["text"] = "Squeese" button[ident["buttonNozzle1"]]["enable"] = True else: #Start up the pumping class config["runthread"]=1 thread = Monitor() thread.start() button[ident["buttonNozzle2"]]["text"] = "Unsqueese" button[ident["buttonNozzle1"]]["enable"] = False self.UpdateUI() #Run the app! app = wx.App(0) frame = MyFrame(None, -1, config["titlestart"]) frame.Show(True) app.MainLoop()