#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2013
    Posts
    1
    Rep Power
    0

    How to create a menu and submenus in Python curses?


    AFAIK, there is no curses menu extension available in Python yet so you have to roll your own solution. I know about this patch http://bugs.python.org/issue1723038 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 http://www.promisc.org/blog/?p=33 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:

    Code:
        0. top
        1. Exit
        2. Another menu
    -- end of the old menu that should go away --
        3. first
        4. second
        5. third
    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:

    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 <= 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 >= 0 and self.pos < 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()
  2. #2
  3. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,889
    Rep Power
    481
    (I investigated the other day and discovered nothing that you don't already know. Look for a library that wraps ncurses, or write it yourself.)

    Would you prefer no answer to this fairly useless answer?
    [code]Code tags[/code] are essential for python code and Makefiles!

IMN logo majestic logo threadwatch logo seochat tools logo