Qt and par2deep

code

I wrote par2deep to make working with the par2 command easier. The tool helps you protect a file tree recursively by providing bulk reporting and options. For instance, if you modify your file tree (add some files, remove some, edit others, and perhaps experience some bitrot) you can easily get an overview of these changes and options for handling them (restore, recreate, etc.).

The tool started out as a cli tool but I quickly added a tkinter GUI, because when reviewing changes to thousands of files, the command line may get a bit cramped, especially if you want to handle changes to groups of files separately. tkinter made sense because the tool is written in Python, and although functional, on KDE, my main platform, it is thoroughly alien looking, as well as hard to read if you use any dark themes. In another project, dosia, I therefore chose to use Qt, through PyQt5 (PySide2 is actually also perfectly usable). Building the GUI is much easier and consistent than tkinter.

So yesterday I completed the port of the par2deep GUI to Qt. It looks much better now on KDE 😃. This rewrite will also make it easier implementing the currently open issues, mainly per file or per sub-directory modification of actions, which I kind of stranded halfway about a year ago with tkinter. Version 2.0.0 should be here soon with all that good stuff implemented. What did I learn doing this?

  1. That my par2deep class was a well chosen separation of concerns, because I did not have to change a single line to the core logic of the program just to change the GUI. This class is now used by three different interfaces (cli,tk,qt).
  2. Qt and Python’s threading library do not work together. Apart from the GUI, I had to shift the threads I was using to keep the UI responsive during long actions to Qt’s QThreads. Fortunately that is ultimately easier and cleaner than threading, at the cost of having to be very clean about shared resources (you can’t!). I therefore pass the par2deep instance around, which in my case is fine because there is no concurrent activity on it. The par2deep class is single threaded, it relies on your version of par2 being multithreaded.
  3. I had some experience with Qt so that came in handy, but googling Qt docs/examples is a bit of a pain. You’ll run into plenty of Qt4 stuff, including official docs. Qt is a huge project, and a great strength (object-oriented composability makes total sense for GUI components) is also a weakness: Qt itself is thusly layered and sometimes you will have to stumble by chance over the existence of a more specialized widget (QTreeWidget) of a very generic prototype (QTreeView).

All in all a huge improvement, composing your own widgets is so much nicer in Qt than tkinter. The downside is that it’s a huge package (PyQt5 is 60MB, PySide2 166MB!) and both packages ship only with a limited selection of themes (Windows and Fusion, the latter of which doesn’t look native on any platform). My distro, KDE Neon, ships with PyQt5 and a theme to match KDE (Breeze), so that’s actually the main reason I use PyQt5. But replacing those imports to PySide2, and renaming pyqtSignal to Signal works fine, so if the packaging situation for PySide2 improves, I can jump ship in a heartbeat.