Texttools dot py
A small script I find really useful, and you might too!
I make a lot of personal software tools. One of these is "texttools.py", which is easiest to explain with an image:
Paste text in the top box, choose a transform, output appears in the bottom box. I can already do most of these transformations in vim, or with one of the many online tools out there, but I prefer my script for two reasons:
- There's no context switching. I don't have to leave my current tab or vim buffer and worry about cleanup later. One hotkey opens the GUI,
<esc>
closes it, with no break in my workflow. - It loads in <10 ms.
It took me like an hour to make and I use it all the time. And it's small enough that I just share the whole script here.
How it works
Texttools is a python script running a tkinter GUI. I used tkinter because it's a builtin; I would generally not recommend it if you have any better options. On the plus side, being a builtin means you don't need to install a package to use this yourself.
import tkinter as tk
from tkinter import N, S, E, W, ttk
# Complex transforms go here,
# Simple transforms are just lambdas
def _wordcount(s: str):
"Returns a tuple of linecount, wordcount, charcount"
return (len(s.splitlines()), len(s.split()), len(s))
transforms = [
# Transforms go here, for example
{"name": "One line", "transform": lambda x: " ".join(x.splitlines())}
,{"name": "Line/Word/Char", "transform": _wordcount}
]
class GUI():
def __init__(self) -> None:
self.root = tk.Tk()
self.active_transform = lambda x: x # start with no transform
self.layout_gui()
def layout_gui(self) -> None:
self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
self.root.title("Text Tools")
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
self.content_box = tk.Text(self.mainframe, undo=True)
self.content_box.grid(column=2, row=2)
self.output_box = tk.Text(self.mainframe, undo=True)
self.output_box.grid(column=2, row=3)
self.transform_box = tk.Listbox(self.mainframe)
self.transform_box.grid(column=1, row=2, rowspan=2)
for t in transforms:
self.transform_box.insert(tk.END, t["name"])
# Keyboard bindings
self.root.bind("<Escape>", lambda _: self.root.quit())
self.content_box.bind("<Tab>", lambda _: self.transform_box.focus())
# vvv makes clicking or pressing enter change the transform
self.transform_box.bind("<Button-1>", self.select_transform)
self.transform_box.bind("<Return>", self.select_transform)
# vvv makes anything typed in update the output
self.content_box.bind("<Key>",
lambda _: self.root.after(1, self.update))
self.content_box.focus()
def update(self):
content = self.content_box.get('1.0', tk.END)
content = self.active_transform(content)
self.output_box.delete('1.0', tk.END)
self.output_box.insert('1.0', content)
def select_transform(self, _):
try:
selection = self.transform_box.curselection()[0]
self.active_transform = transforms[selection]["transform"]
self.update()
except (ValueError, IndexError):
pass
def main(): # Entry point for pyproject.toml
gui = GUI()
gui.root.mainloop()
if __name__ == "__main__":
main()
Man I forget how much I dislike tkinter until I have to look at it again. If you want to add your own text tools, just put a new item in the global transforms
array.
To make it easier to run the script, I put it in a "toolkit" repo with this pyproject.toml:
[project]
name="toolkit"
version="0.0.1"
requires-python=">=3.8"
[project.gui-scripts]
toolkit-texttools = "toolkit.texttools:main"
Then running pip install -e .
creates a toolkit-texttools
binary (or in my case a .exe
).
Finally, I wrote an AutoHotKey script so I could load it with a keyboard shortcut:
; & numpagedown + t both pressed at same time
NumpadPgDn & t:: toggle_app("Text Tools", "toolkit-texttools.exe")
I like mapping stuff to the numpad because it's guaranteed to not interfere with any OS or program-specific hotkeys.
Short newsletter this week because I'm still recovering from jetlag. See you all next week!
If you're reading this on the web, you can subscribe here. Updates are once a week. My main website is here.