This is a simple console menu that just holds a main menu and a few menu "buttons" (numbers and titles which the user has to enter for the button to do its action). It does not need submenus or more complicated input handling.
I went with an object-oriented approach and defined three classes:
Menu initialises with a name and a list of menu buttons.
class Menu(object): """Base class for the menu""" def __init__(self, name, buttons): # Initialize values self.name = name self.buttons = buttons def display(self): """Displaying the menu alongside the navigation elements""" # Display menu name print self.name # Display menu buttons for button in self.buttons: print " ", button.nav, button.name # Wait for user input self.userInput() def userInput(self): """Method to check and act upon user's input""" # This holds the amount of errors for the # navigation element to input comparison. errSel = 0 inputSel = raw_input("Enter selection> ") for button in self.buttons: # If input equals to button's navigation element if inputSel == str(button.nav): # Do the button's function button.do() # If input != navigation element else: # Increase "error on selection" by one, for # counting the errors and checking their # amount against the total number of # buttons. If greater to or equal that means # no elements were selected. # In that case show error and try again errSel += 1 # No usable input, try again if errSel >= len(self.buttons): print "Error on selection; try again."
Button class is accepting a name, a function and a navigation element (the number you have to press for it to do the button's action):
class Button(object): """Base class for menu buttons""" def __init__(self, name, func, nav): # Initialize values self.name = name # Function associated with button self.func = func # Navigation element; number which user has to enter to do button action self.nav = nav def do(self): # Do the button's function self.func()
Finally, the Controller. Did I do it the right way? I mean it works but having that while loop cycling with only the
raw_input() function stopping it from spamming infinite messages - is that correct or should I write something that prevents the while loop while a menu was already displayed and is awaiting user input?
class Controller(object): def __init__(self, menu): # Initialize values self.menu = menu # Start menu displaying / cycling self.cycle() def cycle(self): """Method for displaying / cycling through the menus""" while True: # Display menu and redisplay after button's function completes # Is this right? Will this loop correctly and never interfere # with raw_input()? self.menu.display()
Finally, the code that puts it all together:
import sys def main(): mainMenuButtonName = Button("Show name", showName, 1) mainMenuButtonVersion = Button("Show version", showVersion, 2) mainMenuButtonAbout = Button("Show about", showAbout, 3) mainMenuButtonQuit = Button("Quit", quit, 0) mainMenuButtons = [mainMenuButtonName, mainMenuButtonVersion, mainMenuButtonAbout, mainMenuButtonQuit] mainMenu = Menu("Main menu", mainMenuButtons) controller = Controller(mainMenu) controller.cycle() def showName(): print "My name is..." def showVersion(): print "My version is 0.1" def showAbout(): print "I am a demo app for testing menus" def quit(): sys.exit(0) if __name__ == "__main__": main()