Bundling AppImages with Themes.

One of the projects which I have been undertaking in recent weeks has been, to teach myself GUI programming using the Qt5 GUI Library, of which I have version 5.7.1 installed on a good, tower computer, along with the IDE “Qt Creator”. What can be observed about this already is, that under Debian 9 / Stretch, which is a specific build of Linux, in addition to just a few packages, it’s really necessary to install many additional packages, before one is ready to develop Qt Applications, because of the way Debian breaks the facility into many smaller packages. Hypothetically, if a person was using the Windows, Qt SDK, then he or she would have many of the resources all in one package.

Beyond just teaching myself the basics of how to design GUIs with this, I’ve also explored what the best way is, to deploy the resulting applications, so that other people – technically, my users – may run them. This can be tricky because, with Qt especially, libraries tend to be incompatible, due to even minor version differences. So, an approach which can be taken is, to bundle the main libraries required into an AppImage, such that, when the developer has compiled everything, the resulting AppImage – a binary – is much more likely actually to run, on different versions of Linux specifically.

The tool which I’ve been using, to turn my compiled binaries into AppImage’s, is called ‘linuxdeployqt‘, and is not available in the Debian / Stretch repositories. However, it does run under …Stretch.

But a developer may have questions that go beyond just this basic capability, such as, what he or she can do, so that the application will have a predictable appearance – a “Style” or “Theme” – on the end-user’s Linux computer. And essentially, I can think of two ways to approach that question: The ‘official but slightly quirky way’, and ‘a dirty fix, that seems to get used often’…

The official, but slightly quirky way:

Within the AppImage, there will be a ‘plugins’ directory, within which there will be a ‘platformthemes’ as well as a ‘styles’ subdirectory. It’s important to note, that these subdirectories serve officially different purposes:

• The ‘platformthemes’ subdirectory will contain plugins, that allow the application to connect with whatever theme engine the end-user’s computer has. Its plugins need to match libraries that the eventual user has, determining his desktop theme, And
• The ‘styles’ subdirectory may contain plugins, which the end-user does not have installed, but were usually compiled by upstream developers, to make use of one specific platform-engine each.

Thus, what I had in these directories, for better or worse, was as shown:

dirk@Phosphene:~/Programs/build-Dirk_Roots_GUI_1-Desktop-Release/plugins/platformthemes$ls KDEPlasmaPlatformTheme.so libqgtk2.so libqgtk3.so dirk@Phosphene:~/Programs/build-Dirk_Roots_GUI_1-Desktop-Release/plugins/platformthemes$

dirk@Phosphene:~/Programs/build-Dirk_Roots_GUI_1-Desktop-Release/plugins/styles$ls breeze.so libqgtk2style.so dirk@Phosphene:~/Programs/build-Dirk_Roots_GUI_1-Desktop-Release/plugins/styles$



The reader may already get, that this was a somewhat amateurish way, to satisfy themes on the end-user’s machine. But in reality, what this set of contents, of the AppImage, does rather well is, to make sure that the 3 main theme engines on an end-user’s computer are recognized:

1. Gtk2,
2. Gtk3,
3. Plasma 5.

And, if the application tries to make no attempts to set its own theme or style, it will most probably run with the same theme, that the end-user has selected for his desktop. But, what the point of this posting really is, is to give a hint to the reader, as to how his AppImage could set its own theme eventually. And so, according to what I just cited above, my application could choose to select “Breeze” as the Style with which to display itself, or “Gtk2″. But, here is where the official way gets undermined, at least as the state of the art was, with v5.7.1 of Qt:

• ‘Breeze’ can only be set (by the application), if the end-user’s machine is running Plasma 5 (:1), And
• ‘Gtk2′ can only be set (by the application), if the end-user’s machine supports Gtk2 themes, which many Plasma 5 computers have the additional packages installed, to do.

What this means is that, even though I could try to create a predictable experience for the end-user, what the end-user will see can still vary, depending on what, exactly, his platform is. And beyond that, even though I could set the ‘Gtk2′ Style with better reliability in the outcome, I could also just think, that the classical, ‘Gtk2′ style is a boring style, not worthy of my application. Yet, in this situation, I can only select the “Breeze” theme from within my application successfully, if the end-user is based on Plasma 5. If the end-user is not, then my application’s attempt to set “Breeze” will actually cause Qt v5.7.1 to choose the “Fusion” theme, that Qt5 always supports, that might look okay, but that is not “Breeze”…

So, what other options does the application developer have?

(Updated 9/12/2020, 18h15… )

(As of 9/11/2020, 14h20: )

Well, still playing ‘within the rules’, the dev could populate his ‘styles’ subdirectory as follows:


dirk@Phosphene:~/Programs/QtResources_Manual/plugins/styles$ls breeze.so libqcleanlooksstyle.so libqmotifstyle.so libbb10styleplugin.so libqgtk2style.so libqplastiquestyle.so dirk@Phosphene:~/Programs/QtResources_Manual/plugins/styles$



This would again be done by first, installing the Debian packages specific to one version of Debian, and then copying and pasting some files and/or folders from the root file-system to the project’s folders, all using a simple point-and-click approach, and using a standard file system explorer, which cannot write to the root file system, but which can paste items into the project file system, that belongs to the user. Next, the user can modify his ‘main()’ function approximately like so:

#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QStyleFactory>

int main(int argc, char *argv[])
{

QApplication a(argc, argv);

qDebug() << QStyleFactory::keys();

QApplication::setStyle("Breeze");

MainWindow w;
w.show();

return a.exec();
}



What this ‘main()’ function does is, to output text to the terminal emulator – the console – even though the app may be displaying a proper main window. And in my case, the following is what gets output to the IDE’s Application Output Pane as the application starts up:


Starting /home/dirk/Programs/build-Creator_Test6-Desktop-Debug/Creator_Test6...
("Breeze", "bb10dark", "bb10bright", "cleanlooks", "gtk2", "cde", "motif", "plastique", "Windows", "Fusion")



In other words, Line 11 of my main function outputs the list of available Styles (not taking into account, whether they are implemented in the AppImage, or whether they are merely installed on the developer’s computers), and Line 13 then Selects ‘Breeze’, even though the end-user will need to be running a Plasma 5 -based computer, in order for this selection to take effect.

I could choose another theme from the list that I just arranged my application to print, knowing that most of them will be supported by the ‘Gtk2′ theme engine, presumed to be running on the end-user’s computer.

But, even if I could assume that the end-user supports ‘Gtk2′, I could find every Style in that list to be boring! Therefore, there must be some other way. And, there is…

The Fix, that often gets used instead:

What the developer may do instead is, not to try loading a Style programmatically, but rather, to include one of two style-sheets as resources, to have the application open them as text files at run-time, and then, to apply one of the style-sheets to the application object.

If the developer is looking for good style-sheets to drop-in, the following repository offers some:

https://github.com/Alexhuszagh/BreezeStyleSheets

From what I hear, the style-sheets ‘light.qss‘ and ‘dark.qss‘ from this repository look good, and neither requires in a closely defined way, that a specific theme engine or style be supported on the end-user’s machine. What I would suggest however is, that if the application developer wants to use these, he should mention their developer honourably, wherever the application developer also displays his License Notification, which open-source software is supposed to display.

Then, assuming that both these style-sheets are included by the resource script at virtual directory ‘/res’, this is approximately, how to apply them to the application:


QApplication a(argc, argv);
QFile fh(QString(":/res/dark.qss"));
QTextStream stream(&fh);
fh.close();



1:)

I should admit that a Gtk3-based version of the ‘Breeze’ Desktop Theme also exists. However, installing that will fail to add any plugins, to the Qt5 system directory, from where the application developer would normally need to copy and paste them into his project’s ‘styles’ directory. This is because Gtk3 is a different GUI Library from Qt. Installing that will add it to GNOME, as well as to potential Gtk3 applications. It’s enough to ask, that Qt5, with the proper packages, allows for recognition of Gtk3 Theme Engines, installed on possible end-users’ computers. And this can be found in the ‘platformthemes’ subdirectory of the developer’s ‘Qt5′ system directory, if he or she has it installed.

(Update 9/11/2020, 15h40: )

The fact has come to my attention, that an alternative ‘main()’ function can be written like so:

#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include <QStyleFactory>

int main(int argc, char *argv[])
{

QApplication a(argc, argv);

qDebug() << QStyleFactory::keys();

QApplication::setStyle(QStyleFactory::create("Breeze"));
//    QApplication::setStyle("Breeze");

MainWindow w;
w.show();

return a.exec();
}



Changing the code in this way, may or may not allow the application to create the ‘Breeze’ style, even if it was not available on the end-user’s computer, based on the following assumptions:

• The ‘Breeze’ style is represented by the responsible plug-in, in the project’s ‘styles’ sub-folder, And
• The ‘Plasma 5′ Platform-Theme is represented in the project’s ‘platformthemes’ sub-folder (in my case, by the shared library ‘KDEPlasmaPlatformTheme.so‘…), And
• All the relevant libraries which these plug-ins depend on, have in fact been bundled into the responsible project sub-folder, be it in ‘FHS’ or ‘non-FHS’ form, so that, based entirely on these libraries being bundled, the style in question might be built by the application.

I have yet to test, whether this modification of the code accomplishes, what I wanted.

(Update 9/11/2020, 16h40: )

I have now tested the modified code. The way I did that was, to compile the (new) AppImage and run that on my Chromebook, which obviously has neither Plasma 5 nor the ‘Breeze’ theme installed. What was happening with the earlier version was, that the AppImage ran fine, but with a generic style, that could correspond to ‘Fusion’.

The modified code does exactly what the simpler version does. Therefore, it is not a success, in the context of this posting. ‘One still needs to cheat.’

This poses the follow-up question, of whether I could get the other, Gtk2-based styles to display on my Chromebook, styles which I listed above. And the answer to that question is the idea, that it depends on whether I’m running the AppImage within a VNC session, which I can do. My VNC desktop on the Chromebook is set up as using the desktop manager LXDE, which should, in turn, accept Gtk2 styles.

For past tests, to run the AppImage’s outside any VNC desktop was sufficient to satisfy my curiosity.

(Update 9/12/2020, 12h35: )

I have now performed the experiment of running the same AppImage, which uses the second version of the ‘main()’ function, and which tries to ‘create()’ the Breeze theme, even if it did not appear in the list of styles that could be selected from, on my Chromebook, but, from within my LXDE VNC Desktop. The result was a better approximation of the Breeze style, than the previous ‘Fusion’ style, but still not a perfect implementation of Breeze. I’d estimate the reason for this partial success to be as follows:

• The version of LXDE I have installed on the Chromebook supports ‘Gtk3′, also by having the corresponding theme engine installed, And
• My AppImage was built, with the Qt5 plug-in, for the Gtk3 theme engine, included, And
• When told to recreate the Breeze style, my application did so with partial success, But
• Because neither the ‘Gtk3′ nor the ‘Plasma 5′ Breeze theme was actually installed on the Chromebook, what was still missing was, the icon sets, that also define Breeze. While the application can adjust certain behaviours, where are the Breeze icons supposed to come from? I did not bundle those, and to do so would have complicated the application in a prohibitive way.

So, Breeze can be implemented from my end in a partial way, as long as the target computer either has the ‘Plasma 5′ or the ‘Gtk3′ theme engine installed.

(Update 9/12/2020, 18h15: )

I forgot to mention that the AppImage’s in which I carried out this experiment, are the ones in my repository, named ‘Creator_Test6-x86_64.AppImage‘ and ‘Creator_Test6-FHS-x86_64.AppImage‘. The reader may let me know, whether those AppImage’s give him an approximation of the Breeze style, if he or she tries them. And my repository resides at this URL:

https://dirkmittler.homeip.net/binaries/

(Update 4/04/2021, 23h00: )

I have found out, that there was a basic error in how I was using the plug-ins facility of ‘linuxdeployqt‘. I had mistakenly assumed, that if I simply added plug-ins to the ‘plugins/‘ subdirectory, the tool would automatically relink those, to use the bundled libraries in the ‘lib/‘ subdirectory. But this was wrong, and for that reason, the plug-ins so added could not work. What I found works instead, is to add the following argument to the invocation of ‘linuxdeployqt‘:

-extra-plugins=platformthemes,styles

What this will do is twofold:

• Add all the Theme Engines and Styles to the project that the developer has installed, And
• Link those plug-ins to the bundled libraries.

The AppImage named ‘Creator-Test6-x86_64.AppImage‘ in my binaries repository, now uniquely incorporates this feature, as a test, into how well I can get the AppImage to integrate with the user’s desktop environment.

Enjoy,

Dirk