Continuing on the same project as the previous post, I came to wanting to make a custom menu with curses in Python. Realizing that there are functions to create menus in curses already, I wanted to build this fro m the bottom up. The concept was to produce a menu that would highlight the selection change on the arrow keys or direct input, and then on a press of the Enter key the menu would return that selection.
Now, while writing this, you’ll see in my code that I took probable the least efficient way of building this menu, but it helps in making itself explanatory for the person learning curses. As for the context of this coding, I built it as a function in my XFDL viewer, so there are 5 options in the menu. I used win.keypad(0) to enable the use of the arrow keys but for some reason, the curses.KEY_UP was not being detected so the arrow key up and arrow key down are 259 and 258, respectively. This does work though, I also have the menu catch numbers 1-5 and set the highlighted line accordingly.
pos = 1
x = None
# I'm going to be lazy and save some typing here.
h = curses.color_pair(1)
n = curses.A_NORMAL
while x != ord('\n'):
# Gotta reset the screen from the root or lose the border, window, etc.
screen.addstr(2,2, "XFDL VIEWER", curses.A_STANDOUT)
screen.addstr(4,2, "Please select an option...", curses.A_BOLD)
# Detect what is highlighted by the 'pos' variable.
if pos == 1:
screen.addstr(5,4, "1 - XFDL -> XML",h)
screen.addstr(5,4, "1 - XFDL -> XML",n)
if pos == 2:
screen.addstr(6,4, "2 - XML -> XFDL",h)
screen.addstr(6,4, "2 - XML -> XFDL",n)
if pos == 3:
screen.addstr(7,4, "3 - Show XML",h)
screen.addstr(7,4, "3 - Show XML",n)
if pos == 4:
screen.addstr(8,4, "4 - Exit",h)
screen.addstr(8,4, "4 - Exit",n)
if pos == 5:
screen.addstr(9,4, "5 - DEBUG", h)
screen.addstr(9,4, "5 - DEBUG", n)
x = screen.getch()
# Is 'x' 1-5 or arrow up, arrow down?
if x == ord('1'):
pos = 1
elif x == ord('2'):
pos = 2
elif x == ord('3'):
pos = 3
elif x == ord('4'):
pos = 4
elif x == ord('5'):
pos = 5
# It was a pain in the ass trying to get the arrows working.
elif x == 258:
if pos < 5:
pos += 1
pos = 1
# Since the curses.KEY_* did not work, I used the raw return value.
elif x == 259:
if pos > 1:
pos += -1
pos = 5
elif x != ord('\n'):
# show_error() is my custom function for displaying a message:
# show_error(str:message, int:line#, int:seconds_to_display)
I’ve highlighted the lines pertaining to my work around for the key pad. This function will return the menu option and then that is processed for a reaction. Reminder: the ‘screen’ object for my curses window is a global variable. I’m quite thrilled at the simplicity of this and the curses library, although I am disappointed in the lack of tutorials on the web deeper than typical ‘Hello World’ tutorials, but I hope these posts go to help others exploring this library!
Here’s a nice picture of the library in action:
Be sure to comment on these tutorials and let me know if there is more detail needed or if they are helpful!!