concertina_helper.layouts.layout_loader
1from pathlib import Path 2from collections.abc import Iterable 3 4import re 5 6from yaml import safe_load 7 8from ..type_defs import Pitch, PitchMatrix 9from .bisonoric import BisonoricLayout 10from .unisonoric import UnisonoricLayout 11 12 13def _names_to_pitches(matrix: Iterable[Iterable[str]]) -> PitchMatrix: 14 ''' 15 >>> pitch_matrix = _names_to_pitches([['C4']]) 16 >>> print(pitch_matrix[0][0]) 17 C4 18 ''' 19 return PitchMatrix( 20 tuple( 21 tuple( 22 Pitch(name) for name in row 23 ) for row in matrix 24 ) 25 ) 26 27 28def _parse_matrix(rows: Iterable[str]) -> PitchMatrix: 29 ''' 30 >>> pitch_matrix = _parse_matrix(['C4 E4 G4']) 31 >>> print(pitch_matrix[0][0]) 32 C4 33 ''' 34 return _names_to_pitches([re.split(r'\s+', row.strip()) for row in rows]) 35 36 37def parse_unisonoric_layout(layout_spec: dict) -> UnisonoricLayout: 38 left_matrix = _parse_matrix(layout_spec['left']) 39 right_matrix = _parse_matrix(layout_spec['right']) 40 return UnisonoricLayout(left_matrix, right_matrix) 41 42 43def parse_bisonoric_layout(layout_spec: dict) -> BisonoricLayout: 44 push_layout = parse_unisonoric_layout(layout_spec['push']) 45 pull_layout = parse_unisonoric_layout(layout_spec['pull']) 46 return BisonoricLayout(push_layout=push_layout, pull_layout=pull_layout) 47 48 49def load_bisonoric_layout_by_path(layout_path: Path) -> BisonoricLayout: 50 ''' 51 Expects the file at `layout_path` to be YAML, with a structure like this: 52 ``` 53 push: 54 left: 55 - C3 G3 C4 E4 G4 56 - B3 D4 G4 B4 D5 57 right: 58 - C5 E5 G5 C6 E6 59 - G5 B5 D6 G6 B6 60 pull: 61 left: 62 - G3 B3 D4 F4 A4 63 - A3 F#4 A4 C5 E5 64 right: 65 - B4 D5 F5 A5 B5 66 - F#5 A5 C6 E6 F#6 67 ``` 68 - `push` and `pull` at the top level. 69 - `left` and `right` inside. 70 - Each contains a list of strings, representing rows of buttons. 71 - The strings are the pitches of that row of keys, space delimitted. 72 ''' 73 layout_yaml = layout_path.read_text() 74 layout_spec = safe_load(layout_yaml) 75 return parse_bisonoric_layout(layout_spec) 76 77 78def load_bisonoric_layout_by_name(layout_name: str) -> BisonoricLayout: 79 ''' 80 The `layout_name` must be one of the names returned by `list_layout_names()`. 81 ''' 82 if not re.fullmatch(r'\w+', layout_name): 83 raise ValueError('invalid layout name') 84 layout_path = Path(__file__).parent / f'{layout_name}.yaml' 85 return load_bisonoric_layout_by_path(layout_path) 86 87 88def list_layout_names() -> Iterable[str]: 89 ''' 90 Lists all preconfigured layouts. To change the key of a layout, use 91 `concertina_helper.layouts.bisonoric.BisonoricLayout.transpose`. 92 93 >>> list_layout_names() 94 ['20_cg', '30_jefferies_cg', '30_wheatstone_cg'] 95 ''' 96 return sorted([path.stem for path in Path(__file__).parent.glob('*.yaml')]) 97 98 99# TODO: Add a test and uncomment. 100# def load_unisonoric_layout_by_path(layout_path: Path) -> UnisonoricLayout: 101# layout_yaml = layout_path.read_text() 102# layout_spec = safe_load(layout_yaml) 103# return _parse_unisonoric_layout(layout_spec)
def
parse_unisonoric_layout( layout_spec: dict) -> concertina_helper.layouts.unisonoric.UnisonoricLayout:
def
parse_bisonoric_layout(layout_spec: dict) -> concertina_helper.layouts.bisonoric.BisonoricLayout:
def
load_bisonoric_layout_by_path( layout_path: pathlib.Path) -> concertina_helper.layouts.bisonoric.BisonoricLayout:
50def load_bisonoric_layout_by_path(layout_path: Path) -> BisonoricLayout: 51 ''' 52 Expects the file at `layout_path` to be YAML, with a structure like this: 53 ``` 54 push: 55 left: 56 - C3 G3 C4 E4 G4 57 - B3 D4 G4 B4 D5 58 right: 59 - C5 E5 G5 C6 E6 60 - G5 B5 D6 G6 B6 61 pull: 62 left: 63 - G3 B3 D4 F4 A4 64 - A3 F#4 A4 C5 E5 65 right: 66 - B4 D5 F5 A5 B5 67 - F#5 A5 C6 E6 F#6 68 ``` 69 - `push` and `pull` at the top level. 70 - `left` and `right` inside. 71 - Each contains a list of strings, representing rows of buttons. 72 - The strings are the pitches of that row of keys, space delimitted. 73 ''' 74 layout_yaml = layout_path.read_text() 75 layout_spec = safe_load(layout_yaml) 76 return parse_bisonoric_layout(layout_spec)
Expects the file at layout_path
to be YAML, with a structure like this:
push:
left:
- C3 G3 C4 E4 G4
- B3 D4 G4 B4 D5
right:
- C5 E5 G5 C6 E6
- G5 B5 D6 G6 B6
pull:
left:
- G3 B3 D4 F4 A4
- A3 F#4 A4 C5 E5
right:
- B4 D5 F5 A5 B5
- F#5 A5 C6 E6 F#6
push
andpull
at the top level.left
andright
inside.- Each contains a list of strings, representing rows of buttons.
- The strings are the pitches of that row of keys, space delimitted.
def
load_bisonoric_layout_by_name(layout_name: str) -> concertina_helper.layouts.bisonoric.BisonoricLayout:
79def load_bisonoric_layout_by_name(layout_name: str) -> BisonoricLayout: 80 ''' 81 The `layout_name` must be one of the names returned by `list_layout_names()`. 82 ''' 83 if not re.fullmatch(r'\w+', layout_name): 84 raise ValueError('invalid layout name') 85 layout_path = Path(__file__).parent / f'{layout_name}.yaml' 86 return load_bisonoric_layout_by_path(layout_path)
The layout_name
must be one of the names returned by list_layout_names()
.
def
list_layout_names() -> collections.abc.Iterable[str]:
89def list_layout_names() -> Iterable[str]: 90 ''' 91 Lists all preconfigured layouts. To change the key of a layout, use 92 `concertina_helper.layouts.bisonoric.BisonoricLayout.transpose`. 93 94 >>> list_layout_names() 95 ['20_cg', '30_jefferies_cg', '30_wheatstone_cg'] 96 ''' 97 return sorted([path.stem for path in Path(__file__).parent.glob('*.yaml')])
Lists all preconfigured layouts. To change the key of a layout, use
concertina_helper.layouts.bisonoric.BisonoricLayout.transpose
.
>>> list_layout_names()
['20_cg', '30_jefferies_cg', '30_wheatstone_cg']