#!/usr/bin/env python3 # File: logout-manager-tui.py # License: MIT # Author: adamlamers, bgstack15 # Startdate: 2020-03-09 17:06 # Title: ncurses based logout manager # Usage: # # Reference: # https://docs.python.org/3/howto/curses.html # ripped straight from http://adamlamers.com/post/FTPD9KNRA8CT # https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string/12025554#12025554 # Improve: # add "disabled" option in menu, with default=enabled # Documentation: import curses, sys, os sys.path.append("/home/bgirton/dev/logout-manager") import lmlib class CursesMenu(object): INIT = {'type' : 'init'} def __init__(self, menu_options): self.screen = curses.initscr() self.menu_options = menu_options self.selected_option = 0 self._previously_selected_option = None self.running = True #init curses and curses input curses.noecho() curses.cbreak() curses.start_color() curses.curs_set(0) #Hide cursor self.screen.keypad(1) #set up color pair for highlighted option curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) self.hilite_color = curses.color_pair(1) self.normal_color = curses.A_NORMAL def prompt_selection(self, parent=None): if parent is None: lastoption = "Cancel" else: lastoption = "Return to previous menu ({})".format(parent['title']) option_count = len(self.menu_options['options']) input_key = None ENTER_KEY = ord('\n') #NUM_KEYS = [ord('1')] #NUM_KEYS = [ord(1),ord(2)...] NUM_KEYS = [ord(str(i)) for i in range(1,option_count+2)] done = False while not done: if self.selected_option != self._previously_selected_option: self._previously_selected_option = self.selected_option self.screen.border(0) self._draw_title() for option in range(option_count): if self.selected_option == option: self._draw_option(option, self.hilite_color) else: self._draw_option(option, self.normal_color) if self.selected_option == option_count: self.screen.addstr(5 + option_count, 4, "{:2} - {}".format(option_count+1, lastoption), self.hilite_color) else: self.screen.addstr(5 + option_count, 4, "{:2} - {}".format(option_count+1, lastoption), self.normal_color) max_y, max_x = self.screen.getmaxyx() if input_key is not None: self.screen.addstr(max_y-3, max_x - 5, "{:3}".format(self.selected_option)) self.screen.refresh() input_key = self.screen.getch() down_keys = [curses.KEY_DOWN, ord('j')] up_keys = [curses.KEY_UP, ord('k')] exit_keys = [ord('q')] if input_key in down_keys: if self.selected_option < option_count: self.selected_option += 1 else: self.selected_option = 0 if input_key in up_keys: if self.selected_option > 0: self.selected_option -= 1 else: self.selected_option = option_count if input_key in exit_keys: self.selected_option = option_count #auto select exit and return break if input_key == ENTER_KEY or input_key in NUM_KEYS: if input_key in NUM_KEYS: self.selected_option=int(chr(input_key))-1 done = True return self.selected_option def _draw_option(self, option_number, style): self.screen.addstr(5 + option_number, 4, "{:2} - {}".format(option_number+1, self.menu_options['options'][option_number]['title']), style) def _draw_title(self): self.screen.addstr(2, 2, self.menu_options['title'], curses.A_STANDOUT) self.screen.addstr(4, 2, self.menu_options['subtitle'], curses.A_BOLD) def display(self): selected_option = self.prompt_selection() i, _ = self.screen.getmaxyx() curses.endwin() #os.system('clear') if selected_option < len(self.menu_options['options']): selected_opt = self.menu_options['options'][selected_option] return selected_opt else: self.running = False return {'title' : 'Cancel', 'type' : 'exitmenu'} menu = {'title' : 'Logout Manager', 'type' : 'menu', 'subtitle' : 'Use arrows or number keys'} option_1 = {'title' : 'Hello World', 'type' : 'command', 'command' : 'echo Hello World!'} # build menu object from config. config = lmlib.Initialize_config() actions = lmlib.Actions menu['options'] = [ {'title': 'Lock', 'type': 'action', 'action': 'lock'}, {'title': 'Logout', 'type': 'action', 'action': 'logout'}, {'title': 'Hibernate', 'type': 'action', 'action': 'hibernate'}, {'title': 'Shutdown', 'type': 'action', 'action': 'shutdown'}, {'title': 'Reboot', 'type': 'action', 'action': 'reboot'} ] m = CursesMenu(menu) selected_action = m.display() if selected_action['type'] == 'exitmenu': print("Cancelled") elif selected_action['type'] == 'command': os.system(selected_action['command']) elif selected_action['type'] == 'action': #a = selected_action['action']: func = getattr(globals()['actions'],selected_action['action']) func(config)