Adding another sub-menu to existing code - python

The code below has a main menu and a sub menu. The first menu prompts on load and selecting an option will load another "page" with more options. I cannot figure out how to get to another sub section from here. Called sub2 for example.
Lets say when Option 1 is selected it will prompt you two choose two sub options option1_sub2_1 and option1_sub2_2.
Lets say when you select Option 2 it prompts for option2_sub2_1 option2_sub2_2
I have one set of options stored as:
menu_actions = {
'main_menu': main_menu,
'1': one,
'2': two,
'b': back,
'q': exit,
}
It is possible to have it setup somehow like this:
option1_sub2 = {
'1': one,
'2': two,
}
and
option2_sub2 = {
'1': one,
'2': two,
}
That way I can re-use the same numbers for the different sub2 items. Is this possible?
This is my first code project and I have not been able to make it past this part. Any help would be appreciated.
The code:
import sys, os
# Main definition - constants
menu_actions = {}
# =======================
# MENUS FUNCTIONS
# =======================
# Main menu (display list on screen)
def main_menu():
os.system('clear')
print "1. Option"
print "2. Option"
choice = raw_input(" >> ")
exec_menu(choice)
return
# Choose a menu
def exec_menu(choice):
os.system('clear')
ch = choice.lower()
if ch == '':
menu_actions['main_menu']()
else:
try:
menu_actions[ch]()
except KeyError:
print "Invalid selection, please try again.\n"
menu_actions['main_menu']()
return
# =======================
# SUB MENUS
# =======================
# Back to main menu
def back():
menu_actions['main_menu']()
# Exit program
def exit():
sys.exit()
# Option1
def one():
print "Option 1"
print (30 * '-')
print "Sub Option 1"
print "Sub Option 2"
print "[B]ack"
print "[Q]uit"
choice = raw_input(" >> ")
exec_menu(choice)
return
# FTP
def two():
print (30 * '-')
print "Sub Option 1"
print "Sub Option 2"
print "[B]ack"
print "[Q]uit"
choice = raw_input(" >> ")
exec_menu(choice)
return
# =======================
# MENUS DEFINITIONS
# =======================
# Menu definition
menu_actions = {
'main_menu': main_menu,
'1': one,
'2': two,
'b': back,
'q': exit,
}
# =======================
# MAIN PROGRAM
# =======================
# Main Program
if __name__ == "__main__":
# Launch main menu
main_menu()

start by abstracting it out
first create a method that will continually prompt for input until a given input result is achieved
def input_option(prompt,options):
while True:
result = raw_input(prompt)
if result in option: return result
print "Invalid Option Selected %r"%result
test this function with things like print input_option("Enter 1 or 2 or Three",["1","2","Three"])
now abstract out a function to display a menu and get input from the menu
def select_menu(options):
print "Select From the choices Below:"
options = []
for option_text,input_value in options:
print option_text
options.append(input_value)
return input_option(">>>",options)
you can test this like
options = [("1> option 1","1"),("2> option 2","2")]
print select_menu(options)
options2 = [("[B]ack","B"),("[Q]uit","Q")]
print select_menu(options2)
then all you have to do is tie it together by mapping actions to inputs
menu_otions = [("Show Menu [1]","1"),("Show Menu [2]","2"),("[Q]uit","Q")]
menu_actions = {"1":show_menu1,"2":show_menu2,"Q":sys.exit

Related

How do i use the value in a list, with combo - pysimplegui

import PySimpleGUI as sg
storedvals = []
sg.LOOK_AND_FEEL_TABLE['Theme'] = {'BACKGROUND': '#292929',
'TEXT': '#009bff',
'INPUT': '#ffffff',
'TEXT_INPUT': '#000000',
'SCROLL': '#ffffff',
'BUTTON': ('#5cfd46', '#151515'),
'PROGRESS': ('#01826B', '#D0D0D0'),
'BORDER': 1, 'SLIDER_DEPTH': 0, 'PROGRESS_DEPTH': 0,
}
sg.theme("Theme")
def mainmenu():
mainmenu = [
[sg.Text("Welcome to the Calculator!")],
[sg.Text("What would you like to do:")],
[sg.Text("1) Add\n2) Subtract\n3) Multiply\n4) Divide\n5) Exit")],
[sg.Text("Please enter the number of the option you would like!")],
[sg.InputText("", key="in1"),sg.Text("", key="answer")],
[sg.ReadButton("Submit")]
]
mainmenu = sg.Window('Window that stays open', mainmenu)
while True:
button, values = mainmenu.Read()
if button is None:
break
choice = int(values["in1"])
mainmenu.FindElement("answer").Update(choice)
if choice == 1:
mainmenu.Close()
add()
if choice == 2:
mainmenu.Close()
subtract()
if choice == 3:
mainmenu.Close()
multiply()
if choice ==4:
mainmenu.Close()
divide()
if choice == 5:
exit()
def add():
add = [
[sg.Text("What numbers would you like to add together?")],
[sg.Text("Stored values are in the drop down option!")],
[sg.Combo([storedvals], size=(5,1), key="num1"), sg.Text("+"), sg.Combo([storedvals], size=(5,1), key="num2")],
[sg.Text("Do you want to store this value")],
[sg.Radio("Yes", "1", key="x"), sg.Radio("No", "1",)],
[sg.ReadButton("Add")],
[sg.Text("Answer:"), sg.Text("", size=(10,1), key="answer")],
[sg.Button("Return to the menu"), sg.Exit()],
]
add = sg.Window("", add)
while True:
button, values = add.Read()
if button is None:
break
num1 = int(values["num1"])
num2 = int(values["num2"])
answer = num1 + num2
add.FindElement("answer").Update(answer)
x = values["x"]
if x == 1:
storedvals.append(answer)
sg.Popup("You answer has been stored in position", storedvals.index(answer))
print(storedvals)
event, values = add.Read()
if event == "Return to the menu":
add.Close()
mainmenu()
mainmenu()
This is my code. After this subroutine is complete, they can return to the main menu to use the calculator again. I want it so that they can store a previous calculation answer and then choose to use it in later calculations (which works). I opted to use the combo, however, I get an error and after researching the error I can't find a fix/workaround to the problem. Any tips are helpful.
The error: TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
I have tried multiple different ways to try to manipulate it, but I have just started to learn pysimple GUI, so I am stuck.
The solution is to add element.update(values=answer), where
answer should be adding a complete list and not just a single entry.

How to make a menu in Python navigable with arrow keys

I'm making a text-based game with an option to select a class for their character. At present, the player enters their option, either typing in a number or the name of the class. It works well enough.
However, I would like to have the player navigate the menu with the arrow keys and select an option using the "enter" key. In order to make it clear which option they are about to select, I would also like to have the text of the selected option highlighted. If you've ever played an ASCII roguelike, you know what it looks like.
Here is the code that I currently have for classes:
def character():
print "What is your class?"
print "1. The sneaky thief."
print "2. The smarty wizard."
print "3. The proletariat."
charclass = raw_input("> ")
if charclass == "1" or "thief":
charclass = thief
print "You are a thief!"
elif charclass == "2" or "wizard":
charclass = wizard
print "You are a wizard!"
elif charclass == "3" or "prole":
charclass = prole
print "You are a prole!"
else:
print "I'm sorry, I didn't get that"
Thanks!
As it was already mentioned in a comment, you may use curses. Here is a small working menu to achieve what you want
import curses
classes = ["The sneaky thief", "The smarty wizard", "The proletariat"]
def character(stdscr):
attributes = {}
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
attributes['normal'] = curses.color_pair(1)
curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE)
attributes['highlighted'] = curses.color_pair(2)
c = 0 # last character read
option = 0 # the current option that is marked
while c != 10: # Enter in ascii
stdscr.erase()
stdscr.addstr("What is your class?\n", curses.A_UNDERLINE)
for i in range(len(classes)):
if i == option:
attr = attributes['highlighted']
else:
attr = attributes['normal']
stdscr.addstr("{0}. ".format(i + 1))
stdscr.addstr(classes[i] + '\n', attr)
c = stdscr.getch()
if c == curses.KEY_UP and option > 0:
option -= 1
elif c == curses.KEY_DOWN and option < len(classes) - 1:
option += 1
stdscr.addstr("You chose {0}".format(classes[option]))
stdscr.getch()
curses.wrapper(character)
The last call to getch is just so you can see the result before the program terminates
I would consider using simple-term-menu : https://github.com/IngoMeyer441/simple-term-menu
This project allows you to search through the menu using '/'
Like Jona's answer but actually runs :P
Also have a longer version here that does vertical and horizontal menu: https://pastebin.com/raw/zv9EXdgH
#!/usr/bin/env python3
# SOURCE: https://docs.python.org/2/library/curses.html
# SOURCE: https://docs.python.org/3/howto/curses.html
# For Windows: pip install windows-curses
import curses
window = curses.initscr() # Initialize the library. Returns a WindowObject which represents the whole screen.
window.keypad(True) # Escape sequences generated by some keys (keypad, function keys) will be interpreted by curses.
curses.cbreak() # Keys are read one by one. Also safer than curses.raw() because you can still interrupt a running script with hotkeys.
curses.noecho() # Prevent getch() keys from being visible when pressed. Echoing of input characters is turned off.
# Initialize colors.
curses.start_color() # Must be called if the programmer wants to use colors.
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK)
black = curses.color_pair(1)
white = curses.color_pair(2)
def display_menu(window):
selectedIndex = 0
while True:
window.clear()
window.addstr('Pick an option:\n', curses.A_UNDERLINE)
for i in range(len(MENU_OPTIONS)):
# Uncolored line number.
window.addstr('{}. '.format(i + 1))
# Colored menu option.
window.addstr(MENU_OPTIONS[i] + '\n', black if i == selectedIndex else white)
c = window.getch()
if c == curses.KEY_UP or c == curses.KEY_LEFT:
# Loop around backwards.
selectedIndex = (selectedIndex - 1 + len(MENU_OPTIONS)) % len(MENU_OPTIONS)
elif c == curses.KEY_DOWN or c == curses.KEY_RIGHT:
# Loop around forwards.
selectedIndex = (selectedIndex + 1) % len(MENU_OPTIONS)
# If curses.nonl() is called, Enter key = \r else \n.
elif c == curses.KEY_ENTER or chr(c) in '\r\n':
# If the last option, exit, is selected.
if selectedIndex == len(MENU_OPTIONS) - 1:
curses.endwin() # De-initialize the library, and return terminal to normal status. <-- Works without this on Windows, however in Linux you can't type in the terminal after exiting without this :P
break
window.addstr('\nYou choose {}\n'.format(MENU_OPTIONS[selectedIndex]))
window.getch()
else:
window.addstr("\nThe pressed key '{}' {} is not associated with a menu function.\n".format(chr(c), c))
window.getch()
MENU_OPTIONS = [
'Option 1',
'Option 2',
'Option 3',
'Exit',
]
if __name__ == '__main__':
display_menu(window)

Issue doing a basic option menu system - Aways getting the message "Option not available"

Im showing some text options associated to numbers and then I want to execute some functions based on number that user enter.
First user need to choose 1 or 2, when user choose 1 it is working fine.
But when user choose 2 then I ask user to select other option, and when user choose any number it is always showing option not available.
But I just want to show this message when user choose a number that isnt 3,4,5 or 7.
Do you see where the issue is and how to fix this logic?
def nav(number):
while True:
input = raw_input(number)
if input == "1":
upload()
return False
elif input == "2":
results = table.get()
# here I show the name of users from database
for r in results:
print r["name"]
print " 3 - Add user"
print " 4 - Edit user"
print " 5 - Remove user"
print " 7 - Exit"
nav("Select an option: ")
if input == "":
print "field is empty"
if input == "3":
addUser()
return False
if input == "4":
removeUser()
if input== "5":
editUser()
elif input== "7":
return False
else:
print "Option not available"
def main():
print " 1 - Users managment"
print " 2 - Upload Files"
print " 7 - Exit"
nav("Select an option: ")
main()
You should have two functions. One that asks you to choose an option and one that parses that choice. For instance:
def upload():
# does whatever upload does....
def user_mgmt():
def adduser():
"""Adds a new user"""
pass
def edituser():
"""Edits an existing user"""
pass
def deluser():
"""Deletes an existing user"""
pass
response_options = {'3': ('add user', adduser),
'4': ('edit user', edituser),
'5': ('remove user', deluser),
'7': ('exit', sys.exit)}
response_func = make_choice(response_options)
response_func()
def nav():
response_options = {'1': ('manage users', user_mgmt),
'2': ('upload', upload),
'7': ('exit', sys.exit)}
response_func = make_choice(response_options)
response_func()
def make_choice(optiontable):
for resp, msg_func in optiontable.items():
msg, _ = msg_func
print("{} - {}".format(resp, msg))
usr_resp = raw_input(">> ")
try:
result = optiontable[usr_resp][1]
except KeyError:
raise # let the caller handle it
return result
This is actually a pretty good use case for a collections.namedtuple
from collections import namedtuple
Choice = namedtuple("Choice", ['msg', 'callback'])
def nav():
response_options = {'1': Choice(msg="Add user", callback=adduser),
...}
result = make_choice(response_options)
if result is None:
# improper user input -- handle it
else:
result.callback()
def make_choice(optiontable):
for resp, choiceobj in optiontable.items():
print("{} - {}".format(resp, choiceobj.msg))
usr_resp = raw_input(">> ")
return optiontable.get(usr_resp, None)

Calling function inside if statement

im trying to call function inside if statement but it does not work. This is one of my first attempts in using Python. What am I doing wrong?
#!/usr/bin/python
menu = raw_input ("Hello, please choose form following options (1,2,3) and press enter:\n"
"Option 1\n"
"Option 2\n"
"Option 3\n")
if menu == str("1"):
savinginfile = raw_input ("Please, state your name: ")
option1()
elif menu == str("2"):
print ("Option 2")
elif menu == str("3"):
print ("Option 3")
def option1():
test = open ("test.txt", "rw")
test.write(savinginfile)
print ("Option 1 used")
test.close()
Would recommend that you pass savinginfile as a parameter:
def option1(savinginfile):
test = open ("test.txt", "rw")
test.write(savinginfile)
print ("Option 1 used")
test.close()
You need to define option1 before calling. Python interprets from top to bottom.
You need to define your function before you try to call it. Just put def option1(): #and all that code below it above your if statements.
It's also bad practice to throw around too many global variables. You shouldn't use savinginfile the way you are -- instead, pass it to the function as a parameter and let the function operate in its own scope. You'll need to pass the function the name of the file to use before it's able to use savinginfile. Try instead:
def option1(whattosaveinfile):
test = open("test.txt","a+") #probably better to use a with statement -- I'll comment below.
test.write(whattosaveinfile) #note that you use the parameter name, not the var you pass to it
print("Option 1 used")
test.close()
#that with statement works better for file-like objects because it automatically
#catches and handles any errors that occur, leaving you with a closed object.
#it's also a little prettier :) Use it like this:
#
# with open("test.txt","a+") as f:
# f.write(whattosaveinfile)
# print("Option 1 used")
#
#note that you didn't have to call f.close(), because the with block does that for you
#if you'd like to know more, look up the docs for contextlib
if menu == "1": #no reason to turn this to a string -- you've already defined it by such by enclosing it in quotes
savinginfile = raw_input("Please state your name: ")
option1(savinginfile) #putting the var in the parens will pass it to the function as a parameter.
elif menu == "2": #etc
#etc
#etc

How to have a versatile function call that can call different functions in Python?

I'm trying to make a text-based game in Python, however, code could get out of hand pretty quickly if I can't do one thing on one line.
First, the source code:
from sys import exit
prompt = "> "
inventory = []
def menu():
while True:
print "Enter \"start game\" to start playing."
print "Enter \"password\" to skip to the level you want."
print "Enter \"exit\" to exit the game."
choice = raw_input(prompt)
if choice == "start game":
shell()
elif choice == "password":
password()
elif choice == "exit":
exit(0)
else:
print "Input invalid. Try again."
def password():
print "Enter a password."
password = raw_input(prompt)
if password == "go back":
print "Going to menu..."
else:
print "Wrong password. You are trying to cheat by (pointlessly) guess passwords."
dead("cheating")
def shell(location="default", item ="nothing"):
if location == "default" and item == "nothing":
print "Starting game..."
# starter_room (disabled until room is actually made)
elif location != "default" and item != "nothing":
print "You picked up %s." % item
inventory.append(item)
location()
elif location != "default" and item == "nothing":
print "You enter the room."
location()
else:
print "Error: Closing game."
def location():
print "Nothing to see here."
# Placeholder location so the script won't spout errors.
def dead(reason):
print "You died of %s." % reason
exit(0)
print "Welcome."
menu()
First, an explanation on how my game basically works.
The game has a 'shell' (where input is done) which receives information from and sends information to the different 'rooms' in the game, and it stores the inventory. It can receive two arguments, the location and an eventual item to be added to the inventory. However, line 40-42 (the first elif block in 'shell') and line 43-45 (the last elif block in 'shell') are supposed to go back to whatever location the location was (line 42 and 45, to be exact). I've tried "%s() % location" but that doesn't work, it seems to only work when printing things or something.
Is there any way to do this? If not, even writing an engine for this game would be a nightmare. Or I'd have to make an entirely different engine, which I think would be a way better approach in such a case.
Sorry if I made any mistakes, first question/post ever.
elif location != "default" and item != "nothing":
print "You picked up %s." % item
inventory.append(item)
location()
elif location != "default" and item == "nothing":
print "You enter the room."
location()
I guess you want to call a function having its name. For that you need a reference to the module or class inside which it was defined:
module = some_module # where the function is defined
function = getattr(module, location) # get the reference to the function
function() # call the function
If the function is defined in the current module:
function = globals()[location]
function() # call the function
If I correctly understand what you want is something like this : player will enter a location name and you want to call the related method. "%s"()%location will not work, a string (that is what is "%s" is not callable).
Let's try an OOP way :
class Maze:
def __init__(self):
# do what you need to initialize your maze
def bathroom(self):
#go to the bathroom
def kitchen(self):
# go to the kitchen
def shell(self, location="", item=""):
if location == "" and item == "":
print "Starting game..."
# starter_room (disabled until room is actually made)
elif location and item:
print "You picked up %s." % item
inventory.append(item)
getattr(self, location)()
elif location and item == "":
print "You enter the room."
getattr(self, location)()
else:
print "Error: Closing game."
maze = Maze()
while True: # or whatever you want as stop condition
location = raw_input("enter your location :")
item = raw_input("enter your location :")
maze.shell(location=location, item=item)
I think you can use the getattr() method.
Example : You want to call method "helloword()" from module "test", you would then do :
methodYouWantToCall = getattr(test, "helloworld")
caller = methodYouWantToCall()
Hope it gives you a clue.

Categories