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.
Related
I'm writing a script for game, i need to bind some key for pressing right click for three times. I can do it, but if i will use it in game i can press WASD or something else when i using hotkey and it doesn't work.
My code here :
import keyboard
import mouse
import time
iswork = False
def work():
global iswork
if iswork:
iswork = False
print("Script deactivated")
else:
iswork = True
print("Script activated")
keyboard.add_hotkey("F4",work)
def RRR():
if iswork :
for i in range(3):
mouse.right_click()
time.sleep(0.05)
bind = input("Bind for Right - Right - Right :")
#keyboard.add_hotkey(bind,RRR)
while iswork:
if keyboard.is_pressed("x") and len(keyboard._pressed_events) >= 1:
RRR()
elif keyboard.is_pressed("x"):
RRR()
while True :
count = 0
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 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()
I'm having some trouble with creating a program that automates a checkout process. I'm using python 3 along with Selenium. The program parses through a range of dates, which are outputted on the page as available four available slots. If none are available on the current page, it will click the 'next' button and search through the next four dates. If it gets to the end of the available date ranges and finds nothing, it'll wait thirty seconds and reset and do it all over again.
I've got the majority of this done, except for two issues:
1) I'm trying to add an argument that, when included, will go beyond the base functionality (which is to simply notify the user via text using Twilio), and complete the full checkout process.
This is the python code I'm using:
def find_available(args):
dates_available = True
spaces_free = False
free_spaces = ""
while not spaces_free:
while dates_available:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
free_spaces = selected_spot.text.replace('Select a time', '').strip()
spaces_free = True
else:
print(selected_spot.text.replace('Select a time', '').strip())
if spaces_free:
print('Slots Available!')
if args.checkout:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
driver.find_element_by_xpath("//*[contains(text(), 'Soonest available')]").click()
time.sleep(1.5)
driver.find_element_by_xpath("//input[#type='submit' and #value='Continue']").click()
print('Your order has been placed!')
else:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
print('Your order time will be held for the next hour. Check your date and confirm!')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="auto-checkout")
parser.add_argument('--checkout', '-c', action='store_true',
help="Select first available slot and checkout")
args = parser.parse_args()
find_available(args)
Expected Behavior
If the program is launched using the '--checkout' or '-c' argument, then, once 'spaces-free' is set to true, it should send a text with the text within the 'free_spaces' element. It should then move on to the next phase, which would be a selecting of a radio button that contains the text 'Soonest available' (as in select the first available radio button that contains an available time slot), and then click the continue button.
Actual Behavior
The program will run, find an available time slot, then simply move on to the next days, never attempting to select a radio button and move forward in the checkout process.
What am I doing wrong?
Any help would be appreciated.
it seems to me that you never set the dates_available to False inside your while loop:
while dates_available:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
free_spaces = selected_spot.text.replace('Select a time', '').strip()
spaces_free = True
else:
print(selected_spot.text.replace('Select a time', '').strip())
So you'll never exit the while loop. If you don't want to rewrite the whole logic, you could set dates_available = False right after you set spaces_free = True. That would allow exiting the while loop, but you might need a break or two to exit the for loops too.
If you want a failsafe behavior, you should refactor your code for smaller functions and if you want only the first available something, you could just return from the function with the first available data.
Something like this maybe?
def find_available(args):
def get_a_date():
while True:
time.sleep(1.5)
spots = driver.find_elements_by_css_selector('.ss-carousel-item')
for spot_index, spot in zip(range(date_range), spots):
if spot.value_of_css_property('display') != 'none':
spot.click()
available_dates = driver.find_elements_by_css_selector('.Date-slot-container')
for available_date in available_dates:
if available_date.value_of_css_property('display') != 'none':
selected_spot = available_date.find_element_by_css_selector('#slot-container-UNATTENDED')
if 'No doorstep delivery' not in selected_spot.text:
return selected_spot.text.replace('Select a time', '').strip()
else:
print(selected_spot.text.replace('Select a time', '').strip())
free_spaces = get_a_date()
print('Slots Available!')
if args.checkout:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
driver.find_element_by_xpath("//*[contains(text(), 'Soonest available')]").click()
time.sleep(1.5)
driver.find_element_by_xpath("//input[#type='submit' and #value='Continue']").click()
print('Your order has been placed!')
else:
client.messages.create(to=to_mobilenumber,
from_=from_mobilenumber,
body=free_spaces)
print('Your order time will be held for the next hour. Check your date and confirm!')
I have a menu system in ncurses.
Choosing one of the options takes you to another menu. But how do I get back?
import curses
def Main():
x = 0
while x!= ord('2'):
x = screen.getch()
screen.clear();screen.border();
screen.addstr(1,1, "Please choose:")
screen.addstr(3,1, "1 - Another Menu")
screen.addstr(4,1, "2 - Exit")
if x==ord('1'):
y = 0
while y!= ord('2'):
y = screen.getch()
screen.clear();screen.border();
screen.addstr(1,1, "Please choose from new menu:")
screen.addstr(3,1, "1 - Do Something new")
screen.addstr(4,1, "2 - Previous Menu")
if y == ord('1'): doSomething()
#Here I exit the internal loop. I need to go back to the previous menu, but I don't know how.
##
##exit outside loop and close program
##
curses.endwin(); exit();
screen = curses.initscr()
Main()
Ideally I'd need to use the GOTO module to jump between lines of code, but the device I'm using does not come with that module built-in.
Do you guys know any other methods? Really appreciate any help.
============ Update: ==================
Okay, I also realized that you can regenerate both menu's with ease:
import curses
def Main():
x = 0
while x!= ord('2'): #draws 1st menu
screen.clear();screen.border();
screen.addstr(1,1, "Please choose:")
screen.addstr(3,1, "1 - Another Menu")
screen.addstr(4,1, "2 - Exit")
x = screen.getch() #grab input AFTER first giving options :)
if x==ord('1'):
y = 0
z = 0
while y!= ord('2'): #draws 2nd menu
screen.clear();screen.border();
screen.addstr(1,1, "Please choose from new menu:")
screen.addstr(3,1, "1 - Do Something new")
screen.addstr(4,1, "2 - Previous Menu")
screen.addstr(6,1, "current loop : "+str(z))
y = screen.getch(); #grabs new input
while z!= -1: #never breaks from loop unless 'break' is called
if y == ord('1'):
z += 1
break #regenerates 2nd menu
break #regenerates 1st menu
#Here we exit the internal loop.
##
##exit outside loop and close program
curses.endwin(); exit();
screen = curses.initscr()
Main()
Add x = 0 after the second while loop ends.
(You need to reset x every time around the loop, not just the first. Otherwise exiting from the first menu will x set to "exit", so will also exit the second menu.)