From 03475d4b04b327a9c76cc40912fc384d4c47c6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Alvari=C3=B1o?= Date: Tue, 28 Mar 2023 14:57:40 +0200 Subject: [PATCH] Initial commit with some skeleton functionality --- etc/config.ini | 8 +++ etc/menu.yml | 22 ++++++++ mach5 | 48 ++++++++++++++++++ machlib/__init__.py | 0 machlib/menu.py | 119 ++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 6 files changed, 198 insertions(+) create mode 100644 etc/config.ini create mode 100644 etc/menu.yml create mode 100755 mach5 create mode 100644 machlib/__init__.py create mode 100644 machlib/menu.py create mode 100644 requirements.txt diff --git a/etc/config.ini b/etc/config.ini new file mode 100644 index 0000000..27ce180 --- /dev/null +++ b/etc/config.ini @@ -0,0 +1,8 @@ +# This is mach5 config +[General] +# default_menu_file = ~/.config/mach5/menu.yml +default_menu_file = ./etc/menu.yml + +# root window title and geometry +rtitle = Mach5 +rgeometry = 300x600 \ No newline at end of file diff --git a/etc/menu.yml b/etc/menu.yml new file mode 100644 index 0000000..a3e3556 --- /dev/null +++ b/etc/menu.yml @@ -0,0 +1,22 @@ +name: Principal +sc: null +entries: + - cmd: gimp + name: Gimp + sc: g + - cmd: inkscape + name: Inkscape + sc: i + - name: Ideas + sc: j + entries: + - cmd: ~/apps/Treesheets/current + name: Treesheets + sc: t + - cmd: ~/apps/freeplane/current + name: Freeplane + sc: f + - name: Emacs + sc: e + cmd: emacs + diff --git a/mach5 b/mach5 new file mode 100755 index 0000000..5f816b9 --- /dev/null +++ b/mach5 @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import argparse +from configparser import ConfigParser, ExtendedInterpolation +import os +import errno + +import machlib.menu as mm + +# default_configuration_file = "~/.config/mach5/config.ini" +_default_configuration_file = "./etc/config.ini" +# _default_menu_file = "~/.config/mach5/menu.yml" +_default_menu_file = "./etc/menu.yml" + +# -------------------- MAIN -------------------- + +# Parse command line options +arg_parser = argparse.ArgumentParser( + description='Our simple chords application') +arg_parser.add_argument('-c', '--cfg', + help='Config ini file', + default=_default_configuration_file) +arg_parser.add_argument('-m', '--menu', + help='Menu yaml file', + default=_default_menu_file) + +args = arg_parser.parse_args() + +# Load configuration file +_configuration = ConfigParser(interpolation=ExtendedInterpolation()) +found = _configuration.read(os.path.expanduser(args.cfg)) + +if (not found): + print('[ERROR] Config file not found') + raise FileNotFoundError(errno.ENOENT, + os.strerror(errno.ENOENT), args.cfg) + +menu = mm.Menu.from_file(args.menu) + +print(menu) +print(menu.get_entries().keys()) +for v in menu.get_entries().values(): + print(f" {v}") +print(f"Len is {len(menu)}") + +print ("-"*10) +for i in menu: + print(i) diff --git a/machlib/__init__.py b/machlib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/machlib/menu.py b/machlib/menu.py new file mode 100644 index 0000000..db63a3d --- /dev/null +++ b/machlib/menu.py @@ -0,0 +1,119 @@ +"""Implement Mach5 menus.""" + +import os +import errno +import yaml +from collections import OrderedDict +import subprocess + + +def get_data_from(menu_file): + """Get menu data from a yaml menu definition file.""" + menu_file = os.path.expanduser(menu_file) + if not os.path.exists(menu_file): + raise FileNotFoundError(errno.ENOENT, + os.strerror(errno.ENOENT), menu_file) + with open(menu_file, 'r') as yaml_file: + menu_data = yaml.safe_load(yaml_file) + + ####################################################################### + # WARNING # + # yaml.safe_load does NOT return and OrderedDict # + # so far, dict in Python are ordered, but IT CAN CHANGE # + ####################################################################### + + if 'entries' not in menu_data: + raise ValueError(f"Malformed file {menu_file}.\ + First entry is not a menu") + return menu_data + + +class Command: + """A Mach5 command.""" + + def __init__(self, cmd_data): + """Construct a Mach5 command object.""" + if len(cmd_data['sc']) != 1: + raise ValueError(f"Shortcut must be only one character.\ + {cmd_data['sc']} is invalid.") + self._name = cmd_data['name'] + self._cmd = cmd_data['cmd'] + self._sc = cmd_data['sc'] + + def show(self): + """Return string for Command.""" + return f"{self._sc} - {self._name}" + + def __repr__(self): + """Return representation string for Command object.""" + return f"{self._sc!r} - {self._name!r} - {self._cmd!r}" + + def __str__(self): + """Return string conversion for command.""" + return f"{self._sc} - {self._name}" + + def execute(self): + """Execute command.""" + print(f"Execute command {self._name}") + subprocess.Popen(os.path.expanduser(self._cmd)) + + +class Menu: + """A Mach5 menu class.""" + + def __init__(self, menu_data): + """Init a menu object instance from a menu data dict.""" + self._name = menu_data['name'] + self._entries = OrderedDict() + self._sc = menu_data['sc'] + for e in menu_data['entries']: + if e['sc'] in self._entries: + raise ValueError(f"Repeated shortcut {e['sc']}") + if 'entries' in e: + self._entries[e['sc']] = Menu(e) + else: + self._entries[e['sc']] = Command(e) + + def __len__(self): + """Return number of items in menu.""" + return len(self._entries) + + def __iter__(self): + return MenuIter(self) + + def from_file(path): + """Return a Menu instance from menu definition file.""" + return Menu(get_data_from(path)) + + def __repr__(self): + """Return repr string for menu object.""" + return f"{self._sc!r} - {self._name!r}" + + def __str__(self): + """Return string conversion for menu.""" + if self._sc: + return f"{self._sc} - {self._name}" + else: + return f"{self._name}" + + def get_entries(self): + """Return entries for menu instance.""" + return self._entries + +class MenuIter: + """A helper class to make iterables Menu objects""" + + def __init__(self, menu): + """Return an iterable menu""" + self._entries = menu.get_entries() + self._current = 0 + + def __iter__(self): + return self + + def __next__(self): + if self._current < len(self._entries): + item = self._entries[self._current] + self._current += 1 + return item + raise StopIteration diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bee6c14 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +PyYAML==6.0