Source code for birdears.interfaces.commandline

import os
import sys

from .. import _Getch

from .. import KEYS
from .. import CHROMATIC_SHARP
from .. import CHROMATIC_FLAT

from .. import INTERVALS
from .. import DIATONIC_MODES
from .. import CHROMATIC_TYPE

from ..questionbase import QUESTION_CLASSES

# from os import popen

# TODO: find which version this function was implemented to set minimum python
#       version.
from shutil import get_terminal_size

#  try:
#      from shutil import get_terminal_size
#  except ImportError:
#      from click import get_terminal_size

COLS, LINES = get_terminal_size()


[docs]def center_text(text, sep=True, nl=0): """This function returns input text centered according to terminal columns. Args: text (str): The string to be centered, it can have multiple lines. sep (bool): Add line separator after centered text (True) or not (False). nl (int): How many new lines to add after text. """ linelist = list(text.splitlines()) # gets the biggest line biggest_line_size = 0 for line in linelist: line_length = len(line.expandtabs()) if line_length > biggest_line_size: biggest_line_size = line_length columns = COLS offset = biggest_line_size / 2 perfect_center = columns / 2 padsize = int(perfect_center - offset) spacing = ' ' * padsize # space char dim = '\033[2m' reset = '\033[0m' text = str() for line in linelist: text += (spacing + line + '\n') divider = \ spacing + (dim + '─' * int(biggest_line_size) + reset) # unicode 0x2500 text += divider if sep else '' text += nl * '\n' return text
[docs]def make_input_str(user_input, keyboard_index): """Makes a string representing intervals entered by the user. This function is to be used by questions which takes more than one interval input as MelodicDictation, and formats the intervals already entered. Args: user_input (array_type): The list of keyboard keys entered by user. keyboard_index (array_type): The keyboard mapping used by question. """ input_str = str() user_input_semitones = [keyboard_index.index(s) for s in user_input] user_str = "".join([INTERVALS[s][1].center(5) for s in user_input_semitones]).center(COLS) input_str = ("\r{}".format(user_str)) return input_str
[docs]class CommandLine: def __init__(self, cli_prompt_next=False, cli_no_scroll=False, cli_no_resolution=False, exercise=None, *args, **kwargs): """This function implements the birdears loop for command line. Args: cli_prompt_next (bool): True if --prompt is set. cli_no_scroll (bool): True if --no-scroll is set. cli_no_resolution (bool): True if --no-resolution is set. exercise (str): The question name. **kwargs (kwargs): FIXME: The kwargs can contain options for specific questions. """ if exercise in QUESTION_CLASSES: QUESTION_CLASS = QUESTION_CLASSES[exercise] else: raise Exception("Invalid `exercise` value:", exercise) self.prompt_next = cli_prompt_next self.no_scroll = cli_no_scroll self.no_resolution = cli_no_resolution self.exercise = exercise ####if 'n_notes' in kwargs: ####self.dictate_notes = kwargs['n_notes'] ####else: ####self.dictate_notes = 1 getch = _Getch() self.new_question_bit = True print('\n') while True: if self.new_question_bit is True: self.new_question_bit = False self.input_keys = list() self.question = QUESTION_CLASS(**kwargs) if self.exercise == 'melodic': exercise_title = 'Melodic interval recognition' question_prompt = 'What is the interval?' elif self.exercise == 'harmonic': exercise_title = 'Harmonic interval recognition' question_prompt = 'What is the interval?' elif self.exercise == 'dictation': exercise_title = 'Melodic dictation' question_prompt = 'Now, please type the intervals ' \ 'you\'ve heard.' elif self.exercise == 'instrumental': exercise_title = 'Instrumental melodic ' \ 'time-based detection' # TODO: question_prompt else: # 'notename': exercise_title = 'Note name by interval recognition' question_prompt = 'The tonic is {tonic}. ' \ 'Press the key representing the ' \ 'second note.' \ .format(tonic=self.question.tonic_str) if self.no_scroll: # Clear terminal screen (but keep scrollback) # See https://stackoverflow.com/a/2084628 os.system('cls' if os.name == 'nt' else 'clear -x') print('\n') print(center_text('birdears ─ Functional Ear Training', sep=False, nl=1)) print(center_text(exercise_title, nl=0)) print(center_text('KEY: ' + self.question.tonic_str + ' ' \ + self.question.mode, sep=False, nl=1)) print_question(self.question) if not self.exercise == 'instrumental': self.question.play_question() print(center_text(question_prompt)) print(center_text( 'key- answer r- repeat q- quit', sep=False, nl=1)) if self.exercise == 'instrumental': for r in range(self.question.n_repeats): self.question.play_question() # FIXME: Instrumental is broken in CLI, double countdown... for i in range(self.question.wait_time): time_left = str(self.question.wait_time - i).rjust(3) text = '{} seconds remaining...'.format(time_left) print(center_text(text, sep=False), end='') self.question.question._wait(1) response = self.question.check_question() print_instrumental(response) self.new_question_bit = True continue user_input = getch() self.process_key(user_input)
[docs] def process_key(self, user_input): if user_input in self.question.keyboard_index \ and user_input != ' ': # spc self.input_keys.append(user_input) ###if self.exercise == 'dictation': ###input_str = make_input_str(self.input_keys, ### self.question.keyboard_index) ###print(input_str, end='') if self.question.n_input_notes > 1: input_str = make_input_str(self.input_keys, self.question.keyboard_index) print(input_str, end='') # FIXME: use self.question.n_notes instead #if len(self.input_keys) == self.dictate_notes: if len(self.input_keys) == self.question.n_notes: response = self.question.check_question(self.input_keys) print_response(response) if not self.no_resolution: self.question.play_resolution() if self.prompt_next: print(center_text('Next question', nl=0)) print(center_text('any- play q- quit', sep=False, nl=1)) getch2 = _Getch() while True: # wait for input before next question user_input2 = getch2() # q - quit if user_input2 in ('q', 'Q'): sys.exit() # any key - play next question elif user_input2: break # loop, keep waiting else: pass self.new_question_bit = True # backspace elif user_input == '\x7f': # FIXME: use self.question.n_input_notes instead #if(len(self.input_keys) > 0) and self.exercise == 'dictation': if(len(self.input_keys) > 0) and (self.question.n_input_notes > 1): del(self.input_keys[-1]) input_str = make_input_str(self.input_keys, self.question.keyboard_index) print(input_str, end='') # q - quit elif user_input in ('q', 'Q'): sys.exit() # r - repeat interval elif user_input in ('r', 'R'): self.question.play_question()