title: How to create a menu and submenus in Python curses?
body: <p>AFAIK, there is no curses menu extension available in Python yet so you have to roll your own solution. I know about this patch <a href="http://bugs.python.org/issue1723038">http://bugs.python.org/issue1723038</a> but I don't what's the current state of it. I found a nice class for Python that wraps what I want called 'cmenu' here <a href="http://www.promisc.org/blog/?p=33">http://www.promisc.org/blog/?p=33</a> but I have a problem with that too. I want to make a menu where user can choose a highlighted element but instead of executing a particular action right away I want to display another menu, and then maybe another, ask for some input etc. My first thought was to remove the existing cmenu with screen.clear() or cleanup() but the old menu is not removed before the new one is drawn and the new menu looks like this:</p>
  
  <pre><code>    0. top
      1. Exit
      2. Another menu
  -- end of the old menu that should go away --
      3. first
      4. second
      5. third
  </code></pre>
  
  <p>There is no remove() method for removing an item in cmenu(). I guess the fact that the old menu is not cleared is caused by 'while True' loop in display() method but when I removed it some weird stuff was going on. I am using Python 2.7, this is my current code:</p>
  
  <pre><code>#!/usr/bin/python
  #
  # Adapted from:
  # http://blog.skeltonnetworks.com/2010/03/python-curses-custom-menu/
  #
  # Goncalo Gomes
  # http://promisc.org
  #
  
  import signal
  signal.signal(signal.SIGINT, signal.SIG_IGN)
  
  import os
  import sys
  import curses
  import traceback
  import atexit
  import time
  
  import sys
  reload(sys)
  sys.setdefaultencoding("utf-8")
  
  class cmenu(object):
      datum = {}
      ordered = []
      pos = 0
  
      def __init__(self, options, title="python curses menu"):
          curses.initscr()
          curses.start_color()
          curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
          curses.curs_set(0)
          self.screen = curses.initscr()
          self.screen.keypad(1)
  
          self.h = curses.color_pair(1)
          self.n = curses.A_NORMAL
  
          for item in options:
              k, v = item.items()[0]
              self.datum[k] = v
              self.ordered.append(k)
  
          self.title = title
  
          atexit.register(self.cleanup)
  
      def cleanup(self):
          curses.doupdate()
          curses.endwin()
  
      def upKey(self):
          if self.pos == (len(self.ordered) - 1):
              self.pos = 0
          else:
              self.pos += 1
  
      def downKey(self):
          if self.pos &lt;= 0:
              self.pos = len(self.ordered) - 1
          else:
              self.pos -= 1
  
      def display(self):
          screen = self.screen
  
          while True:
              screen.clear()
              screen.addstr(2, 2, self.title, curses.A_STANDOUT|curses.A_BOLD)
              screen.addstr(4, 2, "Please select an interface...", curses.A_BOLD)
  
              ckey = None
              func = None
  
              while ckey != ord('\n'):
                  for n in range(0, len(self.ordered)):
                      optn = self.ordered[n]
  
                      if n != self.pos:
                          screen.addstr(5 + n, 4, "%d. %s" % (n, optn), self.n)
                      else:
                          screen.addstr(5 + n, 4, "%d. %s" % (n, optn), self.h)
                  screen.refresh()
  
                  ckey = screen.getch()
  
                  if ckey == 258:
                      self.upKey()
  
                  if ckey == 259:
                      self.downKey()
  
              ckey = 0
              self.cleanup()
              if self.pos &gt;= 0 and self.pos &lt; len(self.ordered):
                  self.datum[self.ordered[self.pos]]()
                  self.pos = -1
              else:
                  curses.flash()
  
  
  
  def top():
      os.system("top")
  
  def exit():
      sys.exit(1)
  
  def submenu():
      # c.screen.clear()     # nope
      # c.cleanup()          # nope
      submenu_list = [{"first": exit}, {"second": exit}, {"third": exit}]
      submenu = cmenu(submenu_list)
      submenu.display()
  
  try:
  
      list = [{ "top": top }, {"Exit": exit}, {"Another menu": submenu}]
  
      c = cmenu(list)
  
      c.display()
  
  except SystemExit:
      pass
  else:
      #log(traceback.format_exc())
      c.cleanup()
  </code></pre>