Как создать меню и подменю в Python curses?
AFAIK, в Python нет расширения меню проклятий, поэтому вам нужно свернуть собственное решение. Я знаю об этом патче http://bugs.python.org/issue1723038 но я не знаю, каково его текущее состояние. Я нашел хороший класс для Python, который обертывает то, что я хочу назвать "cmenu" здесь http://www.promisc.org/blog/?p=33 но у меня с этим тоже проблема. Я хочу сделать меню, в котором пользователь может выбрать выделенный элемент, но вместо выполнения конкретное действие сразу же я хочу отобразить другое меню, а затем, возможно, другое, попросить какой-то ввод и т. д. Моей первой мыслью было удалить существующий cmenu с экраном.clear () или cleanup (), но старое меню не удаляется до того, как новое будет нарисовано, и новое меню выглядит следующим образом:
0. top
1. Exit
2. Another menu
-- end of the old menu that should go away --
3. first
4. second
5. third
метод remove() для удаления элемента в cmenu () отсутствует. Я предполагаю, что тот факт, что старое меню не очищено, вызван циклом "while True" в методе display (), но когда я его удалил происходило что-то странное. Я использую Python 2.7, это мой текущий код:
#!/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()
1 ответов
Я действительно рекомендую вам изучить использование панели. В любое время у вас будут виджеты, которые могут перекрываться, это делает жизнь намного проще. Это простой пример, который должен помочь вам начать. (Не ругается.сигнал() или проклятий.flash (), похоже, работает на моем терминале, но это не относится к делу)
#!/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)
некоторые вещи, которые следует отметить при просмотре кода.
С помощью проклятий.обертка (вызываемая) для запуска вашего приложения чище, чем делать свои собственные попробуйте/кроме очистки.
ваш класс вызывает initscr дважды, который, вероятно, будет генерировать два экрана (havent протестирован, если он возвращает тот же экран, если его настройка), а затем, когда у вас есть несколько меню, нет надлежащей обработки (что должно быть) разных окон/экранов. Я думаю, что его более четкая и лучшая бухгалтерия, чтобы передать меню Экран для использования и позволить меню сделать подокно для отображения, как в моем примере.
назвав список "список" не многие идея.
Если вы хотите запустить другое терминальное приложение, такое как "top", вероятно, лучше сначала позволить python выйти из проклятий, а затем запустить, чтобы предотвратить любое futzing с настройками терминала.