You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Enso-Bot/venv/Lib/site-packages/menus/menu.py

207 lines
6.8 KiB
Python

# --------------------------------------------------------------------------
# Copyright (c) 2016 Digital Sapphire
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the
# following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
# ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
# TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
# --------------------------------------------------------------------------
from __future__ import print_function
import logging
import os
import time
import warnings
from dsdev_utils.terminal import (ask_yes_no,
get_correct_answer,
get_terminal_size)
import six
from menus.exceptions import MenusError
from menus.utils import clear_screen_cmd, Getch
log = logging.getLogger(__name__)
class BaseMenu(object):
def __init__(self, **kwargs):
# Used to display the apps name on all menu headers
self.app_name = None
# The custom menu name
self.menu_name = kwargs.get('menu_name')
# If we do not have a custom menu
# name then use the class name
if self.menu_name is None:
self.menu_name = self.__class__.__name__
# A message to display when this menus loads
self.message = kwargs.get('message')
# User commands for this menu
self._commands = kwargs.get('commands', [])
# ToDo: Remove in v1.0
# User commands for this menu
options = kwargs.get('options', [])
if len(options) > 0:
if len(self._commands) > 0:
raise MenusError('Cannot mix old & new API. Use '
'commands kwarg only.')
warn_msg = ('BaseMenu(options=[]) is deprecated, '
'user BaseMenu(commands=[]')
warnings.warn(warn_msg)
self._commands += options
# End ToDo
@property
def commands(self):
return self._commands
def __call__(self):
x = self.display()
x()
def display(self):
self._display_menu_header()
self.display_msg(self.message)
return self._menu_options(self._commands)
def pause(self, seconds=5, enter_to_continue=False):
if not isinstance(enter_to_continue, bool):
raise MenusError('enter_to_continue must be boolean',
expected=True)
if not isinstance(seconds, six.integer_types):
raise MenusError('seconds must be integer', expected=True)
if enter_to_continue is True:
self.display_msg('Press enter to quit')
six.moves.input()
else:
time.sleep(seconds)
return True
# Prompt the user with a question & only accept
# yes or no answers
def ask_yes_no(self, question, default):
return ask_yes_no(question, default)
# Promot the user with a question & confirm it's correct.
# If required is True, user won't be able to enter a blank answer
def get_correct_answer(self, question, default, required=False):
return get_correct_answer(question, default, required)
# Display a message centered on the screen.
def display_msg(self, message=None):
self._display_msg(message)
def done(self):
pass
# ToDo: Remove in v1.0
def get_correct_action(self, question, default, required):
return get_correct_answer(question, default, required)
# End ToDo
# Takes a string and adds it to the menu header along side
# the app name.
def _display_menu_header(self):
window_size = get_terminal_size()[0]
# Adding some styling to the header
def add_style():
top = '*' * window_size + '\n'
bottom = '\n' + '*' * window_size + '\n'
header = self.app_name + ' - ' + self.menu_name
header = header.center(window_size)
msg = top + header + bottom
return msg
os.system(clear_screen_cmd)
print(add_style())
def _display_msg(self, message):
window_size = get_terminal_size()[0]
if message is None:
return ''
if not isinstance(message, six.string_types):
log.warning('Did not pass str')
return ''
# Home grown word wrap
def format_msg():
formatted = []
finished = ['\n']
count = 0
words = message.split(' ')
for w in words:
w = w + ' '
if count + len(w) > window_size / 2:
finished.append(''.join(formatted).center(window_size))
finished.append('\n')
count = len(w)
# Starting a new line.
formatted = []
formatted.append(w)
else:
formatted.append(w)
count += len(w)
finished.append(''.join(formatted).center(window_size))
finished.append('\n')
return ''.join(finished)
print(format_msg())
# Takes a list of tuples(menu_name, call_back) adds menu numbers
# then prints menu to screen.
# Gets input from user, then returns the callback
def _menu_options(self, commands=None):
def add_options():
getch = Getch()
menu = []
count = 1
for s in commands:
item = '{}. {}\n'.format(count, s[0])
menu.append(item)
count += 1
print(''.join(menu))
answers = []
for a in six.moves.range(1, len(menu) + 1):
answers.append(str(a))
while 1:
ans = getch()
if ans in answers:
break
else:
log.debug('Not an acceptable answer!')
return commands[int(ans) - 1][1]
return add_options()
# This is used as the initial Menu
class MainMenu(BaseMenu):
def __init__(self, commands):
super(MainMenu, self).__init__(commands=commands)