tags: python tk launcher title: Simple Launcher 1 ![]{centre}(schmerp_001.png) A very poor mans launcher thing. I bind it to `A-C-M-space` and then define shorthands in `~/.schmerp.json`. This is for things that aren't quite so common that I want to bind an actual key combo to them, but common enough that I want to be able to get them slightly quicker than launching an terminal and using that. The problem was that I would launch a throwaway terminal to run something, not close it, and be left with 100 open terminals that I had to go through to find which ones I wanted to close. If the shorthand is not found, we attempt to run as command, so this also doubles as a quick command prompt. We split ignoring quotes, so `"o 'hello world'"` would split to `["o","'hello","world'"]` — if you want full shell stuff, launch a terminal. The first item is, after expansion, given to `shutil.which()` to see if it is a command, and if so, then we execv that command with the given arguments. We append the result of the items to the result, so that we could then append args to the entry. E.g. `"x y z"` would expand to `["run_something_with_no_args","y","z"]` Example `~/.schmerp.json` ``` { "x": "run_something_with_no_args", "y": [ "run", "something", "with", "args" ] } ``` what actually happens is in the `DoSomething` class supplied via the `delegate=` parameter to the `Main()` constructor, which can be replaced so that e.g. we can run Python code instead of just running an external program. ```py #!/usr/bin/env python """actions defined by ~/.schmerp.json A very poor mans launcher thing. I bind it to A-C-M-space and then define shorthands in ~/.schmerp.json. This is for things that aren't quite so common that I want to bind an actual key combo to them, but common enough that I want to be able to get them slightly quicker than launching an terminal and using that. The problem was that I would launch a throwaway terminal to run something, not close it, and be left with 100 open terminals that I had to go through to find which ones I wanted to close. If the shorthand is not found, we attempt to run as command, so this also doubles as a quick command prompt. We split ignoring quotes, so "o 'hello world'" would split to ["o","'hello","world'"] -- if you want full shell stuff, launch a terminal. The first item is, after expansion, given to shutil.which() to see if it is a command, and if so, then we execv that command with the given arguments. We append the result of the items to the result, so that we could then append args to the entry. E.g. "x y z" would expand to ["run_something_with_no_args","y","z"] Example ~/.schmerp.json { "x": "run_something_with_no_args", "y": [ "run", "something", "with", "args" ] } what actually happens is in the DoSomething class, which can be replaced so that e.g. we can run Python code instead of just running an external program. Keys: Escape: exit Shift-BackSpace: clear """ import tkinter as tk import tkinter.messagebox from subprocess import run import shutil import os import json import re class Main: def __init__(self, delegate=None): self.delegate = delegate def main(self): root = tk.Tk() label = tk.Label(root, text="Schmerp:", font=("Optima",30)) label.grid(row=0) textinput = tk.Entry(root,font=("Hack Nerd Font Mono",30)) textinput.grid(row=0,column=1) textinput.focus() root.bind('',self.return_handler) root.bind('',self.escape_handler) root.bind('',self.sbackspace_handler) self.label = label self.textinput = textinput self.root = root root.mainloop() def __call__(self,*xs,**kw): return self.main(*xs,**kw) def sbackspace_handler(self,e): self.textinput.delete(0,tk.END) def escape_handler(self,e): print("Escape") exit() def return_handler(self,e): value = self.textinput.get() if self.delegate: try: if self.delegate(value): exit() except Exception as e: self.delegate.do_error(e) class DoSomething: def __init__(self): ifn = os.path.expanduser("~/.schmerp.json") try: with open(ifn) as f: self.schmerp = json.load(f) except Exception as e: print("No ~/.schmerp.json",e) self.schmerp = { } def __call__(self,x): xs = re.split(r"\s+",x) x = xs[0] if x in self.schmerp: y = self.schmerp[x] xs.pop(0) if type(y) is str: xs.insert(0,y) elif type(y) is list: xs = y + xs else: self.do_error(ValueError("Must be string or list")) cmd, *args = xs try: self.do(cmd,*args) return True except Exception as e: self.do_error(e) return False def do(self,cmd,*args): wcmd = shutil.which(cmd) if wcmd is not None: return os.execv(wcmd,[cmd,*args]) else: raise FileNotFoundError(f"Cannot do {cmd}") def do_error(self,e): print("do_error",e) txt = repr(e) tk.messagebox.showinfo(message=txt,icon=tkinter.messagebox.ERROR) if __name__ == "__main__": main = Main(delegate=DoSomething()) main() exit() ```