I am creating my first Python project. What I am working on is a simple text-based metric to standard (and vice versa) conversion program. I am not seeking the code as I want to figure this out myself. However, I am running into a problem traversing the menu. For example, there are 3 menus: main menu, metric menu, and standard menu. The program opens to the main menu. On the main menu the user can choose to navigate to the metric menu, standard menu, or to exit the program. If the user wants to be able to return to the main menu from the metric or standard menus, what would be the best approach? I have tried while loops and if/elif. However, it seems that my code gets really bloated and convoluted. Could someone please give me some advice on creating and traversing text menus in Python?
You can try something like this:
import sys
import os
def switch():
clear = lambda: os.system('cls')
clear()
s = 0
while s != 5:
s = int(input('1)Metric 2)Standard 3)Exit\n'))
if s == 1:
clear = lambda: os.system('cls')
clear()
metric()
elif s == 2:
clear = lambda: os.system('cls')
clear()
standard()
elif s == 3:
clear = lambda: os.system('cls')
clear()
exit()
else:
print('Out of range.')
def metric():
'''Your code here'''
s = int(input('0)Back\n'))
if s == 0:
switch()
else:
print('Out of range.')
def standard():
'''Your code here'''
s = int(input('0)Back\n'))
if s == 0:
switch()
else:
print('Out of range.')
switch()
Since you said you were new to Python, the __init__() method of a class is called when the object is first constructed. You can read the docs on classes here (skip to 9.3).
I don't know how efficient this would be, but you could use a class that stores other objects of the same class:
class Menu:
def __init__(self, content, short_view, submenus = None):
self.content = content
self.short_view = short_view
if submenus != None:
self.choices = dict(enumerate(submenus, 1))
for sub in submenus:
sub.parent = self
else:
self.choices = {}
subsub1 = Menu("this is subsub1 content", "this goes to subsub1")
subsub2 = Menu("this is subsub2 content", "this goes to subsub2")
subsub3 = Menu("this is subsub3 content", "this goes to subsub3")
sub1 = Menu("this is the first sub menu content", "this goes to sub1", [subsub1, subsub2, subsub3])
sub2 = Menu("this is the second sub menu content", "this goes to sub2")
main = Menu("this is the main menu content", "this goes to main, but will never be used", [sub1, sub2])
main.parent = main
current_menu = main
while True:
print(current_menu.content)
print("\n".join([f"[{num}] {current_menu.choices[num].short_view}" for num in current_menu.choices]))
inpt = input("Choice: ")
if inpt == "exit":
break
elif inpt == "back":
current_menu = current_menu.parent
else:
current_menu = current_menu.choices[int(inpt)]
Usage (in shell):
this is the main menu content
[1] this goes to sub1
[2] this goes to sub2
Choice: 1
this is the first sub menu content
[1] this goes to subsub1
[2] this goes to subsub2
[3] this goes to subsub3
Choice: back
this is the main menu content
[1] this goes to sub1
[2] this goes to sub2
Choice: exit
>>>
Try using the following code :
def main():
a=int(input("1) metric\n2) standard\n3) exit\n"))
if a==1:
metric()
elif a==2:
standard()
elif a==3:
main() #return main()
def metric():
# your code
pass
def standard():
# your code
pass
main()
Related
Im learning python and I already know the basics. So i'm trying to make a simple program: "A note pad".
Im only using the console and I want to have the following menus
main menu, read note menu, write menu, etc. To do that i'm using a function for each menu, printing the menu, using input() to choose the next menu and then I call the function of the next menu.
But if I have a try / except block in a "a menu", and there's an error in another function called inside that block it will go back and execute the except block.
How can might I be able to avoid this, or is there any better way to make the menus?
Here's what I have:
noteList = [["Here goes the title", "Here goes the text", "Here goes the date"], ["Example", "Text Text Text Text Text Text Text Text", "08/08/2020"]] # note list
#-------------Main Menu-------------
def mainMenu():
print(*"NOTES") # title
print("Write:")
print('-The number of the note to read;')
print('-[N] to create a [N]ew note;')
print('-[D] to [D]elete a note;')
print('-[E] to [E]xit;')
# saved notes
if len(noteList) == 0: # no saved notes
print("\nYou have no notes.")
else: # display saved notes
print("\nSaved notes:")
for number in range(len(noteList)):
print(str(number + 1) + "-" + noteList[number][0])
# ask what action to do
action = input()
actionChooser(action)
#-------------Action Chooser-------------
def actionChooser(action):
try:
action = int(action) - 1 # action is int
readNote(action)
except:
if action == "N" or action == "n": # action is N
newNote()
elif action == "D" or action == "d": # action is E
deleteNote()
elif action == "E" or action == "e": # action is S
exit()
else: # invalid action
print("Invalid action.\n")
mainMenu()
#---------------------------------------
def readNote(noteNumber):
print(*"\n" + noteList[noteNumber][0].upper())
print(noteList[noteNumber][1])
mainMenu()
Thanks and my apologies if I have accedently broken one of your rules it's my first post here.
I will describe how I see it in pseudocode:
1. start program
2. defining processing functions
3. print instructions
4. endless loop
5. wait for input
6. action with try/except
7. exit program
And example program:
import sys
def action_howto():
print("""Instructions:
'test' for action_test
'exit' for exit
""")
def action_test():
print("action_test executed\n")
def action_exit():
sys.exit("bye-bye")
def do_action(action):
try:
{
"test": action_test,
"exit": action_exit
}.get(action.lower(), action_howto)()
except Exception as e:
action_howto()
def main():
action_howto()
while True:
action = input()
do_action(action)
if __name__ == "__main__":
main()
I tried writing a program that tests writing skills. (using tkinter)
For use type in y, press enter, write the sentence you were given press enter again and it outputs the time it took you. This is the part that works.
To add more flexibility I wanted to add a button that shuffles the sentence and gives you a new one. I've tried random.choice from a list, defining the variable as parameter and am currently stuck at random.randint. None of them have worked so far. They output a random sentence after launching the code, but the button simply does nothing.
heres the code of the function and further down where the button its used in. if the complete code is necessary to you please just ask for it :D
def change_sentence():
var = random.randint(0,5)
if var == 1:
sentence_var = "thats gonna work"
return sentence_var
if var == 2:
sentence_var = "probably"
return sentence_var
if var == 3:
sentence_var = "i guess"
return sentence_var
if var == 4:
sentence_var = "maybe?"
return sentence_var
if var == 5:
sentence_var = "please??"
return sentence_var
sentence_var = change_sentence()
shuffle_button = Button(root, text="Shuffle", command=change_sentence)
to conclude, i simply look for a way of making my button working.
Having your callback function return a variable doesn't really do anything. Specifically, it won't update the text in your GUI.
You should look into StringVar and using textvariable parameter for you text object. Here's a small example. I also cleaned up your if statement:
root = tk.Tk()
class tkApp:
def __init__(self, master):
self.sentence_var = tk.StringVar()
self.change_sentence()
sentence_text = tk.Label(master, textvariable=self.sentence_var)
shuffle_button = tk.Button(master, text="Shuffle", command=self.change_sentence)
sentence_text.pack()
shuffle_button.pack()
def change_sentence(self):
var = np.random.randint(1, 6)
if var == 1:
self.sentence_var.set("thats gonna work")
elif var == 2:
self.sentence_var.set("probably")
elif var == 3:
self.sentence_var.set("i guess")
elif var == 4:
self.sentence_var.set("maybe?")
elif var == 5:
self.sentence_var.set("please??")
myApp = tkApp(root)
root.mainloop()
You need to put a print statement inside the function if you just want to print the sentence. Your variable sentence_var will be assigned a string when calling the function but after that the Button will have no effect on it as it is not inside the function.
Just started using python one week ago. At the moment I am trying to code a small program class which creates a menu in a terminal. The idea is to go through the menu using up / down keys. You can select a menu item by pressing enter. I control the keys being pressed using the module "keyboard".
In order to use my class, one has to create an object and add menu items by means of the method "add_menu". The latter mentioned method has two arguments, the first one is used for the name of the menu item, the second one is used to hand over a function, which will be called in case enter was pressed.
In order to check if a key was pressed, I use the method keyboard.on_press from the module keyboard. In case a key was pressed, the method keyboard.on_press executes the method handle_menu of the menu object. The handle_menu method uses a list named "controller" in order to organize the selection of a menu item. Basically, it is just a list like [0,0,1,0]. The element being 1 indicates the currently selected menu item.
Now to my problem: My menu has a menu item "Exit". If this is selected and enter is pressed, I want the whole program to stop. Therefore, if exit was pressed the attribute exit is changed from 0 to 1. In the while loop of my program I always check if object.exit is != 1, if not the program should end. Somehow this does not always work. If I scroll down immediately from the beginning, without pressing enter at other menu items, it works. However, if I press enter several times at other menu items and then go to the exit menu item, the program does not end anymore (or only if I press enter for 10-20 times). I have the feeling that the keyboard.on_press method and the while loop are sometimes uncoupled in the background and run asynchronously? I do not really understand what is going on...
import keyboard #Using module keyboard
import os
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def start_function():
print('Start works')
def save_function():
print('Save works')
def option_function():
print('Option works')
class c_menu:
def __init__ (self):
self.exit = 0
self.menu = []
self.functions = []
self.controller = []
def add_menu(self, menu, function):
self.menu.append(menu)
self.functions.append(function)
if len(self.controller) == 0:
self.controller.append(1)
else:
self.controller.append(0)
def start_menu(self):
os.system('cls' if os.name == 'nt' else 'clear')
for menu_item in range(len(self.menu)):
if self.controller[menu_item] == 1:
print(bcolors.WARNING + self.menu[menu_item])
else:
print(bcolors.OKBLUE + self.menu[menu_item])
def handle_menu(self, event):
os.system('cls' if os.name == 'nt' else 'clear')
if event.name == 'down':
if self.controller.index(1) != (len(self.controller) - 1):
self.controller.insert(0,0)
self.controller.pop()
elif event.name == 'up':
if self.controller.index(1) != 0:
self.controller.append(0)
self.controller.pop(0)
for menu_item in range(len(self.menu)): #printing all menu items with the right color
if self.controller[menu_item] == 1:
print(bcolors.WARNING + self.menu[menu_item])
else:
print(bcolors.OKBLUE + self.menu[menu_item])
if event.name == 'enter':
if self.functions[self.controller.index(1)] == 'exit':
self.exit = 1
return
self.functions[self.controller.index(1)]()
main_menu = c_menu()
main_menu.add_menu('Start', start_function)
main_menu.add_menu('Save', save_function)
main_menu.add_menu('Option', option_function)
main_menu.add_menu('Exit', 'exit')
main_menu.start_menu()
keyboard.on_press(main_menu.handle_menu)
while main_menu.exit != 1:
pass
I think I understood the problem. The program is actually ending properly, however, the last "enter" pressed is still in a kind of buffer (or something similar) and after the end of program, the terminal command "python menu.py" is executed again and again (it goes so fast that it looks like the program did not end). Unfortunately, I do not really understand why this is happening.
My solution so far, I use "keyboard.send('ctrl+c')" at the very end of my program (after the while loop). This prevents the terminal to re-execute the command "python menu.py" again.
I am new to python and learning quickly. Thank you all for the help.
I am attempting to create a text menu that will always run in the background of a storytelling text rpg. I have searched and cannot find an explanation of how to create an "always on" menu or how one would work.
I would like the player to be able to hit "m" at any time in the game and have the menu prompt show up.
So far, I have created a "userinput" function as well as a "menu" function that will be deployed each time the game prompts the user/player for input.
def menu():
print('Press "1" for map >>> "2" for stats >>> "3" for exit')
choice = input()
if choice == '1':
print('map needs to be made and shown')
elif choice == '2':
print('stats need to be made and assinged to choice 2 in def menu')
elif choice == '3':
print('You are exiting the menu. Press "M" at any time to return to the menu')
return
else:
print('I did not recognize your command')
menu()
def userinput():
print('Press 1 to attack an enemy >>> 2 to search a room >>> 3 to exit game')
print('Press "M" for menu at any time')
inputvalue = input()
if inputvalue == 'm':
menu()
elif inputvalue == '1':
print('attack function here')
elif inputvalue == '2':
print('search function here')
elif inputvalue == '3':
exit
else:
userinput()
This does not appear to be an ideal solution because the user cannot choose to view a map or exit the game at any time they want.
Is there a way to have a menu always running in the background?
I thought of using a while loop that would never close and all of the game would be held within that while loop but that doesn't seem economical by any means.
Any thoughts or help would be appreciated.
I took a stab at it. This is perhaps not the best structure for doing what you're looking for but I don't want my reply to get too complicated.
The "standard" approach for anything with a UI is to separate the model, the view and the control. Check out MVC architecture online. While it adds complexity at the start it makes life much simpler in the long run for anything with a non trivial UI.
Other points of note are:
you're not stripping whitespace from your input (potentially problematic "3 " won't do what you want)
you're input is case sensitive (you ask for "M" but check for "m") .. maybe use choice = choice.strip.lower()??
there's a difference between the way raw_input and input work between Python 2 and Python 3 which means your code doesn't work in python 2. What's the difference between raw_input() and input() in python3.x? I've changed my example to use raw_input. You may want to use this work around http://code.activestate.com/recipes/577836-raw_input-for-all-versions-of-python/ near the top of your code for portability.
Some code
# flag we set when we're done
finished = False
def finish():
# ask the user for confirmation?
global finished
finished = True
return
def handle_menu_input(choice):
handled = True
if choice == '1':
print('map needs to be made and shown')
elif choice == '2':
print('stats need to be made and assinged to choice 2 in def menu')
else:
handled = False
return handled
def menu():
finished_menu = False
while not finished_menu:
print('Press "1" for map >>> "2" for stats >>> "3" for exit')
choice = raw_input() # NOTE: changes behaviour in Python 3!
if handle_menu_input(choice):
# done
pass
elif choice == '3':
print('You are exiting the menu. Press "M" at any time to return to the menu')
finished_menu = True
else:
print('I did not recognize your command')
menu()
return
def userinput():
print('Press 1 to attack an enemy >>> 2 to search a room >>> 3 to exit game')
print('Press "M" for menu at any time')
choice = raw_input() # NOTE: changes behaviour in Python 3!
if choice == 'm':
menu()
elif choice == '1':
print('attack function here')
elif choice == '2':
print('search function here')
elif choice == '3':
finish()
# elif handle_menu_input(choice):
# # delegate menu functions?? ..
# # do this if you want to see maps anytime without going through the menu?
# # otherwise comment this elif block out.
# # (Problem is 1, 2 etc are overloaded)
# pass
else:
print('I did not recognize your command')
return
def main():
# main loop
while not finished:
userinput()
return
if __name__ == "__main__":
main()
How should I write this so I could constantly move between "menus".
#!/bin/env python
import os
class Menu:
def __init__(self):
self.menu = '1'
def Main(self):
os.system('clear')
print "main menu"
test = raw_input()
if test == '2':
self.menu = '2'
def Sub(self):
os.system('clear')
print "sub menu"
test = raw_input()
if test == '1':
self.menu = '1'
menu = Menu()
while menu.menu == '1':
menu.Main()
while menu.menu == '2':
menu.Sub()
At the moment I can swap once. ie I start with menu.Main(), enter '2' and menu.Sub() is shown. But then when I enter '1' the program quits. why does it not go back to showing menu menu.Main() ? Any thoughts welcome!
EDIT:
just needed to put them in a main while loop
The first while loop runs, and when you enter '2', finishes. Therefore, the second while loop will begin to loop.
In the second while loop, you enter '1', which causes the second while loop to finish (because menu.menu is now == '1'). Thus, the program finishes.
Instead, you'll probably want one value for menu (that is neither '1' nor '2') to act as the exit state. For example, 'E'. Then, you can replace your two while loops with the following:
while menu.menu != 'E':
menu.Do()
The "Do" method will handle the menu state if it's 1 or 2.
class Menu:
def __init__(self):
self.menu = '1'
def Do(self):
if self.menu == '1':
self.Main()
elif self.menu == '2':
self.Sub()
def Main(self):
os.system('clear')
print "main menu"
test = raw_input()
if test == '2':
self.menu = '2'
def Sub(self):
os.system('clear')
print "sub menu"
test = raw_input()
if test == '1':
self.menu = '1'
You will still need to make it so that you can actually get to the 'E' case. I'll leave that as a task for you to finish.
First of all, please keep the coding conventions when naming your classes and functions.
modules in python start with lowercase and usually do not include more a than a word, if it does it is separated by underscore. (e.g. datetime)
Classes in python start with uppercase and CamelCased for more than one word. (e.g. MainClass)
functions in python start with lowercase and separated with underscore (e.g. keep_trying)
I have been trying to read your code and misunderstood the code because of your style of naming. It is a bad practise for peer-reviewing, also bad practise for yourself as you will have hard time to understand the other codes.
--
To your question, if you follow the workflow you'll see that when you call menu you cannot call sub as your if statement only checks that if it is 1, not 2.
Here is a better way:
#!/bin/env python
import os
class Menu:
def __init__(self):
self.get_input()
def get_input(self):
input = raw_input()
if input == '1':
self.main()
elif input == '2':
self.sub()
def main(self):
os.system('clear')
print "main menu"
self.get_input()
def sub(self):
os.system('clear')
print "sub menu"
self.get_input()
menu = Menu()