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!
Add a comment: