Reputation: 47
I need your help in understanding the mistake in my new menu based python program.
I have created a menu based python using the curses library. The program works fine with main menu options by accepting all the keys including special keys as input when getting user input via getch()
but with a sub-menu list it fails to accept the special key as input.
# Increment or Decrement
elif c == curses.KEY_DOWN: # down arrow
if self.option < len(self.submenu):
self.option += 1
else: self.option = 0
elif c == curses.KEY_UP: # up arrow
if self.option > 0:
self.option -= 1
else: self.option = len(self.submenu)
So above the is condition I am using to select the option but no input is sent to curses when I am using the UP/DOWN arrow keys
I am sharing my full code below
import curses, os, traceback, json
file1 = json.loads(open('/etc/ansible/org/json/tag_definition.json').read())
submenu = list(file1.keys())
cfg_dict = {'InstanceName': 'NONE',
'Environment': 'NONE',
'InstanceType':'t2.micro',
'SystemOwner':'1',
'LifeCycle':'NONE',
'DeptName':'NONE',
'Org':'NONE'}
class CursedMenu(object):
'''A class which abstracts the horrors of building a curses-based menu system'''
def __init__(self):
'''Initialization'''
self.screen = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
self.screen.keypad(1)
# Highlighted and Normal line definitions
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
self.highlighted = curses.color_pair(1)
self.normal = curses.A_NORMAL
def show(self, options, title="Title", subtitle="Subtitle"):
'''Draws a menu with the given parameters'''
self.set_options(options)
self.title = title.upper()
self.subtitle = subtitle.upper()
self.selected = 0
self.draw_menu()
def set_options(self, options):
'''Validates that the last option is "Exit"'''
if options[-1] is not 'Exit':
options.append('Exit')
self.options = options
def set_submenu(self, submenu):
'''Validates that the last option is "Exit"'''
if submenu[-1] is not 'Exit':
submenu.append('Exit')
self.submenu = submenu
def draw_dict(self):
self.screen.addstr(8, 35, " "*43, curses.A_BOLD)
self.screen.addstr(10, 35," "*43, curses.A_BOLD)
self.screen.addstr(12, 35," "*43, curses.A_BOLD)
self.screen.addstr(14, 35," "*43, curses.A_BOLD)
self.screen.addstr(16, 35," "*43, curses.A_BOLD)
self.screen.addstr(18, 35," "*43, curses.A_BOLD)
self.screen.addstr(20, 35," "*43, curses.A_BOLD)
self.screen.addstr(8, 35, cfg_dict['InstanceName'], curses.A_STANDOUT)
self.screen.addstr(10, 35,cfg_dict['Environment'], curses.A_STANDOUT)
self.screen.addstr(12, 35,cfg_dict['InstanceType'], curses.A_STANDOUT)
self.screen.addstr(14, 35,cfg_dict['SystemOwner'], curses.A_STANDOUT)
self.screen.addstr(16, 35,cfg_dict['LifeCycle'], curses.A_STANDOUT)
self.screen.addstr(18, 35,cfg_dict['DeptName'], curses.A_STANDOUT)
self.screen.addstr(20, 35,cfg_dict['Org'], curses.A_STANDOUT)
self.screen.refresh()
def draw_menu(self):
'''Actually draws the menu and handles branching'''
request = ""
try:
while request is not "Exit":
self.draw()
request = self.get_user_input()
self.handle_request(request)
self.__exit__()
# Also calls __exit__, but adds traceback after
except Exception as exception:
self.__exit__()
traceback.print_exc()
def draw(self):
'''Draw the menu and lines'''
#self.screen.border(0)
self.screen.subwin(40, 80, 0, 0).box()
self.screen.addstr(2,30, self.title, curses.A_STANDOUT|curses.A_BOLD) # Title for this menu
self.screen.hline(3, 1, curses.ACS_HLINE, 78)
self.screen.addstr(4,2, self.subtitle, curses.A_BOLD) #Subtitle for this menu
# Display all the menu items, showing the 'pos' item highlighted
left = 2
for index in range(len(self.options)):
menu_name = len(self.options[index])
textstyle = self.normal
if index == self.selected:
textstyle = self.highlighted
self.screen.addstr(5, left, "%d.%s" % (index+1, self.options[index]), textstyle)
left = left + menu_name + 3
self.screen.addstr(8, 4, " Instance Name:", curses.A_BOLD)
self.screen.addstr(10, 4," Environment:", curses.A_BOLD)
self.screen.addstr(12, 4," Instance Type:", curses.A_BOLD)
self.screen.addstr(14, 4," SystemOwner:", curses.A_BOLD)
self.screen.addstr(16, 4," LifeCycle:", curses.A_BOLD)
self.screen.addstr(18, 4," Department Name:", curses.A_BOLD)
self.screen.addstr(20, 4," Org:", curses.A_BOLD)
self.draw_dict()
self.screen.refresh()
def get_user_input(self):
'''Gets the user's input and acts appropriately'''
user_in = self.screen.getch() # Gets user input
'''Enter and Exit Keys are special cases'''
if user_in == 10:
return self.options[self.selected]
if user_in == 27:
return self.options[-1]
if user_in == (curses.KEY_END, ord('!')):
return self.options[-1]
# This is a number; check to see if we can set it
if user_in >= ord('1') and user_in <= ord(str(min(9,len(self.options)+1))):
self.selected = user_in - ord('0') - 1 # convert keypress back to a number, then subtract 1 to get index
return
# Increment or Decrement
if user_in == curses.KEY_LEFT: # left arrow
self.selected -=1
if user_in == curses.KEY_RIGHT: # right arrow
self.selected +=1
self.selected = self.selected % len(self.options)
return
def handle_request(self, request):
'''This is where you do things with the request'''
if request is "Org":
self.org_func()
if request is None: return
def org_func(self):
'''Actually draws the submenu and handles branching'''
c = None
self.option = 0
self.set_submenu(submenu)
height = len(self.submenu)
while c != 10:
self.s = curses.newwin(height+2, 20, 6, 14)
self.s.box()
for index in range(len(self.submenu)):
textstyle = self.normal
if index == self.option:
textstyle = self.highlighted
self.s.addstr(index+1,1, "%d-%s" % (index+1, self.submenu[index]), textstyle)
self.s.refresh()
c = self.s.getch() # Gets user input
if c == ord('k'):
d = self.submenu[self.option]
cfg_dict['Org'] = file1[d]['Org']
# This is a number; check to see if we can set it
if c >= ord('1') and c <= ord(str(len(self.submenu)+1)):
self.option = c - ord('0') - 1 # convert keypress back to a number, then subtract 1 to get index
# Increment or Decrement
elif c == curses.KEY_DOWN: # down arrow
if self.option < len(self.submenu):
self.option += 1
else: self.option = 0
elif c == curses.KEY_UP: # up arrow
if self.option > 0:
self.option -= 1
else: self.option = len(self.submenu)
return self.option
def __exit__(self):
curses.endwin()
os.system('clear')
'''demo'''
cm = CursedMenu()
cm.show(['Instance-ID','Org','Tag'], title='Instance Launch', subtitle='Options')
It works perfectly fine if I am using the alphabets and numerics as input but not with the special keys. I have used the code below and it was working
elif c == ord('k'): # down arrow
if self.option < len(self.submenu):
self.option += 1
else: self.option = 0
elif c == ord('i'): # up arrow
if self.option > 0:
self.option -= 1
else: self.option = len(self.submenu)
Please help understand why the code is not accepting the Special Keys.
Below is the input json I have used
{
"Dept1": {
"Instance Name": "instance1",
"Environment": "environment1",
"SystemOwner": "Owner1",
"LifeCycle": "Lcy1",
"DeptName": "Check1",
"Org": "CSO"
}
}
Upvotes: 1
Views: 2237
Reputation: 54465
When you did
self.s = curses.newwin(height+2, 20, 6, 14)
you omitted setting properties for the new window, e.g,
self.s.keypad(1)
Upvotes: 3