Don’t get me wrong, I am still a fan of Qt, and it was my first choice before I moved to other options. At the time, around 2013, when I started developing my desktop application, Qt was the most mature option available. Although Electron was introduced around the same time, I might have missed it, or perhaps I did not see its potential. Regardless, I chose Qt, and it worked well for about a year.
The application I am referring to was originally written in VB6 + C++. One reason I chose Qt was that I needed a cross-platform solution. VB6 was promoting a migration to .NET framework, but I did not feel comfortable with that approach, even though I was a C# developer during that time.
Over time, I found that creating GUIs in Qt became more challenging and time-consuming, even for a simple task. Therefore, I started looking for alternative options. Additionally, I needed to expose my application as a Web API, which was not an area in which Qt could assist. However, I still believe that Qt is one of the best options if you need to develop GUI applications for embedded systems.
The first step I took was to rewrite my core code in plain C++ instead of relying on the Qt framework. With the new features introduced in C++14 and C++17, this was straightforward. In my opinion, no one should write core application Qt code unless they have a really good reason to do so.
To make things simple, I will list my migration path in point form:
- I moved the core business logic to plain C++.
- I created a Node.js N-API module that wrapped up the core features I wrote (reference: https://nodejs.org/api/n-api.html).
- I compiled that Node.js wrapper into an Electron-supported plain native Node.js add-on.
After writing the core logic in C++ and importing it as a JS library into an Electron + Angular setup, the development was relatively easy. Now, I am not limited to the components provided by the Qt widgets. Some of you might think that Qt Quick (QML) serves this purpose. I also tried QML before moving to Electron, but, for me, it was still a lot of work.
I wrote a NestJS wrapper for authorization and deployed it as a Lambda function. One difficulty I faced was having to compile my C++ Node.js module in the exact same kernel version that Lambda uses.
It’s not all sunshine and rainbows. There are things I don’t like about the Electron + node addon approach.
Some may argue that Electron is just a glorified Chromium with some extended abilities. There is some truth in that argument. Before you move on, you may have to look for Electron limitations. Electron can sometimes be too heavy for simple applications. For my personal project, one of the biggest drawbacks was Electron’s lack of ability to show print preview out of the box. I used a workaround.
What I did was create a PDF print in memory and pass that byte stream to the PDF.js and show it in a new Electron window as a print preview. I know some of you may feel this is too hacky. Still, I like the outcome. The best part is that designing a printout with HTML and CSS is too easy compared to doing it with QtWidget, QPrintPreviewDialog, etc.
My application was a bit CPU intensive when it came to core business logic. However, this may not be the case for your specific application. Alternatively, you could consider using WebAssembly instead of writing a Native Addon. This would save you the trouble of having to compile the Node Addon for all platforms if you’re starting from scratch.
Apart from these considerations, I already had existing code written in C++, so all I needed to do was remove all QT-related libraries.
I hope this personal experience is helpful to you. It’s important to remember that there’s no one-size-fits-all solution to every problem, and each problem has its unique perspective. You may still choose to use Qt over other options for desktop application development.