def __init__(self, master = None): """Application constructor method. """ # run super constructor method tk.Frame.__init__(self, master) # set alignment inside the frame self.grid() # create widgets self.__createDisplay() # display canvas: holds the image, CPs and spaces self.__createMenu() # menu canvas: holds the buttons and menu bar image # lists to hold parking space and control point references self.__parking_spaces = Boxes(self.display, type = 0) self.__control_points = Boxes(self.display, type = 1) # create mouse button and key-press handlers -> set focus to this frame self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set() if self.__is_verbose: print "INFO: __parking_spaces length:", self.__parking_spaces.length() print "INFO: __control_points length:", self.__control_points.length() # load the default background self.loadImage(self.DEFAULT_IMAGE, self.display, s.PICTURE_RESOLUTION[0]/2, s.PICTURE_RESOLUTION[1]/2)
def task(cont = False): # Delete and redraw the current box Boxes.getCur().delete_rect(SelWindow.w).draw_rect(SelWindow.w) # Update the info text at the top SelWindow.w.delete(SelWindow.text) SelWindow.text = SelWindow.w.create_text((s.WINDOW_WIDTH/2, 10), text = str(Boxes.sel) + "selected - O = save output, 1-9 to change box num, T = toggle type, C = clear current box, close window to return to setup") if cont: SelWindow.master.after(2000,task) # reschedule event in 2 seconds
def task(cont=False): # Delete and redraw the current box Boxes.getCur().delete_rect(SelWindow.w).draw_rect(SelWindow.w) # Update the info text at the top SelWindow.w.delete(SelWindow.text) SelWindow.text = SelWindow.w.create_text( (s.WINDOW_WIDTH / 2, 10), text=str(Boxes.sel) + "selected - O = save output, 1-9 to change box num, T = toggle type, C = clear current box, close window to return to setup" ) if cont: SelWindow.master.after(2000, task) # reschedule event in 2 seconds
def output_coords(): # Open the file to output the co-ordinates to f1 = open('./setup_data.py', 'w+') # Print the dictionary data to the file print >> f1, 'boxes = [' for i in range(Boxes.length()): c = Boxes.get(i).get_output(SelWindow.bgcoords) if c != None: o = (i) print >> f1, c, ',' print >> f1, ']' print 'INFO: Box data saved in file boxdata.py.' tkMessageBox.showinfo("Pi Setup", "Box data saved in file.")
def output_coords(): # Open the file to output the co-ordinates to f1 = open('./setup_data.py', 'w+') # Print the dictionary data to the file print >>f1, 'boxes = [' for i in range(Boxes.length()): c = Boxes.get(i).get_output(SelWindow.bgcoords) if c != None: o = (i) print >>f1, c, ',' print >>f1, ']' print 'INFO: Box data saved in file boxdata.py.' tkMessageBox.showinfo("Pi Setup", "Box data saved in file.")
def __init__(self, master=None): tk.Frame.__init__(self, master) self.grid() self.__createDisplay() self.__createMenu() self.__parking_spaces = Boxes(self.display, type=0) self.__control_points = Boxes(self.display, type=1) self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set()
def __init__(self, master=None): """Application constructor method. """ # run super constructor method tk.Frame.__init__(self, master) # set alignment inside the frame self.grid() # create widgets self.__createDisplay( ) # display canvas: holds the image, CPs and spaces self.__createMenu( ) # menu canvas: holds the buttons and menu bar image # lists to hold parking space and control point references self.__parking_spaces = Boxes(self.display, type=0) self.__control_points = Boxes(self.display, type=1) # create mouse button and key-press handlers -> set focus to this frame self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set() if self.__is_verbose: print "INFO: __parking_spaces length:", self.__parking_spaces.length( ) print "INFO: __control_points length:", self.__control_points.length( ) # load the default background self.loadImage(self.DEFAULT_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2)
def callbackKey(event): key = (event.char) try: # Make an output file if key == 'o': output_coords() # Swap the type of the box if key == 't': Boxes.getCur().swap_type() # Clear the current box if key == 'c': Boxes.getCur().clear() # Switch the setting to a new number i = int(key) if i < 10 and i >= 0: Boxes.setCur(i) print "INFO: Switching to", i except: pass task()
class Application(tk.Frame): # -------------------------------------------------------------------------- # Instance Attributes # -------------------------------------------------------------------------- # booleans __is_verbose = s.IS_VERBOSE # print messages to terminal __is_saved = False # lists to hold parking space and control point references __parking_spaces = None __control_points = None # picamera __camera = None __camera_is_active = False # image load/save locoations SETUP_IMAGE = "./images/setup.jpeg" DEFAULT_IMAGE = "./images/default.jpeg" # -------------------------------------------------------------------------- # Constructor Method # -------------------------------------------------------------------------- def __init__(self, master = None): """Application constructor method. """ # run super constructor method tk.Frame.__init__(self, master) # set alignment inside the frame self.grid() # create widgets self.__createDisplay() # display canvas: holds the image, CPs and spaces self.__createMenu() # menu canvas: holds the buttons and menu bar image # lists to hold parking space and control point references self.__parking_spaces = Boxes(self.display, type = 0) self.__control_points = Boxes(self.display, type = 1) # create mouse button and key-press handlers -> set focus to this frame self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set() if self.__is_verbose: print "INFO: __parking_spaces length:", self.__parking_spaces.length() print "INFO: __control_points length:", self.__control_points.length() # load the default background self.loadImage(self.DEFAULT_IMAGE, self.display, s.PICTURE_RESOLUTION[0]/2, s.PICTURE_RESOLUTION[1]/2) # ============================================================================== # # Public Application Methods # # ============================================================================== # -------------------------------------------------------------------------- # Load Image # -------------------------------------------------------------------------- def loadImage(self, image_address, canvas, width, height): """ Load image at image_address. If the load is successful then return True, otherwise return False. Keyword Arguments: image_address -- The address of the image to be loaded (default = './'). canvas -- The Tkinter Canvas into which the image is loaded. width -- Width of the image to load. height -- Height of the image to load. Returns: Boolean -- True if load successful, False if not. """ # clear the old canvas canvas.delete(tk.ALL) try: # guard against incorrect argument datatypes if not isinstance(canvas, tk.Canvas): raise TypeError if not isinstance(image_address, str): raise TypeError if not isinstance(width, int): raise TypeError if not isinstance(height, int): raise TypeError # load the image into the canvas photo = ImageTk.PhotoImage(Image.open(image_address)) canvas.create_image((width, height), image = photo) canvas.image = photo # image load successful return True except TypeError: # arguments of incorrect data type, load unsuccessful if self.__is_verbose: print "ERROR: loadImage() arguments of incorrect data type." return False except: # image failed to load if self.__is_verbose: print "ERROR: loadImage() failed to load image " + image_address return False # -------------------------------------------------------------------------- # Activate the Pi Camera # -------------------------------------------------------------------------- def turnOnCamera(self): """ Instruct the user how to take a new setup image, then activate the PiCam. If the camera fails to load, catch the exception and present error message. """ # show quick dialogue box with basic instruction tkMessageBox.showinfo(title = "", message = "Press the ENTER key to take a new setup image " + "or the ESCAPE key to cancel.") try: # initialise the camera using the settings in the imageread module self.__camera = imageread.setup_camera(is_fullscreen = True) self.__camera.start_preview() self.__camera_is_active = True if self.__is_verbose: print "INFO: PiCam activated." except: # camera failed to load, display error message tkMessageBox.showerror(title = "Error!", message = "Error: Failed to setup and start PiCam.") # -------------------------------------------------------------------------- # Save Data # -------------------------------------------------------------------------- def saveData(self): """Save the CP and parking space reference data to ./setup_data.py. """ # Open the file to output the co-ordinates to f1 = open('./setup_data.py', 'w+') # Print the dictionary data to the file print >> f1, 'boxes = [' # for every parking space, save the data to ./setup_data.py for i in range(self.__parking_spaces.length()): space = self.__parking_spaces.get(i).getOutput() # ignore the space if no data present if space != None: o = (i) print >> f1, space, ',' # for every control point, save the data to ./setup_data.py for j in range(self.__control_points.length()): cp = self.__control_points.get(j).getOutput() # ignore the CP if no data present if cp != None: o = (i) print >> f1, cp, ',' # save to ./setup_data.py print >> f1, ']' self.__is_saved = True if self.__is_verbose: print 'INFO: Data saved in file setup_data.py.' tkMessageBox.showinfo(title = "PiPark Setup", message = "Data saved successfully.") # -------------------------------------------------------------------------- # Load Data # -------------------------------------------------------------------------- def loadData(self): try: # load the setup data, reload to refresh the data import setup_data reload(setup_data) except: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" tkMessageBox.showerror( title = "Error!", message = "Problem loading data from setup_data.py" ) self.__is_saved = True return setup_data.boxes # -------------------------------------------------------------------------- # Check Data # -------------------------------------------------------------------------- def checkData(self): """ Check that the setup data meets the following criteria: 1) There is at least 1 parking space. 2) There are exactly 3 control points. Returns: Boolean -- True if criteria is met, False if not. """ if self.__is_verbose: print "INFO: Data is being checked for validity." # get the boxes data to check from setup_data try: # load the setup data import setup_data reload(setup_data) # ensure that boxes exists and is not empty box_data = setup_data.boxes if not box_data: raise ValueError except ImportError: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" except ValueError: if self.__is_verbose: print "ERROR: ./setup_data.py 'boxes' is empty." except: if self.__is_verbose: print "ERROR: ./setup_data.py does not contain 'boxes'." # create lists to hold data of each type space_boxes = [] control_boxes = [] # append the box data to space or control list as appropriate for data_set in box_data: if data_set[1] == 0: space_boxes.append(data_set) elif data_set[1] == 1: control_boxes.append(data_set) elif self.__is_verbose: print "ERROR: Box-type not set to either 0 or 1." # data is valid if there is at least 1 space and exactly 3 control points if len(space_boxes) > 0 and len(control_boxes) == 3: valid_data = True else: valid_data = False if self.__is_verbose: print "INFO: Data checked. Data is", valid_data return valid_data # -------------------------------------------------------------------------- # Register Data # -------------------------------------------------------------------------- def register(self): """Register the Pi with the server. """ # if setup hasn't been saved recently since last change, ask the if # user to save first if not self.__is_saved: response = tkMessageBox.askokcancel(title = "Save Setup", message = "Setup data must be saved before the registration" + " process can be completed. Would you like to save now?") # if user selects 'yes' save the data and continue, else do not # register the pi if response: self.saveData() else: tkMessageBox.showinfo(title = "PiPark Setup", message = "Registration not completed.") return # check that most recent saved data is valid (#CPs == 3, #Spaces > 0) if not self.checkData(): # data invalid, so display message and return tkMessageBox.showinfo( title = "PiPark Setup", message = "Registration not complete.\n\nSaved data is " + "invalid. Please ensure that there are 3 control points and " + "at least 1 parking spaces marked." ) return # attempt to import the setup data and ensure 'boxes' is a list try: import setup_data reload(setup_data) boxes = setup_data.boxes if not isinstance(boxes, list): raise ValueError() except: print "ERROR: Setup data does not exist. Please run options 1 and 2 first." return # attempt to import the server senddata module try: import senddata except: print "ERROR: Could not import send data file." return # deregister all areas associated with this pi (start fresh) out = senddata.deregister_pi() try: out['error'] print "ERROR: Error in connecting to server. Please update settings.py." return except: pass # register each box on the server for box in boxes: if box[1] == 0: output = senddata.register_area(box[0]) if "error" in output.keys(): if self.__is_verbose: print "ERROR:", output["error"] return else: if self.__is_verbose: print "INFO: Registering area", box[0], "on server." # print success message if verbose if self.__is_verbose: print "\nINFO: Server registration successful." # ============================================================================== # # Event Handlers # # ============================================================================== # -------------------------------------------------------------------------- # Return-key-press Event Handler # -------------------------------------------------------------------------- def returnPressHandler(self, event): """ Handle Return-key-press events. Capture a new setup image when PiCam is active, and load the image. """ # ensure focus on window self.focus_set() # do nothing if camera is not active, or no camera object exists if not self.__camera_is_active or not self.__camera: return try: # capture new setup image, then close the camera self.__camera.capture(self.SETUP_IMAGE) self.__camera.stop_preview() self.__camera.close() self.__camera_is_active = False if self.__is_verbose: print "INFO: New setup image captured." print "INFO: PiCam deactivated." except: # image failed to capture, show error message tkMessageBox.showerror(title = "Error!", message = "Error: Failed to capture new setup image.") # load the new setup image self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0]/2, s.PICTURE_RESOLUTION[1]/2) # activate buttons if they're disabled self.cps_button.config(state = tk.ACTIVE) self.spaces_button.config(state = tk.ACTIVE) # -------------------------------------------------------------------------- # Escape-key-press Event Handler # -------------------------------------------------------------------------- def escapePressHandler(self, event): # ensure focus on window self.focus_set() # do nothing if camera is not active, or no camera object exists if not self.__camera_is_active or not self.__camera: return try: # close the camera without taking new image self.__camera.stop_preview() self.__camera.close() self.__camera_is_active = False if self.__is_verbose: print "INFO: PiCam deactivated." except: # image failed to close for some reason, show error message if self.__is_verbose: print "ERROR: PiCam failed to close correctly." # -------------------------------------------------------------------------- # Key-press Event Handler # -------------------------------------------------------------------------- def keyPressHandler(self, event): """Handle key-press events for numeric keys. """ key = event.char NUM_KEYS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'] if key in NUM_KEYS: if self.__is_verbose: print "INFO: Number-key pressed", key if self.spaces_button.getIsActive(): self.__parking_spaces.setCurrentBox(int(key)) if self.cps_button.getIsActive(): # ignore all other numbers, but 1, 2 and 3 as 3 is the maximum # number of control points allowed. if key not in ['1', '2', '3']: return # NB: -1 from key press, because list indices are [0, 1, 2], # but for ease of user selection the numbers 1, 2, 3 are used # for input self.__control_points.setCurrentBox(int(key) - 1) # -------------------------------------------------------------------------- # LMB Event Handler # -------------------------------------------------------------------------- def leftClickHandler(self, event): """Handle LMB-click events to add/remove control points & spaces. """ # ensure focus on display canvas to recieve mouse clicks self.display.focus_set() # perform correct operation, dependent on which toggle button is active # add new control points (max = 3) if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Add Control Point" self.__is_saved = False this_cp_id = self.__control_points.getCurrentBox() this_cp = self.__control_points.boxes[this_cp_id] this_cp.updatePoints(event.x, event.y) # add new parking space elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Add Parking Space" self.__is_saved = False this_space_id = self.__parking_spaces.getCurrentBox() this_space = self.__parking_spaces.boxes[this_space_id] this_space.updatePoints(event.x, event.y) # do nothing -- ignore LMB clicks else: if self.__is_verbose: print "INFO: Just clicking LMB merrily =D" # return focus to the main frame for key-press events self.focus_set() # -------------------------------------------------------------------------- # RMB Event Handler # -------------------------------------------------------------------------- def rightClickHandler(self, event): """Handle RMB-click events to add/remove control points & spaces. """ # ensure focus is set to the display canvas self.display.focus_set() # perform correct operation, dependent on which toggle button is active if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Remove Control Point" self.__is_saved = False self.__control_points.boxes[self.__control_points.getCurrentBox()].clear() self.__control_points.boxes[self.__control_points.getCurrentBox()].deleteRectangle(self.display) elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Remove parking space" self.__is_saved = False self.__parking_spaces.boxes[self.__parking_spaces.getCurrentBox()].clear() self.__parking_spaces.boxes[self.__parking_spaces.getCurrentBox()].deleteRectangle(self.display) else: if self.__is_verbose: print "INFO: Just clicking RMB merrily =)" # return focus to the main frame for key-press events self.focus_set() # ============================================================================== # # Button Handlers # # ============================================================================== def clickStart(self): """ Close the current setup application, then initiate the main PiPark program. """ if self.__is_verbose: print "ACTION: Clicked 'Start'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # set initial responses response = False response1 = False response2 = False # if setup data has not been saved. Ask user if they would like to save # before continuing. if not self.__is_saved: response = tkMessageBox.askyesno( title = "Save Setup", type = tkMessageBox.YESNOCANCEL, message = "Most recent changes to setup have not been saved." + "Would you like to save before running PiPark?" ) if response: self.saveData() # data is saved, ask the user if they are sure they wish to quit. else: response = tkMessageBox.askyesno( title = "Save Setup", message = "Are you ready to leave setup and run PiPark?" ) # user wishes to quit setup and run pipark, so do it! if response: # ensure data is valid before continuing if not self.checkData(): # data invalid, so display message and return tkMessageBox.showinfo( title = "PiPark Setup", message = "Saved data is invalid. Please ensure that " + "there are 3 control points and at least 1 parking " + "space marked." ) return self.quit_button.invoke() if self.__is_verbose: print "INFO: Setup application terminated. " main.main() def clickRegister(self): """Register the car park with the server. """ if self.__is_verbose: print "ACTION: Clicked 'Register'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.register() def clickNewImage(self): """Use PiCam to take new 'setup image' for PiPark setup. """ if self.__is_verbose: print "ACTION: Clicked 'Capture New Image'" self.__is_saved = False # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # clear the Tkinter display canvas, and all related setupdata. Then # turn on PiCam to allow for new image to be taken. self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) self.turnOnCamera() def clickSave(self): if self.__is_verbose: print "ACTION: Clicked Save'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.saveData() def clickLoad(self): if self.__is_verbose: print "ACTION: Clicked 'Load'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() if not self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0]/2, s.PICTURE_RESOLUTION[1]/2): tkMessageBox.showerror(title = "Error!", message = "Error loading setup image." + " Please ensure setup image exists as ./image/setup_image.jpeg.") return # clear all previous data, and activate buttons self.clear_button.invoke() self.cps_button.config(state = tk.ACTIVE) self.spaces_button.config(state = tk.ACTIVE) def clickClear(self): if self.__is_verbose: print "ACTION: Clicked 'Clear'" self.__is_saved = False # clear all data points, to start afresh self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) def clickSpaces(self): """Add/remove parking-space bounding boxes. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove Spaces'" # toggle the button, and turn off other toggle buttons self.spaces_button.toggle() if self.cps_button.getIsActive(): self.cps_button.setOff() def clickCPs(self): """Add/remove control points. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove CPs'" # toggle the button, and turn off other toggle buttons self.cps_button.toggle() if self.spaces_button.getIsActive(): self.spaces_button.setOff() def clickQuit(self): """Quit & terminate the application. """ if self.__is_verbose: print "ACTION: Clicked 'Quit'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() response = True # if the user hasn't recently saved, ask if they really wish to quit if not self.__is_saved: response = tkMessageBox.askyesno( title = "Quit?", message = "Are you sure you wish to quit?" + "All unsaved setup will be lost." ) if response: # user wishes to quit, destroy the application self.quit() self.master.destroy() def clickAbout(self): """Open the README file for instructions on GUI use. """ if self.__is_verbose: print "ACTION: Clicked 'Open README'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # load external README from command line # TODO: Put this in new Tkinter window with scroll bar os.system("leafpad " + "./SETUP_README.txt") if self.__is_verbose: print "INFO: Opened ./SETUP_README.txt in leafpad." # ============================================================================== # # Application Layout Management # # ============================================================================== # -------------------------------------------------------------------------- # Create Image Display Canvas # -------------------------------------------------------------------------- def __createDisplay(self): """ Create the display tkinter canvas to hold the images taken by the pi camera. """ self.display = tk.Canvas( self, width = s.PICTURE_RESOLUTION[0], height = s.PICTURE_RESOLUTION[1] ) self.display.grid(row = 2, column = 0, rowspan = 1, columnspan = 6) # -------------------------------------------------------------------------- # Create Options Menu # -------------------------------------------------------------------------- def __createMenu(self): """Create a tkinter canvas in which to hold the menu buttons. """ # Layout: # ------- # ------------------------------------------------------------------ # | Start || Capture New Image || Add/Remove Spaces || Quit | # ------------------------------------------------------------------ # | Register || Save || Load || Clear || Add/Remove CPs || ReadMe | # ------------------------------------------------------------------ # padding around buttons PADDING = 10; # start the main program self.start_button = tk.Button(self, text = "Start PiPark", command = self.clickStart, padx = PADDING) self.start_button.grid(row = 0, column = 0, sticky = tk.W + tk.E + tk.N + tk.S) # register the car park button self.register_button = tk.Button(self, text = "Register", command = self.clickRegister, padx = PADDING) self.register_button.grid(row = 1, column = 0, sticky = tk.W + tk.E + tk.N + tk.S) # take new setup image button self.image_button = tk.Button(self, text = "Capture New Setup Image", command = self.clickNewImage, padx = PADDING) self.image_button.grid(row = 0, column = 1, rowspan = 1, columnspan = 3, sticky = tk.W + tk.E + tk.N + tk.S) # save setup data & image self.save_button = tk.Button(self, text = "Save", command = self.clickSave, padx = PADDING) self.save_button.grid(row = 1, column = 1, sticky = tk.W + tk.E + tk.N + tk.S) # load setup data & image self.load_button = tk.Button(self, text = "Load", command = self.clickLoad, padx = PADDING) self.load_button.grid(row = 1, column = 2, sticky = tk.W + tk.E + tk.N + tk.S) # clear all parking spaces and CPs self.clear_button = tk.Button(self, text = "Clear", command = self.clickClear, padx = PADDING) self.clear_button.grid(row = 1, column = 3, sticky = tk.W + tk.E + tk.N + tk.S) # add/remove spaces button self.spaces_button = ToggleButton(self) self.spaces_button.config(text = "Add/Remove Spaces", command = self.clickSpaces, padx = PADDING, state = tk.DISABLED) self.spaces_button.grid(row = 0, column = 4, sticky = tk.W + tk.E + tk.N + tk.S) # add/remove control points button self.cps_button = ToggleButton(self) self.cps_button.config(text = "Add/Remove Control Points", command = self.clickCPs, padx = PADDING, state = tk.DISABLED) self.cps_button.grid(row = 1, column = 4, sticky = tk.W + tk.E + tk.N + tk.S) # quit setup self.quit_button = tk.Button(self, text = "Quit", command = self.clickQuit, padx = PADDING) self.quit_button.grid(row = 0, column = 5, sticky = tk.W + tk.E + tk.N + tk.S) # about button - display information about PiPark self.about_button = tk.Button(self, text = "Open ReadMe", command = self.clickAbout, padx = PADDING) self.about_button.grid(row = 1, column = 5, sticky = tk.W + tk.E + tk.N + tk.S) # ============================================================================== # # Getters and Setters # # ============================================================================== # -------------------------------------------------------------------------- # Is Verbose? # -------------------------------------------------------------------------- def getIsVerbose(): return self.__is_verbose def setIsVerbose(value): if isinstance(value, bool): self.__is_verbose = value
class Application(tk.Frame): __is_verbose = s.IS_VERBOSE __is_saved = False __parking_spaces = None __control_points = None __camera = None __camera_is_active = False SETUP_IMAGE = "./images/setup.jpeg" DEFAULT_IMAGE = "./images/default.jpeg" def __init__(self, master=None): tk.Frame.__init__(self, master) self.grid() self.__createDisplay() self.__createMenu() self.__parking_spaces = Boxes(self.display, type=0) self.__control_points = Boxes(self.display, type=1) self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set() def loadImage(self, image_address, canvas, width, height): canvas.delete(tk.ALL) try: if not isinstance(canvas, tk.Canvas): raise TypeError if not isinstance(image_address, str): raise TypeError if not isinstance(width, int): raise TypeError if not isinstance(height, int): raise TypeError photo = ImageTk.PhotoImage(Image.open(image_address)) canvas.create_image((width, height), image=photo) canvas.image = photo return True except TypeError: if self.__is_verbose: print "ERROR: loadImage() arguments of incorrect data type." return False except: # image failed to load if self.__is_verbose: print "ERROR: loadImage() failed to load image " + image_address return False def turnOnCamera(self): tkMessageBox.showinfo( title="", message="Press the ENTER key to take a new setup image " + "or the ESCAPE key to cancel.") try: self.__camera = imageread.setup_camera(is_fullscreen=True) self.__camera.awb_mode = 'auto' self.__camera.exposure_mode = 'auto' self.__camera.start_preview() self.__camera_is_active = True except: tkMessageBox.showerror( title="Error!", message="Error: Failed to setup and start PiCam.") def saveData(self): f1 = open('./setup_data.py', 'w+') print >> f1, 'boxes = [' for i in range(self.__parking_spaces.length()): space = self.__parking_spaces.get(i).getOutput() if space != None: o = (i) print >> f1, space, ',' for j in range(self.__control_points.length()): cp = self.__control_points.get(j).getOutput() if cp != None: o = (i) print >> f1, cp, ',' print >> f1, ']' self.__is_saved = True if self.__is_verbose: print 'INFO: Data saved in file setup_data.py.' tkMessageBox.showinfo(title="Setup", message="Data saved successfully.") def loadData(self): try: import setup_data reload(setup_data) except: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" tkMessageBox.showerror( title="Error!", message="Problem loading data from setup_data.py") self.__is_saved = True return setup_data.boxes def checkData(self): if self.__is_verbose: print "INFO: Data is being checked for validity." try: import setup_data reload(setup_data) box_data = setup_data.boxes if not box_data: raise ValueError except ImportError: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" except ValueError: if self.__is_verbose: print "ERROR: ./setup_data.py 'boxes' is empty." except: if self.__is_verbose: print "ERROR: ./setup_data.py does not contain 'boxes'." space_boxes = [] control_boxes = [] for data_set in box_data: if data_set[1] == 0: space_boxes.append(data_set) elif data_set[1] == 1: control_boxes.append(data_set) elif self.__is_verbose: print "ERROR: Box-type not set to either 0 or 1." if len(space_boxes) > 0 and len(control_boxes) == 3: valid_data = True else: valid_data = False if self.__is_verbose: print "INFO: Data checked. Data is", valid_data return valid_data def register(self): if not self.__is_saved: response = tkMessageBox.askokcancel( title="Save Setup", message="Setup data must be saved before the registration" + " process can be completed. Would you like to save now?") if response: self.saveData() else: tkMessageBox.showinfo(title="Setup", message="Registration not completed.") return if not self.checkData(): tkMessageBox.showinfo( title="Setup", message="Registration not complete.\n\nSaved data is " + "invalid. Please ensure that there are 3 control points and " + "at least 1 parking spaces marked.") return try: import setup_data reload(setup_data) boxes = setup_data.boxes if not isinstance(boxes, list): raise ValueError() except: print "ERROR: Setup data does not exist. Please run options 1 and 2 first." return try: import senddata except: print "ERROR: Could not import send data file." return # start fresh out = senddata.deregister_pi() try: out['error'] print "ERROR: Error in connecting to server. Please update settings.py." return except: pass for box in boxes: if box[1] == 0: output = senddata.register_area(box[0]) if "error" in output.keys(): if self.__is_verbose: print "ERROR:", output["error"] return else: if self.__is_verbose: print "INFO: Registering area", box[0], "on server." # print success message if self.__is_verbose: print "\nINFO: Server registration successful." def returnPressHandler(self, event): self.focus_set() if not self.__camera_is_active or not self.__camera: return try: self.__camera.capture(self.SETUP_IMAGE) self.__camera.stop_preview() self.__camera.close() self.__camera_is_active = False if self.__is_verbose: print "INFO: New setup image captured." print "INFO: PiCam deactivated." except: # image failed to capture, show error message tkMessageBox.showerror( title="Error!", message="Error: Failed to capture new setup image.") # load the new setup image self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2) self.cps_button.config(state=tk.ACTIVE) self.spaces_button.config(state=tk.ACTIVE) def escapePressHandler(self, event): self.focus_set() if not self.__camera_is_active or not self.__camera: return try: # close the camera without taking new image self.__camera.stop_preview() self.__camera.close() self.__camera_is_active = False if self.__is_verbose: print "INFO: PiCam deactivated." except: # image failed to close for some reason, show error message if self.__is_verbose: print "ERROR: PiCam failed to close correctly." def keyPressHandler(self, event): """Handle key-press events for numeric keys. """ key = event.char NUM_KEYS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'] if key in NUM_KEYS: if self.spaces_button.getIsActive(): self.__parking_spaces.setCurrentBox(int(key)) if self.cps_button.getIsActive(): # ignore all other numbers, but 1, 2 and 3 as 3 is the maximum # number of control points allowed. if key not in ['1', '2', '3']: return # NB: -1 from key press, because list indices are [0, 1, 2], # but for ease of user selection the numbers 1, 2, 3 are used # for input self.__control_points.setCurrentBox(int(key) - 1) def leftClickHandler(self, event): # ensure focus on display canvas to recieve mouse clicks self.display.focus_set() # perform correct operation, dependent on which toggle button is active # add new control points (max = 3) if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Add Control Point" self.__is_saved = False this_cp_id = self.__control_points.getCurrentBox() this_cp = self.__control_points.boxes[this_cp_id] this_cp.updatePoints(event.x, event.y) # add new parking space elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Add Parking Space" self.__is_saved = False this_space_id = self.__parking_spaces.getCurrentBox() this_space = self.__parking_spaces.boxes[this_space_id] this_space.updatePoints(event.x, event.y) # do nothing -- ignore LMB clicks else: if self.__is_verbose: print "INFO: Just clicking LMB merrily =D" # return focus to the main frame for key-press events self.focus_set() def rightClickHandler(self, event): # ensure focus is set to the display canvas self.display.focus_set() # perform correct operation, dependent on which toggle button is active if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Remove Control Point" self.__is_saved = False self.__control_points.boxes[ self.__control_points.getCurrentBox()].clear() self.__control_points.boxes[ self.__control_points.getCurrentBox()].deleteRectangle( self.display) elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Remove parking space" self.__is_saved = False self.__parking_spaces.boxes[ self.__parking_spaces.getCurrentBox()].clear() self.__parking_spaces.boxes[ self.__parking_spaces.getCurrentBox()].deleteRectangle( self.display) else: if self.__is_verbose: print "INFO: Just clicking RMB merrily =)" # return focus to the main frame for key-press events self.focus_set() def clickStart(self): """ Close the current setup application, then initiate the main PiPark program. """ if self.__is_verbose: print "ACTION: Clicked 'Start'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # set initial responses response = False response1 = False response2 = False # if setup data has not been saved. Ask user if they would like to save # before continuing. if not self.__is_saved: response = tkMessageBox.askyesno( title="Save Setup", type=tkMessageBox.YESNOCANCEL, message="Most recent changes to setup have not been saved." + "Would you like to save before running PiPark?") if response: self.saveData() # data is saved, ask the user if they are sure they wish to quit. else: response = tkMessageBox.askyesno( title="Save Setup", message="Are you ready to leave setup and run PiPark?") # user wishes to quit setup and run pipark, so do it! if response: # ensure data is valid before continuing if not self.checkData(): # data invalid, so display message and return tkMessageBox.showinfo( title="Setup", message="Saved data is invalid. Please ensure that " + "there are 3 control points and at least 1 parking " + "space marked.") return self.quit_button.invoke() if self.__is_verbose: print "INFO: Setup application terminated. " main.main() def clickRegister(self): """Register the car park with the server. """ if self.__is_verbose: print "ACTION: Clicked 'Register'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.register() def clickNewImage(self): """Use PiCam to take new 'setup image' for PiPark setup. """ if self.__is_verbose: print "ACTION: Clicked 'Capture New Image'" self.__is_saved = False # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) self.turnOnCamera() def clickSave(self): if self.__is_verbose: print "ACTION: Clicked Save'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.saveData() def clickLoad(self): if self.__is_verbose: print "ACTION: Clicked 'Load'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() if not self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2): tkMessageBox.showerror( title="Error!", message="Error loading setup image." + " Please ensure setup image exists as ./image/setup_image.jpeg." ) return # clear all previous data, and activate buttons self.clear_button.invoke() self.cps_button.config(state=tk.ACTIVE) self.spaces_button.config(state=tk.ACTIVE) def clickClear(self): if self.__is_verbose: print "ACTION: Clicked 'Clear'" self.__is_saved = False # clear all data points, to start afresh self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) def clickSpaces(self): """Add/remove parking-space bounding boxes. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove Spaces'" # toggle the button, and turn off other toggle buttons self.spaces_button.toggle() if self.cps_button.getIsActive(): self.cps_button.setOff() def clickCPs(self): """Add/remove control points. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove CPs'" # toggle the button, and turn off other toggle buttons self.cps_button.toggle() if self.spaces_button.getIsActive(): self.spaces_button.setOff() def clickQuit(self): """Quit & terminate the application. """ if self.__is_verbose: print "ACTION: Clicked 'Quit'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() response = True # if the user hasn't recently saved, ask if they really wish to quit if not self.__is_saved: response = tkMessageBox.askyesno( title="Quit?", message="Are you sure you wish to quit?" + "All unsaved setup will be lost.") if response: # user wishes to quit, destroy the application self.quit() self.master.destroy() def clickAbout(self): """Open the README file for instructions on GUI use. """ if self.__is_verbose: print "ACTION: Clicked 'Open README'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # load external README from command line # TODO: Put this in new Tkinter window with scroll bar os.system("leafpad " + "./SETUP_README.txt") if self.__is_verbose: print "INFO: Opened ./SETUP_README.txt in leafpad." def __createDisplay(self): self.display = tk.Canvas(self, width=s.PICTURE_RESOLUTION[0], height=s.PICTURE_RESOLUTION[1]) self.display.grid(row=2, column=0, rowspan=1, columnspan=6) def __createMenu(self): """Create a tkinter canvas in which to hold the menu buttons. """ # padding around buttons PADDING = 10 # start the main program self.start_button = tk.Button(self, text="Start", command=self.clickStart, padx=PADDING) self.start_button.grid(row=0, column=0, sticky=tk.W + tk.E + tk.N + tk.S) # take new setup image button self.image_button = tk.Button(self, text="Capture New Setup Image", command=self.clickNewImage, padx=PADDING) self.image_button.grid(row=0, column=1, rowspan=1, columnspan=3, sticky=tk.W + tk.E + tk.N + tk.S) # save setup data & image self.save_button = tk.Button(self, text="Save", command=self.clickSave, padx=PADDING) self.save_button.grid(row=1, column=1, sticky=tk.W + tk.E + tk.N + tk.S) # load setup data & image self.load_button = tk.Button(self, text="Load", command=self.clickLoad, padx=PADDING) self.load_button.grid(row=1, column=2, sticky=tk.W + tk.E + tk.N + tk.S) # clear all parking spaces and CPs self.clear_button = tk.Button(self, text="Clear", command=self.clickClear, padx=PADDING) self.clear_button.grid(row=1, column=3, sticky=tk.W + tk.E + tk.N + tk.S) # add/remove spaces button self.spaces_button = ToggleButton(self) self.spaces_button.config(text="Add/Remove Spaces", command=self.clickSpaces, padx=PADDING, state=tk.DISABLED) self.spaces_button.grid(row=0, column=4, rowspan=1, columnspan=2, sticky=tk.W + tk.E + tk.N + tk.S) # add/remove control points button self.cps_button = ToggleButton(self) self.cps_button.config(text="Add/Remove Control Points", command=self.clickCPs, padx=PADDING, state=tk.DISABLED) self.cps_button.grid(row=1, column=4, rowspan=1, columnspan=2, sticky=tk.W + tk.E + tk.N + tk.S) # quit setup self.quit_button = tk.Button(self, text="Quit", command=self.clickQuit, padx=PADDING) self.quit_button.grid(row=1, column=0, sticky=tk.W + tk.E + tk.N + tk.S) def getIsVerbose(): return self.__is_verbose def setIsVerbose(value): if isinstance(value, bool): self.__is_verbose = value
def callbackMouse(event): SelWindow.w.focus_set() Boxes.getCur().update_pos(event.x, event.y) print "INFO: Clicked at", event.x, event.y task()
class Application(tk.Frame): # -------------------------------------------------------------------------- # Instance Attributes # -------------------------------------------------------------------------- # booleans __is_verbose = s.IS_VERBOSE # print messages to terminal __is_saved = False # lists to hold parking space and control point references __parking_spaces = None __control_points = None # picamera __camera = None __camera_is_active = False # image load/save locoations SETUP_IMAGE = "./images/setup.jpeg" DEFAULT_IMAGE = "./images/default.jpeg" # -------------------------------------------------------------------------- # Constructor Method # -------------------------------------------------------------------------- def __init__(self, master=None): """Application constructor method. """ # run super constructor method tk.Frame.__init__(self, master) # set alignment inside the frame self.grid() # create widgets self.__createDisplay( ) # display canvas: holds the image, CPs and spaces self.__createMenu( ) # menu canvas: holds the buttons and menu bar image # lists to hold parking space and control point references self.__parking_spaces = Boxes(self.display, type=0) self.__control_points = Boxes(self.display, type=1) # create mouse button and key-press handlers -> set focus to this frame self.bind("<Return>", self.returnPressHandler) self.bind("<Key>", self.keyPressHandler) self.bind("<Escape>", self.escapePressHandler) self.display.bind("<Button-1>", self.leftClickHandler) self.display.bind("<Button-3>", self.rightClickHandler) self.focus_set() if self.__is_verbose: print "INFO: __parking_spaces length:", self.__parking_spaces.length( ) print "INFO: __control_points length:", self.__control_points.length( ) # load the default background self.loadImage(self.DEFAULT_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2) # ============================================================================== # # Public Application Methods # # ============================================================================== # -------------------------------------------------------------------------- # Load Image # -------------------------------------------------------------------------- def loadImage(self, image_address, canvas, width, height): """ Load image at image_address. If the load is successful then return True, otherwise return False. Keyword Arguments: image_address -- The address of the image to be loaded (default = './'). canvas -- The Tkinter Canvas into which the image is loaded. width -- Width of the image to load. height -- Height of the image to load. Returns: Boolean -- True if load successful, False if not. """ # clear the old canvas canvas.delete(tk.ALL) try: # guard against incorrect argument datatypes if not isinstance(canvas, tk.Canvas): raise TypeError if not isinstance(image_address, str): raise TypeError if not isinstance(width, int): raise TypeError if not isinstance(height, int): raise TypeError # load the image into the canvas photo = ImageTk.PhotoImage(Image.open(image_address)) canvas.create_image((width, height), image=photo) canvas.image = photo # image load successful return True except TypeError: # arguments of incorrect data type, load unsuccessful if self.__is_verbose: print "ERROR: loadImage() arguments of incorrect data type." return False except: # image failed to load if self.__is_verbose: print "ERROR: loadImage() failed to load image " + image_address return False # -------------------------------------------------------------------------- # Activate the Pi Camera # -------------------------------------------------------------------------- def turnOnCamera(self): """ Instruct the user how to take a new setup image, then activate the PiCam. If the camera fails to load, catch the exception and present error message. """ # show quick dialogue box with basic instruction tkMessageBox.showinfo( title="", message="Press the ENTER key to take a new setup image " + "or the ESCAPE key to cancel.") try: # initialise the camera using the settings in the imageread module self.__camera = imageread.setup_camera(is_fullscreen=True) #preview window while True: ret, frame = self.__camera.read() rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2BGRA) cv2.imshow('frame', rgb) if cv2.waitKey(1) & 0xFF == ord('\x0D'): cv2.destroyWindow('frame') break #self.__camera.awb_mode = 'auto'; #self.__camera.exposure_mode = 'auto'; #self.__camera.start_preview() #self.__camera_is_active = True #if self.__is_verbose: print "INFO: PiCam activated." except: # camera failed to load, display error message tkMessageBox.showerror( title="Error!", message="Error: Failed to setup and start PiCam.") # -------------------------------------------------------------------------- # Save Data # -------------------------------------------------------------------------- def saveData(self): """Save the CP and parking space reference data to ./setup_data.py. """ # Open the file to output the co-ordinates to f1 = open('./setup_data.py', 'w+') # Print the dictionary data to the file print >> f1, 'boxes = [' # for every parking space, save the data to ./setup_data.py for i in range(self.__parking_spaces.length()): space = self.__parking_spaces.get(i).getOutput() # ignore the space if no data present if space != None: o = (i) print >> f1, space, ',' # for every control point, save the data to ./setup_data.py for j in range(self.__control_points.length()): cp = self.__control_points.get(j).getOutput() # ignore the CP if no data present if cp != None: o = (i) print >> f1, cp, ',' # save to ./setup_data.py print >> f1, ']' self.__is_saved = True if self.__is_verbose: print 'INFO: Data saved in file setup_data.py.' tkMessageBox.showinfo(title="PiPark Setup", message="Data saved successfully.") # -------------------------------------------------------------------------- # Load Data # -------------------------------------------------------------------------- def loadData(self): try: # load the setup data, reload to refresh the data import setup_data reload(setup_data) except: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" tkMessageBox.showerror( title="Error!", message="Problem loading data from setup_data.py") self.__is_saved = True return setup_data.boxes # -------------------------------------------------------------------------- # Check Data # -------------------------------------------------------------------------- def checkData(self): """ Check that the setup data meets the following criteria: 1) There is at least 1 parking space. 2) There are exactly 3 control points. Returns: Boolean -- True if criteria is met, False if not. """ if self.__is_verbose: print "INFO: Data is being checked for validity." # get the boxes data to check from setup_data try: # load the setup data import setup_data reload(setup_data) # ensure that boxes exists and is not empty box_data = setup_data.boxes if not box_data: raise ValueError except ImportError: if self.__is_verbose: print "ERROR: Problem loading data from ./setup_data.py" except ValueError: if self.__is_verbose: print "ERROR: ./setup_data.py 'boxes' is empty." except: if self.__is_verbose: print "ERROR: ./setup_data.py does not contain 'boxes'." # create lists to hold data of each type space_boxes = [] control_boxes = [] # append the box data to space or control list as appropriate for data_set in box_data: if data_set[1] == 0: space_boxes.append(data_set) elif data_set[1] == 1: control_boxes.append(data_set) elif self.__is_verbose: print "ERROR: Box-type not set to either 0 or 1." # data is valid if there is at least 1 space and exactly 3 control points if len(space_boxes) > 0 and len(control_boxes) == 3: valid_data = True else: valid_data = False if self.__is_verbose: print "INFO: Data checked. Data is", valid_data return valid_data # -------------------------------------------------------------------------- # Register Data # -------------------------------------------------------------------------- def register(self): """Register the Pi with the server. """ # if setup hasn't been saved recently since last change, ask the if # user to save first if not self.__is_saved: response = tkMessageBox.askokcancel( title="Save Setup", message="Setup data must be saved before the registration" + " process can be completed. Would you like to save now?") # if user selects 'yes' save the data and continue, else do not # register the pi if response: self.saveData() else: tkMessageBox.showinfo(title="PiPark Setup", message="Registration not completed.") return # check that most recent saved data is valid (#CPs == 3, #Spaces > 0) if not self.checkData(): # data invalid, so display message and return tkMessageBox.showinfo( title="PiPark Setup", message="Registration not complete.\n\nSaved data is " + "invalid. Please ensure that there are 3 control points and " + "at least 1 parking spaces marked.") return # attempt to import the setup data and ensure 'boxes' is a list try: import setup_data reload(setup_data) boxes = setup_data.boxes if not isinstance(boxes, list): raise ValueError() except: print "ERROR: Setup data does not exist. Please run options 1 and 2 first." return # attempt to import the server senddata module try: import senddata except: print "ERROR: Could not import send data file." return # deregister all areas associated with this pi (start fresh) out = senddata.deregister_pi() try: out['error'] print out #debug print "ERROR: Error in connecting to server. Please update settings.py." return except: pass # register each box on the server for box in boxes: if box[1] == 0: output = senddata.register_area(box[0]) if "error" in output.keys(): if self.__is_verbose: print "ERROR:", output["error"] return else: if self.__is_verbose: print "INFO: Registering area", box[0], "on server." # print success message if verbose if self.__is_verbose: print "\nINFO: Server registration successful." # ============================================================================== # # Event Handlers # # ============================================================================== # -------------------------------------------------------------------------- # Return-key-press Event Handler # -------------------------------------------------------------------------- def returnPressHandler(self, event): """ Handle Return-key-press events. Capture a new setup image when PiCam is active, and load the image. """ # ensure focus on window self.focus_set() # do nothing if camera is not active, or no camera object exists #if not self.__camera_is_active or not self.__camera: return try: # capture new setup image, then close the camera #self.__camera.capture(self.SETUP_IMAGE) ret, frame = self.__camera.read() cv2.imwrite(self.SETUP_IMAGE, frame) self.__camera.release() #self.__camera.stop_preview() #self.__camera.close() #self.__camera_is_active = False if self.__is_verbose: print "INFO: New setup image captured." print "INFO: PiCam deactivated." except: # image failed to capture, show error message tkMessageBox.showerror( title="Error!", message="Error: Failed to capture new setup image.") # load the new setup image self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2) # activate buttons if they're disabled self.cps_button.config(state=tk.ACTIVE) self.spaces_button.config(state=tk.ACTIVE) # -------------------------------------------------------------------------- # Escape-key-press Event Handler # -------------------------------------------------------------------------- def escapePressHandler(self, event): # ensure focus on window self.focus_set() # do nothing if camera is not active, or no camera object exists if not self.__camera_is_active or not self.__camera: return try: # close the camera without taking new image # TODO close the camera without taking new image using opencv cv2.destroyWindow('frame') self.__camera.release() if self.__is_verbose: print "INFO: PiCam deactivated." except: # image failed to close for some reason, show error message if self.__is_verbose: print "ERROR: PiCam failed to close correctly." # -------------------------------------------------------------------------- # Key-press Event Handler # -------------------------------------------------------------------------- def keyPressHandler(self, event): """Handle key-press events for numeric keys. """ key = event.char NUM_KEYS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'] if key in NUM_KEYS: if self.__is_verbose: print "INFO: Number-key pressed", key if self.spaces_button.getIsActive(): self.__parking_spaces.setCurrentBox(int(key)) if self.cps_button.getIsActive(): # ignore all other numbers, but 1, 2 and 3 as 3 is the maximum # number of control points allowed. if key not in ['1', '2', '3']: return # NB: -1 from key press, because list indices are [0, 1, 2], # but for ease of user selection the numbers 1, 2, 3 are used # for input self.__control_points.setCurrentBox(int(key) - 1) # -------------------------------------------------------------------------- # LMB Event Handler # -------------------------------------------------------------------------- def leftClickHandler(self, event): """Handle LMB-click events to add/remove control points & spaces. """ # ensure focus on display canvas to recieve mouse clicks self.display.focus_set() # perform correct operation, dependent on which toggle button is active # add new control points (max = 3) if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Add Control Point" self.__is_saved = False this_cp_id = self.__control_points.getCurrentBox() this_cp = self.__control_points.boxes[this_cp_id] this_cp.updatePoints(event.x, event.y) # add new parking space elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Add Parking Space" self.__is_saved = False this_space_id = self.__parking_spaces.getCurrentBox() this_space = self.__parking_spaces.boxes[this_space_id] this_space.updatePoints(event.x, event.y) # do nothing -- ignore LMB clicks else: if self.__is_verbose: print "INFO: Just clicking LMB merrily =D" # return focus to the main frame for key-press events self.focus_set() # -------------------------------------------------------------------------- # RMB Event Handler # -------------------------------------------------------------------------- def rightClickHandler(self, event): """Handle RMB-click events to add/remove control points & spaces. """ # ensure focus is set to the display canvas self.display.focus_set() # perform correct operation, dependent on which toggle button is active if self.cps_button.getIsActive(): if self.__is_verbose: print "INFO: Remove Control Point" self.__is_saved = False self.__control_points.boxes[ self.__control_points.getCurrentBox()].clear() self.__control_points.boxes[ self.__control_points.getCurrentBox()].deleteRectangle( self.display) elif self.spaces_button.getIsActive(): if self.__is_verbose: print "INFO: Remove parking space" self.__is_saved = False self.__parking_spaces.boxes[ self.__parking_spaces.getCurrentBox()].clear() self.__parking_spaces.boxes[ self.__parking_spaces.getCurrentBox()].deleteRectangle( self.display) else: if self.__is_verbose: print "INFO: Just clicking RMB merrily =)" # return focus to the main frame for key-press events self.focus_set() # ============================================================================== # # Button Handlers # # ============================================================================== def clickStart(self): """ Close the current setup application, then initiate the main PiPark program. """ if self.__is_verbose: print "ACTION: Clicked 'Start'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # set initial responses response = False response1 = False response2 = False # if setup data has not been saved. Ask user if they would like to save # before continuing. if not self.__is_saved: response = tkMessageBox.askyesno( title="Save Setup", type=tkMessageBox.YESNOCANCEL, message="Most recent changes to setup have not been saved." + "Would you like to save before running PiPark?") if response: self.saveData() # data is saved, ask the user if they are sure they wish to quit. else: response = tkMessageBox.askyesno( title="Save Setup", message="Are you ready to leave setup and run PiPark?") # user wishes to quit setup and run pipark, so do it! if response: # ensure data is valid before continuing if not self.checkData(): # data invalid, so display message and return tkMessageBox.showinfo( title="PiPark Setup", message="Saved data is invalid. Please ensure that " + "there are 3 control points and at least 1 parking " + "space marked.") return self.quit_button.invoke() if self.__is_verbose: print "INFO: Setup application terminated. " main.main() def clickRegister(self): """Register the car park with the server. """ if self.__is_verbose: print "ACTION: Clicked 'Register'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.register() def clickNewImage(self): """Use PiCam to take new 'setup image' for PiPark setup. """ if self.__is_verbose: print "ACTION: Clicked 'Capture New Image'" self.__is_saved = False # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # clear the Tkinter display canvas, and all related setupdata. Then # turn on PiCam to allow for new image to be taken. self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) self.turnOnCamera() def clickSave(self): if self.__is_verbose: print "ACTION: Clicked Save'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() self.saveData() def clickLoad(self): if self.__is_verbose: print "ACTION: Clicked 'Load'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() if not self.loadImage(self.SETUP_IMAGE, self.display, s.PICTURE_RESOLUTION[0] / 2, s.PICTURE_RESOLUTION[1] / 2): tkMessageBox.showerror( title="Error!", message="Error loading setup image." + " Please ensure setup image exists as ./image/setup_image.jpeg." ) return # clear all previous data, and activate buttons self.clear_button.invoke() self.cps_button.config(state=tk.ACTIVE) self.spaces_button.config(state=tk.ACTIVE) def clickClear(self): if self.__is_verbose: print "ACTION: Clicked 'Clear'" self.__is_saved = False # clear all data points, to start afresh self.__parking_spaces.clearAll(self.display) self.__control_points.clearAll(self.display) def clickSpaces(self): """Add/remove parking-space bounding boxes. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove Spaces'" # toggle the button, and turn off other toggle buttons self.spaces_button.toggle() if self.cps_button.getIsActive(): self.cps_button.setOff() def clickCPs(self): """Add/remove control points. """ if self.__is_verbose: print "ACTION: Clicked 'Add/Remove CPs'" # toggle the button, and turn off other toggle buttons self.cps_button.toggle() if self.spaces_button.getIsActive(): self.spaces_button.setOff() def clickQuit(self): """Quit & terminate the application. """ if self.__is_verbose: print "ACTION: Clicked 'Quit'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() response = True # if the user hasn't recently saved, ask if they really wish to quit if not self.__is_saved: response = tkMessageBox.askyesno( title="Quit?", message="Are you sure you wish to quit?" + "All unsaved setup will be lost.") if response: # user wishes to quit, destroy the application self.quit() self.master.destroy() def clickAbout(self): """Open the README file for instructions on GUI use. """ if self.__is_verbose: print "ACTION: Clicked 'Open README'" # turn off toggle buttons self.spaces_button.setOff() self.cps_button.setOff() # load external README from command line # TODO: Put this in new Tkinter window with scroll bar os.system("leafpad " + "./SETUP_README.txt") if self.__is_verbose: print "INFO: Opened ./SETUP_README.txt in leafpad." # ============================================================================== # # Application Layout Management # # ============================================================================== # -------------------------------------------------------------------------- # Create Image Display Canvas # -------------------------------------------------------------------------- def __createDisplay(self): """ Create the display tkinter canvas to hold the images taken by the pi camera. """ self.display = tk.Canvas(self, width=s.PICTURE_RESOLUTION[0], height=s.PICTURE_RESOLUTION[1]) self.display.grid(row=2, column=0, rowspan=1, columnspan=6) # -------------------------------------------------------------------------- # Create Options Menu # -------------------------------------------------------------------------- def __createMenu(self): """Create a tkinter canvas in which to hold the menu buttons. """ # Layout: # ------- # ------------------------------------------------------------------ # | Start || Capture New Image || Add/Remove Spaces || Quit | # ------------------------------------------------------------------ # | Register || Save || Load || Clear || Add/Remove CPs || ReadMe | # ------------------------------------------------------------------ # padding around buttons PADDING = 10 # start the main program self.start_button = tk.Button(self, text="Start PiPark", command=self.clickStart, padx=PADDING) self.start_button.grid(row=0, column=0, sticky=tk.W + tk.E + tk.N + tk.S) # register the car park button self.register_button = tk.Button(self, text="Register", command=self.clickRegister, padx=PADDING) self.register_button.grid(row=1, column=0, sticky=tk.W + tk.E + tk.N + tk.S) # take new setup image button self.image_button = tk.Button(self, text="Capture New Setup Image", command=self.clickNewImage, padx=PADDING) self.image_button.grid(row=0, column=1, rowspan=1, columnspan=3, sticky=tk.W + tk.E + tk.N + tk.S) # save setup data & image self.save_button = tk.Button(self, text="Save", command=self.clickSave, padx=PADDING) self.save_button.grid(row=1, column=1, sticky=tk.W + tk.E + tk.N + tk.S) # load setup data & image self.load_button = tk.Button(self, text="Load", command=self.clickLoad, padx=PADDING) self.load_button.grid(row=1, column=2, sticky=tk.W + tk.E + tk.N + tk.S) # clear all parking spaces and CPs self.clear_button = tk.Button(self, text="Clear", command=self.clickClear, padx=PADDING) self.clear_button.grid(row=1, column=3, sticky=tk.W + tk.E + tk.N + tk.S) # add/remove spaces button self.spaces_button = ToggleButton(self) self.spaces_button.config(text="Add/Remove Spaces", command=self.clickSpaces, padx=PADDING, state=tk.DISABLED) self.spaces_button.grid(row=0, column=4, sticky=tk.W + tk.E + tk.N + tk.S) # add/remove control points button self.cps_button = ToggleButton(self) self.cps_button.config(text="Add/Remove Control Points", command=self.clickCPs, padx=PADDING, state=tk.DISABLED) self.cps_button.grid(row=1, column=4, sticky=tk.W + tk.E + tk.N + tk.S) # quit setup self.quit_button = tk.Button(self, text="Quit", command=self.clickQuit, padx=PADDING) self.quit_button.grid(row=0, column=5, sticky=tk.W + tk.E + tk.N + tk.S) # about button - display information about PiPark self.about_button = tk.Button(self, text="Open ReadMe", command=self.clickAbout, padx=PADDING) self.about_button.grid(row=1, column=5, sticky=tk.W + tk.E + tk.N + tk.S) # ============================================================================== # # Getters and Setters # # ============================================================================== # -------------------------------------------------------------------------- # Is Verbose? # -------------------------------------------------------------------------- def getIsVerbose(): return self.__is_verbose def setIsVerbose(value): if isinstance(value, bool): self.__is_verbose = value