Linux GUI app is a rabbit hole

How it started

First, I was reading the reddit /linuxsucks community. After that, I noticed more 'migration to Linux' discussions, and I thought: why would no one make a Windows-to-Linux migration toolkit? This raised two questions:

Starting with the latter point, I suppose more people should get acquainted with Linux through Git for Windows and virtual machines. The main mistake is when you don’t understand what you are doing, and you install Linux as a single or dual-boot operating system (backups are for cowards), and then it doesn’t work as expected. That’s called 'expectation management', and probably we never really saw it as a task. The most successful case, Ubuntu, tried to meet existing expectations, making Linux 'almost like Windows', but I don’t think it’s worth it. I use Fedora and suggest more people should try it – but it’s an entirely different animal, and you’d better be prepared for that. And, by the way, the question of choosing a distribution could also be addressed in my program.

What could be automated? I thought of two tasks: backups and saving a report about hardware and software installed, for reference. Both tasks are not '1-click' in Windows:

To develop this 'one-time-use app', I went with Python + Tkinter. Python is great for its libraries: getting hardware/software info, creating backups, generating Markdown reports, converting Markdown to HTML, and so on. Tkinter is an integrated GUI toolkit—outdated, sure, but fast, small, and stable. The resulting program was about 17 MB. Then I decided to localize it to German, and Python made that pretty easy. Later, I ran pylint and found so many errors – I honestly don’t know how it worked at all. But I had fun programming it, thinking about class structure and so on.

How it’s going: a new challenge

For the next fun project, I decided to create a GUI app for Linux. The idea was to manipulate PDF files, but also to build a chain of commands: extract pages 3–4 from one file, unite them with pages 1–2 from another, and so on. I personally use the CLI for these tasks, the only problem I have is forgetting the CLI parameters. So I thought: wouldn’t it be nice if I could save this chain of commands and reuse it? Nothing you can’t do with a bash script, though. :) Anyway, here are my requirements:

The program itself should look like three vertically stacked parts:

  1. Files: add and remove files; results of operations (e.g. splitting or uniting PDFs) also show up here.
  2. Operations: a chain of manipulations in a human-readable form; users should be able to add/remove operations and (in the distant future) change their order.
  3. Add an operation: choose an operation type, which changes the number of input fields. For split, I need the pages and an output filename; for unite, I need the files and output filename; and so on.

This UI implies lots of changes on the fly: I need to redraw parts of the UI, but I also need to handle faulty situations. For example, you can’t use a file if it’s only created in the next step. There’s a long list of 'what can’t be done' and 'what to do if a filename or operation is removed', so this error-handling logic will be a significant part of the program.

A rabbit hole

And then the 'rabbit hole' thing began: choosing a GUI library. I chaotically researched everything below, had chatbots write examples to see if I could handle them, and I hope my findings will be useful to someone. I’ll rate GUI libraries from simplest to most complicated:

  1. Zenity is a great tool. You can use it with any language or even a bash script, but you’re not supposed to redraw parts of the UI – it’s like launching a single program. But it has a file-open dialog, and it can return multiple files. When I tried Fyne (which doesn’t allow selecting multiple files), I had to use Zenity as well.
  2. Tk: sounds stupid because it’s old as hell, but it works. It has a new version (9), a new 'azure' theme, and new Go bindings. So, if it’s stupid but it works – then it’s not stupid (C). It’s fast, works on Linux, Mac, and Windows (a bonus since Go also works cross-platform), it’s surprisingly stable, and it’s rich in widgets. I mean, if your grandpa could get away with these widgets, so can you. It requires simulating HTML, but I don’t need that for this app. So I stuck with Tk9.0 for now. I even considered writing my app in Tcl – it has procedures, loops, and ifs – but it’s too primitive. Go is nicer with structs, interfaces, goroutines, and all that. And surprise! Tk supports selecting multiple files in a file-open dialog. I felt like the Tk guys were my brothers: they think about functionality first.
  3. Fyne is the recommended Go GUI library, and it’s great. But I tried to write a single operation (extracting pages from a PDF), and Fyne implied using channels just to keep the UI responsive. That felt like overkill. I cried and vented to a chatbot, and it agreed: in this case, Fyne is probably too much.
  4. GTK4 bindings exist for Go. The funny part is that you can use Cambalache (a RAD tool) and call it from your Go logic. It’s a bit like the next, more 'cutting-edge' approach: treating your GTK4 spec as a front-end. I couldn’t figure out why I should use these widgets, and GTK4 only works on Linux. I also doubted whether I’d be able to manipulate the UI easily by adding/removing data. Honestly, I miss React here.
  5. Wails (for Go) is a great approach: you use JS (probably React) for the front-end. It’s like Tauri for Rust or Electron for JS, with the same drawback: the output is always heavy (20 MB minimum). Yes, I considered Rust (Tauri’s footprint is a bit smaller), but my programming skills aren’t there. If your app is big and complicated anyway, this might be the way to go. React is awesome for declaring UIs and handling user input – I liked it.

Ok, now you’re ready to look at my work in progress, which I spent about a week building, covered in blood and tears (needed some drama here): a Go app with Tk9.0 and the azure theme:

Add file

I think we need more high-quality GUI apps like this one. :) That’s all for today, folks – hope you enjoyed the ride! :)