mirror of https://github.com/sgoudham/Enso-Bot.git
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.
207 lines
6.8 KiB
Python
207 lines
6.8 KiB
Python
5 years ago
|
# --------------------------------------------------------------------------
|
||
|
# 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)
|