How to create a menu and submenus in Python curses? How to create a menu and submenus in Python curses? python python

How to create a menu and submenus in Python curses?


I really recommend you look into using panels. Anytime you will have widgets that could possibly overlap, it makes life alot easier. This is a simple example that should get you started. (Neither curses.beep() or curses.flash() seem to work on my terminal, but that is beside the point)

#!/usr/bin/env pythonimport cursesfrom curses import panelclass Menu(object):    def __init__(self, items, stdscreen):        self.window = stdscreen.subwin(0, 0)        self.window.keypad(1)        self.panel = panel.new_panel(self.window)        self.panel.hide()        panel.update_panels()        self.position = 0        self.items = items        self.items.append(("exit", "exit"))    def navigate(self, n):        self.position += n        if self.position < 0:            self.position = 0        elif self.position >= len(self.items):            self.position = len(self.items) - 1    def display(self):        self.panel.top()        self.panel.show()        self.window.clear()        while True:            self.window.refresh()            curses.doupdate()            for index, item in enumerate(self.items):                if index == self.position:                    mode = curses.A_REVERSE                else:                    mode = curses.A_NORMAL                msg = "%d. %s" % (index, item[0])                self.window.addstr(1 + index, 1, msg, mode)            key = self.window.getch()            if key in [curses.KEY_ENTER, ord("\n")]:                if self.position == len(self.items) - 1:                    break                else:                    self.items[self.position][1]()            elif key == curses.KEY_UP:                self.navigate(-1)            elif key == curses.KEY_DOWN:                self.navigate(1)        self.window.clear()        self.panel.hide()        panel.update_panels()        curses.doupdate()class MyApp(object):    def __init__(self, stdscreen):        self.screen = stdscreen        curses.curs_set(0)        submenu_items = [("beep", curses.beep), ("flash", curses.flash)]        submenu = Menu(submenu_items, self.screen)        main_menu_items = [            ("beep", curses.beep),            ("flash", curses.flash),            ("submenu", submenu.display),        ]        main_menu = Menu(main_menu_items, self.screen)        main_menu.display()if __name__ == "__main__":    curses.wrapper(MyApp)

Some things to note when looking over your code.

Using curses.wrapper(callable) to launch your application is cleaner than doing your own try/except with cleanup.

Your class calls initscr twice which will probably generate two screens (havent tested if it returns the same screen if its setup), and then when you have multiple menus there is no proper handling of (what should be) different windows/screens. I think its clearer and better bookkeeping to pass the menu the screen to use and let the menu make a subwindow to display in as in my example.

Naming a list 'list' isn't a great idea, because it shadows the list() function.

If you want to launch another terminal app like 'top', it is probably better to let python exit curses cleanly first then launch in order to prevent any futzing with terminal settings.