Mini Shell
#!/opt/imh-python/bin/python3
# hi, this routine isn't licensed. but credit would be nice somewhere.
# See http://pp19dd.com/2013/11/simple-python-list-picker-using-curses/
# usage:
# opts = Picker(
# title = 'Delete all files',
# options = ["Yes", "No"]
# ).get_selected()
# returns a simple list
# cancel returns False
import curses
class Picker:
"""Allows you to select from a list with curses"""
stdscr = None
win = None
title = ""
arrow = ""
footer = ""
more = ""
c_selected = ""
c_empty = ""
cursor = 0
offset = 0
selected = 0
selcount = 0
aborted = False
window_height = 15
window_width = 60
all_options = []
length = 0
def curses_start(self):
self.stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
self.win = curses.newwin(
5 + self.window_height, self.window_width, 2, 4
)
def curses_stop(self):
curses.nocbreak()
self.stdscr.keypad(0)
curses.echo()
curses.endwin()
def get_selected(self):
if self.aborted:
return False
ret_s = [x for x in self.all_options if x["selected"]]
ret = [x["label"] for x in ret_s]
return ret
def redraw(self):
self.win.clear()
self.win.border(
self.border[0],
self.border[1],
self.border[2],
self.border[3],
self.border[4],
self.border[5],
self.border[6],
self.border[7],
)
self.win.addstr(self.window_height + 4, 5, " " + self.footer + " ")
position = 0
option_range = self.all_options[
self.offset : self.offset + self.window_height + 1
]
for option in option_range:
if option["selected"]:
line_label = self.c_selected + " "
else:
line_label = self.c_empty + " "
self.win.addstr(position + 2, 5, line_label + option["label"])
position = position + 1
# hint for more content above
if self.offset > 0:
self.win.addstr(1, 5, self.more)
# hint for more content below
if self.offset + self.window_height <= self.length - 2:
self.win.addstr(self.window_height + 3, 5, self.more)
self.win.addstr(0, 5, " " + self.title + " ")
self.win.addstr(
0,
self.window_width - 8,
" " + str(self.selcount) + "/" + str(self.length) + " ",
)
self.win.addstr(self.cursor + 2, 1, self.arrow)
self.win.refresh()
def check_cursor_up(self):
if self.cursor < 0:
self.cursor = 0
if self.offset > 0:
self.offset = self.offset - 1
def check_cursor_down(self):
if self.cursor >= self.length:
self.cursor = self.cursor - 1
if self.cursor > self.window_height:
self.cursor = self.window_height
self.offset = self.offset + 1
if self.offset + self.cursor >= self.length:
self.offset = self.offset - 1
def curses_loop(self, stdscr):
while 1:
self.redraw()
char = stdscr.getch()
if char == ord('q') or char == ord('Q'):
self.aborted = True
break
if char == curses.KEY_UP:
self.cursor = self.cursor - 1
elif char == curses.KEY_DOWN:
self.cursor = self.cursor + 1
# elif c == curses.KEY_PPAGE:
# elif c == curses.KEY_NPAGE:
elif char == ord(' '):
self.all_options[self.selected][
"selected"
] = not self.all_options[self.selected]["selected"]
elif char == 10:
break
# deal with interaction limits
self.check_cursor_up()
self.check_cursor_down()
# compute selected position only after dealing with limits
self.selected = self.cursor + self.offset
temp = self.get_selected()
self.selcount = len(temp)
def __init__(
self,
options,
title='Select',
arrow="-->",
footer="Space = toggle, Enter = accept, q = cancel",
more="...",
border="||--++++",
c_selected="[X]",
c_empty="[ ]",
):
self.title = title
self.arrow = arrow
self.footer = footer
self.more = more
self.border = border
self.c_selected = c_selected
self.c_empty = c_empty
self.all_options = []
for option in options:
self.all_options.append({"label": option, "selected": False})
self.length = len(self.all_options)
self.curses_start()
curses.wrapper(self.curses_loop)
self.curses_stop()
Zerion Mini Shell 1.0