Files
@ 221117506281
Branch filter:
Location: majic-scripts/utils/mapping_generator.py
221117506281
4.6 KiB
text/x-python
[mapping_generator.py] Added support for YAML output to info command:
- The YAML output format for mappings should provide user with an easy
way to get started with assigning the mappings in template.
- The YAML output format for mappings should provide user with an easy
way to get started with assigning the mappings in template.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2025 Branko Majic
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
import shutil
import math
import click
import yaml
from defusedxml import ElementTree
def display_mappables_as_text(mappables):
"""
Display mappables in text format.
:param mappables: List of mappable IDs in the template.
:type mappable: list[str]
"""
# Show mappables in multiple columns, while enforcing a minimum column height (element count). For example, if
# there are only 5 mappables, show them in a single column instead of splitting them up into multiple ones.
terminal_size = shutil.get_terminal_size((80, 20))
column_spacing = 4
row_threshold = math.floor(terminal_size.lines * 0.5)
column_width = max([len(m) for m in mappables])
columns = math.floor((terminal_size.columns) / (column_width + column_spacing))
page_threshold = row_threshold * columns
# Expand the list with blank strings in order to have exact number of 'cells' when printing to
# screeen. Simplifies the code dealing with printing a bit and avoid invalid index access.
if len(mappables) <= row_threshold:
rows = len(mappables)
elif len(mappables) <= page_threshold:
rows = row_threshold
mappables.extend([''] * (page_threshold - len(mappables)))
else:
rows = math.ceil(len(mappables) / columns)
mappables.extend([''] * (columns * rows - len(mappables)))
click.echo('Mappables:\n')
for i in range(rows):
click.echo((" " * column_spacing).join([f'{m:<{column_width}}' for m in mappables[i::rows]]))
def display_mappables_as_yaml(mappables):
"""
Display mappables in YAML format.
:param mappables: List of mappable IDs in the template.
:type mappable: list[str]
"""
document = {
'mappables': {}
}
for mappable in mappables:
document['mappables'][mappable] = ''
click.echo(yaml.dump(document, width=4096))
# Allow use of short option for showing help.
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
"""
Generate input mapping cheatsheets for input devices using specially-crafted scalable vector graphics (SVG)
templates.
To prepare a template, create an SVG using Inkscape, and assign custom IDs to text elements that should be managed
by the mapping generator.
"""
pass
@cli.command()
@click.argument('template', type=click.File('r'))
@click.option('--search', '-s', default='', help='only show matching mappings')
@click.option('--output-format', '-f', type=click.Choice(['text', 'yaml']), default='text', help='output format')
def info(template, search, output_format):
"""
Shows information about the passed-in SVG template. This currently includes:
- List of mappable elements.
"""
template_tree = ElementTree.fromstring(template.read())
namespaces = {
'svg': 'http://www.w3.org/2000/svg',
}
# Inkscape default ID starts with string 'text' unless user assigns custom ID.
mappable_elements = [
e for e in template_tree.findall('.//svg:text', namespaces) if not e.get('id').startswith('text')
]
matched_mappables = sorted([e.get('id') for e in mappable_elements if search in e.get('id')])
if output_format == 'yaml':
click.echo('---\n')
if not mappable_elements:
error = 'No mappables found in the specified template. Please check that you have specified correct file.'
elif not matched_mappables:
error = 'Search term did not match any mappable in the specified template.'
else:
error = None
if error and output_format == 'yaml':
error = yaml.dump({'error': error}, width=4096)
if error:
click.echo(error)
elif output_format == 'text':
display_mappables_as_text(matched_mappables)
elif output_format == 'yaml':
display_mappables_as_yaml(matched_mappables)
if __name__ == '__main__':
cli()
|