patternpythonMinor
Console user main menu
Viewed 0 times
userconsolemenumain
Problem
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:
The
I went with an object-oriented approach and defined three classes:
Menu, Button, and Controller. The 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."The
Button class is accepting a name, a function and a navigation element (thSolution
I think display() and userInput() would be clearer if you did it like this:
So display() keeps showing the menu as long as userInput() returns None. userInput() returns None if the user's input doesn't match a button, or the inputSel value if it does match.
I think the way the Controller loops is fine. The program will always stop when it gets to raw_input() and read stdin until the user hits ENTER or RETURN. That's what raw_input() does.
You're managing your buttons as a list, which means you have to scan the whole list until you find the navigation element the user selected. If you put them in a dict, you can simply use the user's input to index directly into the structure and select the correct button (or determine that the user's selection is invalid). However, you still want them as a list for display because you can't control the order in which entries are pulled from a dictionary and I think you want your menu to display in the same order every time. That needs a list.
I would suggest the following for the constructor for the Display class. We're going to pass in a list so it's easy to define, and we'll keep the list, but we'll also have the class internally turn that list into a dict so it can more easily find the user's input on each selection.
With the simplification in userInput() and display() above, the shouldCycle member goes away. Now, with the menu defined internally as a dictionary (self.menu_dict) and as a list (self.menu_list), display() and userInput() will look like this:
def display(self):
"""
Display the menu alongside the navigation elements
"""
response = None
while response is None:
# Display menu buttons
for button in self.menu:
print " ", button.nav, button.name
# Wait for user input
response = self.userInput()
def userInput(self):
"""
Method to check and act upon user's input
"""
inputSel = raw_input("Enter selection> ")
for button in self.menu:
# If input equals to button's navigation element
if inputSel == str(button.nav):
button.do()
return inputSel
return NoneSo display() keeps showing the menu as long as userInput() returns None. userInput() returns None if the user's input doesn't match a button, or the inputSel value if it does match.
I think the way the Controller loops is fine. The program will always stop when it gets to raw_input() and read stdin until the user hits ENTER or RETURN. That's what raw_input() does.
You're managing your buttons as a list, which means you have to scan the whole list until you find the navigation element the user selected. If you put them in a dict, you can simply use the user's input to index directly into the structure and select the correct button (or determine that the user's selection is invalid). However, you still want them as a list for display because you can't control the order in which entries are pulled from a dictionary and I think you want your menu to display in the same order every time. That needs a list.
I would suggest the following for the constructor for the Display class. We're going to pass in a list so it's easy to define, and we'll keep the list, but we'll also have the class internally turn that list into a dict so it can more easily find the user's input on each selection.
class Display
def __init__(self, menu):
self.menu_list = menu
self.menu_dict = {}
# Initialise values
for button in menu:
self.menu_dict[button.nav] = buttonWith the simplification in userInput() and display() above, the shouldCycle member goes away. Now, with the menu defined internally as a dictionary (self.menu_dict) and as a list (self.menu_list), display() and userInput() will look like this:
def display(self):
"""
Display the menu alongside the navigation elements
"""
response = None
while response is None:
# Display menu buttons -- use the list so we get the same
# order every time.
for button in self.menu_list:
print " ", button.nav, button.name
# Wait for user input
response = self.userInput()
def userInput(self):
"""
Method to check and act upon user's input
"""
inputSel = raw_input("Enter selection> ")
try:
# Here we use the dictionary for ease of lookup
button = self.menu_dict[int(inputSel)]
except KeyError:
# The user's selection didn't match any of the button.nav
# values, so we got a KeyError exception on the dictionary
return None
button.do()
return inputSelCode Snippets
def display(self):
"""
Display the menu alongside the navigation elements
"""
response = None
while response is None:
# Display menu buttons
for button in self.menu:
print " ", button.nav, button.name
# Wait for user input
response = self.userInput()
def userInput(self):
"""
Method to check and act upon user's input
"""
inputSel = raw_input("Enter selection> ")
for button in self.menu:
# If input equals to button's navigation element
if inputSel == str(button.nav):
button.do()
return inputSel
return Noneclass Display
def __init__(self, menu):
self.menu_list = menu
self.menu_dict = {}
# Initialise values
for button in menu:
self.menu_dict[button.nav] = buttondef display(self):
"""
Display the menu alongside the navigation elements
"""
response = None
while response is None:
# Display menu buttons -- use the list so we get the same
# order every time.
for button in self.menu_list:
print " ", button.nav, button.name
# Wait for user input
response = self.userInput()
def userInput(self):
"""
Method to check and act upon user's input
"""
inputSel = raw_input("Enter selection> ")
try:
# Here we use the dictionary for ease of lookup
button = self.menu_dict[int(inputSel)]
except KeyError:
# The user's selection didn't match any of the button.nav
# values, so we got a KeyError exception on the dictionary
return None
button.do()
return inputSelContext
StackExchange Code Review Q#32207, answer score: 2
Revisions (0)
No revisions yet.