python python-2.7 curses

¿Cómo crear un menú y submenús en curses Python?



python-2.7 (1)

AFAIK, no hay una extensión del menú de curses disponible en Python, por lo que tiene que lanzar su propia solución. Sé sobre este parche http://bugs.python.org/issue1723038 pero no sé cuál es el estado actual de este parche. Encontré una buena clase para Python que envuelve lo que quiero que se llame ''cmenu'' aquí http://www.promisc.org/blog/?p=33 pero también tengo un problema con eso. Quiero hacer un menú donde el usuario pueda elegir un elemento resaltado, pero en lugar de ejecutar una acción particular de inmediato, quiero mostrar otro menú, y luego tal vez otro, pedir alguna entrada, etc. Mi primer pensamiento fue eliminar el cmenu existente con screen.clear () o cleanup () pero el menú anterior no se elimina antes de que se dibuje el nuevo y el nuevo menú se parece a esto:

0. top 1. Exit 2. Another menu -- end of the old menu that should go away -- 3. first 4. second 5. third

No hay un método remove () para eliminar un elemento en cmenu (). Supongo que el hecho de que el menú anterior no se haya borrado está causado por el método ''while True'' loop in display (), pero cuando lo eliminé, algo extraño estaba sucediendo. Estoy usando Python 2.7, este es mi código actual:

#!/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()


Realmente recomiendo mirar en el uso de panels . En cualquier momento en que tengas widgets que posiblemente puedan superponerse, esto hace que la vida sea mucho más fácil. Este es un ejemplo simple que debería comenzar. (Parece que ninguno de los curses.beep () o curses.flash () funciona en mi terminal, pero eso no viene al caso)

#!/usr/bin/env python2 import curses from curses import panel class 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)

Algunas cosas a tener en cuenta al revisar su código.

Usar curses.wrapper (invocable) para iniciar su aplicación es más limpio que hacer su propio intento / excepto con la limpieza.

Su clase llama a initscr dos veces, lo que probablemente generará dos pantallas (no se ha probado si devuelve la misma pantalla si está configurada), y luego, cuando tiene varios menús, no hay un manejo adecuado de (lo que debería ser) diferentes ventanas / pantallas. Creo que es más claro y mejor llevar la contabilidad al menú, usar la pantalla y dejar que el menú haga una subventana para mostrar como en mi ejemplo.

Nombrar una lista "lista" no es una gran idea.

Si desea iniciar otra aplicación de terminal como ''top'', probablemente es mejor dejar que Python salga de forma limpia primero y luego iniciar para prevenir cualquier problema con la configuración de la terminal.