|
|
|
"""Implements Mach5 menues."""
|
|
|
|
|
|
|
|
import os.path
|
|
|
|
import errno
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
from collections import OrderedDict
|
|
|
|
from getkey import getkey, keys
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_data_from(menu_file):
|
|
|
|
"""Get menu data from a yaml menu 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)
|
|
|
|
|
|
|
|
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['heu']} 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 execute(self):
|
|
|
|
"""Execute command."""
|
|
|
|
print(f"Execute command {self._name}")
|
|
|
|
subprocess.Popen(os.path.expanduser(self._cmd))
|
|
|
|
|
|
|
|
|
|
|
|
class Menu:
|
|
|
|
"""A Mach5 menu."""
|
|
|
|
|
|
|
|
def __init__(self, menu_data):
|
|
|
|
"""Construct a menu object 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 show(self, recursive=False):
|
|
|
|
"""Show Menu."""
|
|
|
|
my_str = ""
|
|
|
|
if self._sc:
|
|
|
|
my_str = f"{self._sc} - "
|
|
|
|
my_str += f"{self._name} (m)"
|
|
|
|
if recursive:
|
|
|
|
my_str += "\n"
|
|
|
|
for i, v in self._entries.items():
|
|
|
|
my_str += f"{v.show()}\n"
|
|
|
|
return my_str
|
|
|
|
|
|
|
|
def show_entries(self):
|
|
|
|
"""Show Menu entries."""
|
|
|
|
my_str = ''
|
|
|
|
for i, v in self._entries.items():
|
|
|
|
my_str += f"{v.show()}\n"
|
|
|
|
return my_str
|
|
|
|
|
|
|
|
def get_command(self):
|
|
|
|
"""Get command from menu."""
|
|
|
|
print(self.show(True))
|
|
|
|
while True:
|
|
|
|
opt = getkey()
|
|
|
|
if opt in self._entries:
|
|
|
|
if isinstance(self._entries[opt], Menu):
|
|
|
|
return self._entries[opt].get_command()
|
|
|
|
else:
|
|
|
|
return self._entries[opt]
|
|
|
|
elif opt == keys.ESC:
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
def get_option(self):
|
|
|
|
"""Get option with Tkinter."""
|
|
|
|
|
|
|
|
def key_pressed(event):
|
|
|
|
"""Return char for key_pressed event."""
|
|
|
|
opt = event.char
|
|
|
|
if opt in self._entries:
|
|
|
|
if isinstance(self._entries[opt], Menu):
|
|
|
|
return self._entries[opt].get_option()
|
|
|
|
else:
|
|
|
|
root.destroy()
|
|
|
|
self._entries[opt].execute()
|
|
|
|
exit(0)
|
|
|
|
elif opt == keys.ESC:
|
|
|
|
exit(0)
|
|
|
|
|
|
|
|
root = tk.Tk()
|
|
|
|
root.geometry('300x100')
|
|
|
|
root.title("Mach5")
|
|
|
|
text_box = tk.Text(root)
|
|
|
|
text_box.insert(tk.END, self.show(True))
|
|
|
|
text_box.grid(row=0, column=0)
|
|
|
|
root.bind("<Any-KeyPress>", key_pressed)
|
|
|
|
|
|
|
|
root.mainloop()
|