Questions:
- How could this code be written better?
- Could this be written in less lines of code if constructed using classes?
- Also, If the user-created menu items were to be saved to a file for later use, which would be the best approach to use? (pickle? plain text file? etc.)
Problems:
- How do I dynamically draw the fixed menu items in the settings menu from a list or dict?
- How to make items selectable by number or a specific character?
- My main struggle came with dynamically displaying and executing sub-menu items.
Preface:
After about 3 weeks of research, trial and error, playing with different approaches and data structures, I ended up scrapping all my code and resigning myself to writing the following code.
This problem originated as a programming that challenged I created for myself and it now haunts me because I am having to admit defeat by asking you guys for guidance. This drives me crazy since I know there are probably very elegant ways of writing this script, but this was the only way I could make it work and produce the desired outputs.
I have spent hours researching stackoverflow and other sites for similar answers, but none fully addressed my particular design goals, such as dealing with sub-menus and the dynamic nature of this menu system.
I should also mention that I have not fully coded every aspect of this script, but there is enough here to allow the creation of menu items and demonstrate what i'm trying to achieve.
#!/usr/bin/python
import sys, os
menu_actions = {}
user_menu_items = []
def add_userItem(item):
returnitem = "item " + item + " created..."
return returnitem
def show_settings():
os.system('clear')
print "\nEdit"
print "Default Settings"
print "\nBack"
choice = raw_input(" >> ")
if choice.lower() == 'back':
main_menu()
else:
decision(choice)
return
def quit():
raise SystemExit()
def s_edit():
os.system('clear')
print "\nCreate"
print "Modify"
print "\nBack\n"
choice = raw_input(" >> ")
if choice.lower() == 'back':
show_settings()
else:
decision(choice)
return
def s_default():
os.system('clear')
choice = raw_input("(**All user created menus will be erased**)\nAre you sure? (YES / NO) or Back\n >> ")
if choice.lower() == 'back':
show_settings()
elif choice.lower() == 'yes':
print 'all settings have been set to default'
del user_menu_items[:] # removing all items from user menu
show_settings()
elif choice.lower() == 'no':
show_settings()
return
def e_create():
go = True
addeditem = []
global user_menu_items
while go:
badinput = False
if badinput == True:
print "That's not a selection. Please try again..."
else:
os.system('clear')
if addeditem != '':
z = 0
for i in addeditem:
print(addeditem[z])
z += 1
if not user_menu_items:
print
print "\n**Menu Items**"
z = 0
for i in user_menu_items:
print(user_menu_items[z])
z += 1
choice = raw_input("\nCreate New Item? (YES / NO)\n >> ")
if choice.lower() == 'yes':
additem = raw_input("\n\nSpecify Name of New Menu Item: ")
addeditem.append(add_userItem(additem))
user_menu_items.append(additem)
else:
if choice.lower() == 'no':
go = False
else:
badinput = True
s_edit()
return
def e_modify():
os.system('clear')
print "Rename"
print "Delete"
print "Move Item"
print "\nBack\n"
choice = raw_input("Choose an action: ")
return choice
def m_rename():
os.system('clear')
print "\nBack\n"
choice = raw_input("Enter a new name: ")
return choice
def m_delete():
os.system('clear')
print "\nBack\n"
choice = raw_input("Select Menu Item to Delete: ")
return choice
def m_move():
os.system('clear')
print "\nBack\n"
choice = raw_input("Specify new location: ")
return choice
def goback():
os.system('clear')
print "\nBack\n"
decision('')
return
def user_menu():
z = 0
for i in user_menu_items:
print(user_menu_items[z])
z += 1
return
def main_menu():
os.system('clear')
user_menu()
print "\nSettings"
print "Quit"
choice = raw_input(" >> ")
decision(choice)
return
def decision(decision):
dec = decision.lower()
if dec == '':
menu_actions['main menu']()
else:
try:
menu_actions[dec]()
except KeyError:
print "invalid selection, please try again.\n"
menu_actions['main menu']()
return
menu_actions = {
'main': main_menu,
'settings': show_settings,
'quit': quit,
'edit': s_edit,
'default': s_default,
'create': e_create,
'modify': e_modify,
'rename': m_rename,
'delete': m_delete,
'move': m_move,
'back' : goback
}
main_menu()
About: As you can see, I have designed a menu creator based on user input, while still providing a fixed menu system for creating and modifying the user-created menus. My desire was to display one tier at a time, making menu items numerically selectable (except for "go back" and "quit"). Below is a pseudo code view of my desired menu tree.
I should also mention that I am fully aware of the fact that the user-created menu items will not have any particular functionality, but I figured I could use this program to create the menus and then code functionality later on -- if desired.
Desired Menu Tree:
Settings
Edit Menu
- Create> # Specify Location of Menu Item.> # Specify Name of New Menu Item.
- Modify> # Select Menu Item to Modify.> # Pick an Action?
- Rename> # Enter a new name
- Delete> # Select Menu Item to Delete.
- Move Item> # Where?
x. Go Back
Default Settings (All user created menus will be erased)> #Are you sure? (YES / NO?)
x. Quit> #exit program
-
\$\begingroup\$ Can you fix the indentation so that your initial code and intent can be undetstood ? Your code doesn't runs with current indentation. Start fixing with line#63 \$\endgroup\$Archit Jain– Archit Jain2014年02月06日 18:47:11 +00:00Commented Feb 6, 2014 at 18:47
-
\$\begingroup\$ All fixed, sorry about that, when I copied the text over and then specified it as code, stackexchange didn't recognize all of my indentions. You should be good to go now. \$\endgroup\$holaymolay– holaymolay2014年02月07日 08:10:27 +00:00Commented Feb 7, 2014 at 8:10
1 Answer 1
Disclaimer : your code is not indented properly and I cannot run it. Also, it seems a bit convoluted to me so I haven't really tried to understand it. However, here are a few comments that might help you if you want to improve your code.
Keep it simple
def add_userItem(item):
returnitem = "item " + item + " created..."
return returnitem
could be
def add_userItem(item):
return "item " + item + " created..."
goback
could just call main_menu()
instead of decision('')
. This would make decision()
much simpler as it shouldn't be its duty to try to handle special cases (see Separation of Concerns)
You don't need to have menu_actions
at the top of your file.
return
at the end of the function is not required if you don't plan to return any value.
choice = raw_input("Choose an action: ")
return choice
could be
return raw_input("Choose an action: ")
For Loop
The way Python loops is designed is supposed to help you to write concise and expressive code : what you write should match pretty closely how you think about things : "I want to do this for each item in this container" instead of "I want to have this index and I start at 0 and I increment and I get the element at this index...".
z = 0
for i in addeditem:
print(addeditem[z])
z += 1
should be written :
for i in addeditem:
print(i)
How nice is this ?
Pretty much everytime you initialise a variable at 0 to use it as an index in a loop you are doing it wrong. If you really need to know what is the current index, enumerate is what you are looking for.
Don't repeat yourself
for i in user_menu_items:
print(i)
appears in your code twice. You should just call user_menu
.
Multiple methods looks quite similar, it might be worth defining a more generic function and then just calling it everywhere
if choice.lower() == 'back':
show_settings()
elif choice.lower() == 'yes':
print 'all settings have been set to default'
del user_menu_items[:] # removing all items from user menu
show_settings()
elif choice.lower() == 'no':
show_settings()
calls lower()
and show_settings
in 3 different places.
It might be easier to do something like :
choice = raw_input("(**All user created menus will be erased**)\nAre you sure? (YES / NO) or Back\n >> ").lower()
if choice == 'yes':
print 'all settings have been set to default'
del user_menu_items[:] # removing all items from user menu
show_settings()
elif choice in ['back','no']:
show_settings()
If you ever change the label of menu
(which most likely happened already - I assume that main menu
is its old name), things will fail and you'll have to fix it in different places in decision
.
What about doing :
def decision(decision):
try:
menu_actions[decision.lower()]()
except KeyError:
print "invalid selection, please try again.\n"
main_menu()
Documentation
You should add comments at least to describe the program and the different main functions.
-
\$\begingroup\$ This is excellent feedback, thank you Josay. I am new to programming and only recently began being more disciplined with using comments. I will go back and work on it cleaning things up a bit before submitting my edits. Thanks! \$\endgroup\$holaymolay– holaymolay2014年02月12日 17:40:20 +00:00Commented Feb 12, 2014 at 17:40