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.
#!/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('<Return>',self.return_handler)
root.bind('<Escape>',self.escape_handler)
root.bind('<Shift-BackSpace>',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()