In my previous post I talked about my new application imageSelector. This week I will talk about the development process. It all started during a long journey I took across my home country. I was thinking... Most of my work revolves around low level system programming and I had actually never ever written a piece of code that draws anything on the screen. Just text applications.
I decided it's about time for me try something different and also to write a kick-ass image selector program. I googled a bit what frameworks I could use and I picked pygame semi random.
I had not actually used pygame before, but I was told it is one of the easiest way to start building a graphics application. It was easy to setup (used my distro's package manager), opened my favourite editor and started coding. Overall it took me about 4-5 hour to get the desired functionality. I had a few major stumbling blocks:
- 100% CPU usage.
pygame was making my fans go to mach 1 after just a minute of running my application. After googling, it turned out it could be a sound problem, or an issue of busy-waiting on events (pygame.event.wait() vs pygame.event.get()), or just having too many events. In my case it turned out that the issue was that mouse movements produced way too many events. Apparently in pygame its recommended to disable the events you don't use one by one by setting them as blocked.
pygame.event.set_blocked(pygame.MOUSEMOTION) #We don't do mouse motion event so disable them
This, together with using event.wait() finally made the CPU usage tamer.
- Scaling images. Drawing an image is done very easy with pygame.blit(image, location), however it does not do any intelligent scaling, resulting in very annoying stretching. As such I couldn't just ask pygame to fit the image to the screen, but had to manually calculate how tall and wide the image would be and draw that on top of a black background.
- Mouse cursor is ugly, window is ugly, icon is ugly. I'm sure all of those are fixable, but having a mouse cursor outside of my desktop theme was pretty ugly.
- What if I want to have a file dialog, to select my folders instead of command line arguments? There are various ways to do this, using GTK, tkinter or even, if you are so inclined, to write everything in pygame. However it looked like waaaay too much work just for a file dialog.
Now, after I had the initial version I showed it to a few people and I was surprised of the amount of positive feedback I had received. Apparently that was a legitimate problem that people had in their daily lives and a few expressed interest at trying out my software. I was beyond exalted!
That being said, I wasn't particularly proud of how it looked in pygame, and the interface was strictly command line which severely limits the potential user pool. Furthermore most of my friends actually don't run Linux as their main operating system (surprise). Thinking about it, I wanted to use a cross-platform toolkit with with minimum installation burden. Making a full fledged executable out of pygame looked like too much of a hassle so I looked elsewhere:
- Java Probably the first choice when it comes to cross-platform application, because only one binary is necessary to run on any operating system. however...
- Requires Java to be installed on the target system. Java is not as popular as it used to be. It doesn't come preinstalled on most computers sold nowadays. It's harder to make people use your software if it requires extra external dependencies.
- I don't like java. This project is supposed to be fun for me, and java is not xD
- C++ As everyone knows, I quite enjoy C++ (unlike the majority of my friends). Unlike java, it requires to have a separate binary compiled for every operating system. If I provide pre-built binaries for Windows and Mac and source code with build instructions for linux users, I should be able to reach everyone without extra external dependencies, however...
- It is not always possible to write platform agnostic code in C++ (more on that later)
- Compiling for Mac pretty much requires you to own a Mac...
I decided to go with C++ and use my favourite cross-platform UI toolkit: Qt
Unlike pygame, Qt has extensive documentation with plenty of examples for most situation. The recommended way to start using it is with the
Due to coding in C++, other aspects of the program such as file manipulation take more times to write, but thanks to the awesome (and cross platform) C++17 filesystem, it is not that many lines of code!
Another great thing about Qt creator is that I can easily (and WYSIWYG) make dialogs and fields. They will automatically have the proper desktop theme and look awesome on any platform.
Having my file dialog now a matter of calling QFileDialog. Qt is fast, simple and very friendly for a person who has never developed a single application with a GUI. I definitely recommend it to first time GUI programmers.
Pygame vs Qt
After implementing the same application in two different frameworks, I can give a comparison between them from the beginner's perspective.
Time necessary to get something out
While Qt is much friendlier when doing advanced things, it requires a few hours of reading before you can get anything out. With pygame I had my initial window that displays images in about 15 minutes. With Qt, that took about 2 hours of reading and set up.
Ease to work with
After the initial steep learning curve, Qt becomes very pleasant to work with. Event management is very clean, the visual editor is newbie friendly, functionality is much richer and the documentation and examples available online make it a lot easier to add functionality compared with pygame. Just for comparison, my image scaling code is about 20 lines in python and just 1 line in Qt.
Winner: Qt. Maybe
Pygame should run anywhere where python is installed, right? Yes, but sometimes on mac input doesn't work. Getting it installed on windows is very simple, as long as you are running a python 2.7 or 3.2. For anything newer you need to recompile pygame yourself. There are options apparently for getting an .exe but it's not as straightforward as I hoped.
With Qt on the other hand, you can Cross-Compile using MXE and mingw or if you have access to the platform, you can load up the project with qmake and build it. Provided, of course, you use cross platform C++ functionality (Qt itself is cross-platform). For deployment, Qt provides windows deployment tool (also runs in wine!!!) and a mac deployment tool which package all your dependencies together with the .exe file. (On Linux, of course, deployment is much simpler due to the widespread usage of package managers).
From a developer's point of view it would seem that a Qt application would be much easier to maintain and distribute to his/her users.
Cross-platform adaptation issues
As a linux user, I developed on linux and then looked to compile on other platforms. It was a relatively painless experience, in a sense that the same codebase is used for all platforms, but getting everything to compile on different platforms is... another matter.
Normally, it would be very easy to port the application to windows, because you can just cross-compile it using mingw. However, as C++17 filesystem is not yet supported, I had to boot up Windows and compile my application using Microsoft Visual Studio C++ compiler (free). Unfortunately, MSVC++ by default does not assume any string is UTF-8 formatted and requires special conversion first, which is... Annoying. Other than that, everything else works out of the box. Again, this issue will be resolved when mingw supports C++17 filesystem for cross-compilation.
Time spent on the windows version ~1 week.
Making my application working on Mac was, surprisingly, a lot more difficult than making it work on Windows. First it's very difficult (and in the case of MacOS Mojave impossible) to install MacOS in a VM. You can, of course use some VM images from around the web, but there is no guarantee that those are not tainted and Apple has conveniently removed all of their checksums to discourage users from getting their software in a non Apple approved way...
After about a week fiddling with VMs and failing, I procured a Mac, installed Xcode and the Qt and voilà... The clang bundled with Xcode doesn't support C++17 Filesystem. So I had to turn to homebrew with gcc, which in turn makes configuring Qt messy and annoying.
I noticed my application's interface looks very big and zoomed on most Macs. It turns out the issue is because of the way Mac scales the user interface with HiDPI displays (or retina as they call them). On Linux and Windows (at least by default) scaling only extends to fonts, whereas on MacOS the resolution is scaled. As a result, any application designed to look good on a HiDPI Linux or Windows will look very different on any but the non-scaled Retina Macs... The proper solution is to have a different interface for scaled Mac resolutions and other low resolution displays, but I left this as future work.
I spent 3 weeks, on and off dealing with various Mac issues and I described all the steps necessary for compiling on github.
It was an amazing experience to conceive a project from scratch, develop it and do a release. I received overwhelmingly positive feedback on the application, as well as personal testimonies of people who tested it and requests for new features!
In the end of the day, the language, the platform don't really matter that much. The important thing is for you to have fun. That's why I ignored the haters and went full C++.