Index RSS

Org-Mode as simple RAD GUI toolkit

It took me way to long since the last article teasing this one, but today I want to finally describe how I am using Emacs and Org-Mode as a simple RAD (rapid application development) GUI toolkit.

For your convenience, you can find my old article here:

Emacs - Two years later (2022-12-29)

Let's start with my very abstract requirements: I want to use the power of plain text to manage some data. Plain text is no silver bullet, but it has some cool qualities: It is well-supported by lots of applications (be it powerful ones like Emacs or simple ones like your favorite shell commands, e.g. grep), it is searchable (grep, again) and it is efficient to compress and easy to diff (which is good for storing it in version control systems like Git).

But at the same time, I want to hide some complexities behind simpler UI elements. Even for me as a terminal zealot, there is a mental toll for having to remember all these shell commands with their arguments. And I tend to forget things I did not need in a while: What once was absolutely natural to me might be gone when I need it the next time. A good GUI has one large advantage over a shell command: You can quickly discover how to do something and only need to remember where to find the correct widget to do so. The more trivial your GUI is, the more this is true.

A concrete example

I gave two examples in my previous example: Managing and executing unit tests and managing and watching videos. I will go into more detail for the second example here, but the fundamentals should be simple to apply for any other use case.

My concrete requirements are these: I often find videos on Youtube that I want to watch at some later time. But I do not like watching them in a browser, but prefer my dear VLC player. Therefore, I want to somehow remember the videos and watch them via their URLs. I need a way to store these URLs and I want to just click on a button to start VLC with the given video. I also want to know the title of each video - it would be bad for my user experience if all I had were the URLs. But I also want to insert as few meta information manually as possible.

* Designing the Org-Mode document At its most basic form, an Org-Mode document is just a text document that is structured via (cascaded) headlines - Org-Mode is an outliner above all. I am using a separate headline for each category of videos. For example, I separate technical videos from gaming videos.

The contents below each headline can be anything: Unstructured text, links, lists, tables, subsections with own headlines, ... We will be making good use of tables and links here.

My requirements lead to tables because tables can have formulas. This makes it possible to only fill in part of a row and automatically set the other table cells. But this is Emacs: I could also write custom commands that could generate any structure I want. I also could make use of Org-Mode capture templates, which are supposed to quickly make a note of something without having to much of a context switch, which also matches my requirements. They might be a better fit for your requirements - here, I will use tables.

The table will have at least four columns: I want to input the URL in one column, have a link to watch it in the next column and see the title in the third column. I also want to mark the video as watched - I will do this manually in the fourth column. If I want to add further information, it would be trivial to add more columns or add a footnote to some column, which will jump to a detail section. Let's keep it simple here.

The links in the second column will be generated via a formula and will execute a small bit of Emacs Lisp when clicked. I will get to that later.

The Org-Mode document

Here is a simple example of my document:

* Video List
| URL                                         |   | Title | Finished |
|---------------------------------------------+---+-------+----------|
| https://www.youtube.com/watch?v=TSyGryuMh-U |   |       |          |
|---------------------------------------------+---+-------+----------|
#+TBLFM: $2='(format "[[elisp:(vlc \"%s\")][PLAY]]" $1)::$3='(if (is-youtube-url $1) (get-youtube-title $1) "")

This shows the static content of my document, which I had to create once. At this point of time, I have pasted a single URL into the table, but I have not evaluated the formula (via C-*) yet.

When I evaluate the formula, it fills in the second and third column as described: The second column creates a link, which uses square brackets in Org-Mode. The link is just labeled as "PLAY" and calls the "vlc" function with the value from the first column as a parameter.

The third column checks whether the first column contains a valid Youtube URL (via the "is-youtube-url" function). If it does so, it extracts the title via the "get-youtube-title" function. Otherwise, it just returns an empty string. To make good use of this document, I will have to define these three functions and store them in my Emacs configuration.

The Elisp functions


(defun get-youtube-title (url)
  (let ((default-directory "~"))
    (let* ((json (json-parse-string (shell-command-to-string (format "PYTHONWARNINGS=ignore ~/.local/bin/yt-dlp -j --no-warnings \"%s\"" url))))
	   (title (gethash "title" json)))
      (replace-regexp-in-string "|" "-" title))))

(defun is-youtube-url (url)
  (string-match-p "^https://www\\.youtube\\.com/watch\\?v=.*$" url))

(defun vlc (url)
  (let ((default-directory "~")
	(vlc-cmd "vlc"))
    (async-shell-command (format "%s \"%s\"" vlc-cmd url))))

You can find some interesting details in my otherwise simple definitions of these three functions. The first one is that I am using yt-dlp to get all the video information as a JSON structure. This can be parsed by built-in Emacs functions. I also silence all warnings for this call, which disturbs my parsing otherwise, leaving me with invalid JSON.

Another detail is that I might store this document on a different system then the one I am watching my videos on. Emacs is good at opening files via SSH (using Tramp mode), so I do not care much about the locality of my files. I also open my document via a custom command and therefore do not need to type in the network path each time - it always works the same wherever I am. But when I execute yt-dlp or vlc, I want to execute my application locally (especially vlc!). That is the reason for me setting the default-directory temporarily to my home directory before executing the program - I am forcing a return to my local system here.

Closing

I have shown how I use Org-Mode to create a simple application with clickable buttons and automatically determined data. I am using small documents like these quite often and I hope I inspired you (or at least the Emacs users here) to consider using Org-Mode for similar tasks, which can help reduce your own mental load while keeping the advantages of plain text documents.