pkg2appimage icon indicating copy to clipboard operation
pkg2appimage copied to clipboard

Native look for Qt applications on Gtk systems

Open probonopd opened this issue 9 years ago • 42 comments

We should try to make Qt applications look native.

https://bugreports.qt.io/browse/QTBUG-53844

https://forum.manjaro.org/t/solved-qt-apps-theming-broken-after-last-nights-update/6471

probonopd avatar Sep 07 '16 07:09 probonopd

Need to find out why the Ubuntu global menu works for one of the Qt-based AppImages mentioned there but not for the others: http://forum.freecadweb.org/viewtopic.php?f=10&t=15525&start=30#p152606

Update: Adding the appmenu-qt5 or appmenu-qt (for Qt 4 apps) package to the ingredients should improve it for Ubuntu.

probonopd avatar Jan 12 '17 18:01 probonopd

Also see https://github.com/probonopd/AppImageKit/issues/125#issuecomment-286194585 (Qt Platform Theme integration plugins for the KDE Plasma workspaces. Unofficial mini version).

probonopd avatar Mar 13 '17 19:03 probonopd

See https://github.com/MartinBriza/adwaita-qt to make it look native on GTK.

adwaita-qt

probonopd avatar Nov 18 '17 19:11 probonopd

On Ubuntu, /usr/lib/x86_64-linux-gnu/qt5/plugins/platformthemes/libqgtk3.so is present from the package qt5-gtk-platformtheme. Perhaps we need to find and bundle this or at least symlink to it?

probonopd avatar Nov 18 '17 20:11 probonopd

Just side note. Do we have confirmed if QIcon::fromTheme(...) works in AppImage? It seems that my application - https://github.com/martinrotter/textosaurus/releases/tag/0.9.2 - does not work correctly. You can launch it and change icon theme in tools/settings/gui to some custom icon theme - for example Papirus (or other you have installed).

Then if you restart application, the icons for buttons/menus are not loaded correctly. When I use the app outside of AppImage (e.g. I install it with distro package manager), everything works. Is QIcon::fromTheme(..) somehow "sandboxed" away?

martinrotter avatar Apr 20 '18 10:04 martinrotter

Neither linuxdeployqt nor AppImage are sandboxing anything. It is more likely that the application or some Qt library is trying to load something from a hardcoded location within /usr. Can you verify what is going on using sudo strace -eopen -f Your.AppImage 2>&1 | grep ENOENT?

probonopd avatar Apr 20 '18 15:04 probonopd

This worked with LXImage-QT on XFCE Vanilla:

  • Included GTK theme support plugins for QT5 no AppImage

And run:

lximageqt.AppImage -style=GTK+

And the GTK theme is used instead of Fusion

Maybe a Script checking for Desktop Environment?

sudo-give-me-coffee avatar Apr 20 '18 15:04 sudo-give-me-coffee

QIcon::fromTheme(..) only worked with QT5ct for me

sudo-give-me-coffee avatar Apr 20 '18 15:04 sudo-give-me-coffee

This worked with LXImage-QT on XFCE Vanilla

With which exact AppImage is this working for you? Please provide a URL so that I can test. This would be a big step forward. Thanks.

probonopd avatar Apr 20 '18 16:04 probonopd

@probonopd OK, will do tonight. It is just weird.

martinrotter avatar Apr 23 '18 04:04 martinrotter

Btw, just to mention. Flatpak'ed version of the same application does not suffer from the problem.

martinrotter avatar Apr 23 '18 04:04 martinrotter

From https://community.kde.org/Guidelines_and_HOWTOs/Flatpak#Styles_and_integration_with_other_desktops I deduce that when creating an AppImage, one might consider to bundle

  1. Adwaita icon theme (can't it be assumed to be "there" on the target systems?)
  2. Adwaita KStyle https://github.com/MartinBriza/adwaita-qt.git
  3. QGnomePlatform Qt platform theme (according to this, this Qt 5 platform theme applies the appearance settings of GNOME for Qt applications. It does not provide a Qt style itself, instead it requires a style that support both Qt and GTK+.)

inside the AppImage.

What I am not sure about, though, is why all of this is apparently needed for Qt 5 when in fact in earlier versions of Qt it was "just working", and widgets looked like Gtk+ widgets on Gtk+ systems.

What is also not clear to me is the relationship between this issue and Qt vs. KDE and Gtk+ vs. GNOME. What I am looking for is the best way to make all Qt applications (including, but not limited to, KDE ones) look native on Gtk+ systems (including, but not limited to, GNOME ones).

probonopd avatar May 17 '18 07:05 probonopd

According to Qt's documentation

Plugins linked with a Qt library that has a higher version number will not be loaded by a library with a lower version number.

Example: Qt 5.0.0 will not load a plugin built with Qt 5.0.1.

Plugins linked with a Qt library that has a lower major version number will not be loaded by a library with a higher major version number.

Example: Qt 5.0.1 will not load a plugin built with Qt 4.8.2. Example: Qt 5.1.1 will load plugins built with Qt 5.1.0 and Qt 5.0.3.

I ended up writing a wrapper script that improve (not resovle, KDE dialogs on some system are not native) native look.

FILE_TO_RUN.wrapper

#!/bin/sh

# clear LD_LIBRARY_PATH (set from AppRun)
# without clear "qtpaths --plugin-dir" returns $HERE/usr/lib
OLD_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH=

SELF="`readlink -f "${0}"`"
EXEC="${SELF%.wrapper}"
HERE="${SELF%/*}"

QT_PLUGIN_PATH="${HERE}/../plugins"

PLUGINS_KF5="`kf5-config --qt-plugins 2>/dev/null`"
[ ! -z ${PLUGINS_KF5} ] && QT_PLUGIN_PATH="${QT_PLUGIN_PATH}:${PLUGINS_KF5}"

PLUGINS_QT5="`qtpaths --plugin-dir 2>/dev/null`"
[ ! -z ${PLUGINS_QT5} ] && [ "${PLUGINS_QT5}" != "${PLUGINS_KF5}" ] && QT_PLUGIN_PATH="${QT_PLUGIN_PATH}:${PLUGINS_QT5}"

export QT_PLUGIN_PATH
export LD_LIBRARY_PATH="${OLD_LD_LIBRARY_PATH}"
exec "${EXEC}" "${@}"

[Desktop Entry] Exec=MyExecutable.wrapper

Note Your AppImage must be bundled/compiled with Qt >= QT_ON_HOST

sandman7920 avatar Jun 18 '18 13:06 sandman7920

Thanks @sandman7920, that is very clever. Maybe this should be added to https://cgit.kde.org/scratch/brauch/appimage-exec-wrapper.git/ (discussed at https://github.com/AppImage/AppImageKit/issues/396)?

probonopd avatar Jun 18 '18 17:06 probonopd

So sad that Qt 5.2.1 looked correct on Gtk+ based systems like Xubuntu 18.04 but newer Qt versions don't, anymore.

native

Qt regression, really?! Can anyone confirm that things actually worsened?

probonopd avatar Jun 18 '18 19:06 probonopd

From Qt 5.6+ full version definition is embedded in library. On start if (plugin_version <= this.version) try_load(plugin)

objdump -p libQt5Gui.so.5

Version definitions: 1 0x01 0x04de00d5 libQt5Gui.so.5 2 0x00 0x0dcbd2c9 Qt_5_PRIVATE_API 3 0x00 0x00058a25 Qt_5 4 0x02 0x058a2810 Qt_5.0 Qt_5 5 0x02 0x058a2811 Qt_5.1 Qt_5.0 6 0x02 0x058a2812 Qt_5.2 Qt_5.1 7 0x02 0x058a2813 Qt_5.3 Qt_5.2 8 0x02 0x058a2814 Qt_5.4 Qt_5.3 9 0x02 0x058a2815 Qt_5.5 Qt_5.4 10 0x02 0x058a2816 Qt_5.6 Qt_5.5 11 0x02 0x058a2817 Qt_5.7 Qt_5.6 12 0x02 0x058a2818 Qt_5.8 Qt_5.7 13 0x02 0x058a2819 Qt_5.9 Qt_5.8 14 0x02 0x08a28110 Qt_5.10 Qt_5.9 15 0x00 0x08a28111 Qt_5.11 Qt_5.10

sandman7920 avatar Jun 18 '18 20:06 sandman7920

Also FILE_TO_RUN.wrapper causes strange behavior inside AppImage ENV["APPIMAGE"] return /path/to/Application.AppImage.wrapper

This was bug inside application.AppImage, not runtime

sandman7920 avatar Jun 18 '18 20:06 sandman7920

This is a nightmare, on some system "KDEPlasmaPlatformTheme.so" cannot be loaded due QImage ABI change, on some is loaded and finally on Kubuntu 18.04 shipped with Qt 5.9.5 my app is with 5.11.0

Cannot mix incompatible Qt library (version 0x50905) with this library (version 0x50b00) Aborted (core dumped)

sandman7920 avatar Jun 19 '18 09:06 sandman7920

Progress. In order plugins to be loaded correctly all Qt5 libraries (preferably from qt.io 5.11.0 GLIBC_2.17 compatible) used by them must be shipped with AppImage.

For gtk "platformthemes/libqgtk3.so" plugin must be bundled (preferred from qt.io)

For KDE "platformthemes/KDEPlasmaPlatformTheme.so" (do not bundle this plugin)

libQt5Concurrent.so.5 libQt5Core.so.5 libQt5DBus.so.5 libQt5Gui.so.5 libQt5Network.so.5 libQt5PrintSupport.so.5 libQt5Qml.so.5 libQt5QuickControls2.so.5 libQt5Quick.so.5 libQt5QuickTemplates2.so.5 libQt5Script.so.5 libQt5Svg.so.5 libQt5TextToSpeech.so.5 libQt5Widgets.so.5 libQt5X11Extras.so.5 libQt5Xml.so.5

must be bundled

I will attach list with all libraries used by KDE plugins (extracted from Arch).

Maybe will be a good idea linuxdeployqt to have something like -bundle-kde-deps

Note: libQt5Script is deprecated (used by kio->kded plugin) and must be installed separated

On clean ubuntu/kubuntu install qtpaths returns could not find a Qt installation of https://github.com/AppImage/AppImages/issues/88#issuecomment-398047936

kde_qt5_theme_list.txt kde_qt5_full_list.txt kde_plugins_so.txt

sandman7920 avatar Jun 19 '18 12:06 sandman7920

Screenshots

Ubuntu 14.04

Unity unity_ubuntu_14 04

KDE4 kde4_ubuntu_14 04

Ubuntu 16.04

Gnome gnome_ubuntu_16 04

KDE kde_ubuntu_16 04

Ubuntu 18.04

Gnome gnome_ubuntu_18 04

KDE kde_ubuntu_18 04

sandman7920 avatar Jun 19 '18 12:06 sandman7920

Hi @sandman7920 that looks awesome. Can you achieve the same result when using Qt from https://launchpad.net/~beineri?

probonopd avatar Jun 19 '18 16:06 probonopd

All Qt version should be fine as long they are newer than Qt on client machine.

new wrapper https://github.com/sandman7920/AppImageQt5run/releases

To run usr/bin/MyQt5Executable

cd AppDir
cp /some/path/appimage.qt5run usr/bin/MyQt5Executable.qt5run
ln -s usr/bin/MyQt5Executable.qt5run AppRun

This is c++ wrapper linked with Qt 5.1.1. Wrapper make call to QCoreApplication::libraryPaths() function and return system path. Executable must be compiled without RPATH/RUNPATH

main.cpp

#include <QCoreApplication>
#include <QStringList>
#include <iostream>
#include <string>
#include <unistd.h>

int main(int /*argc*/, char *argv[], char **envp) {
    char *real_path;
    char *appDir = getenv("APPDIR");

    if (appDir != nullptr) {
        real_path = realpath(std::string(appDir).append("/AppRun").c_str(), nullptr);
    } else {
        real_path = realpath(argv[0], nullptr);
    }

    if (real_path == nullptr) return 1;

    const std::string self(real_path);

    size_t pos = self.find(".qt5run");
    if (pos == std::string::npos) return 1;

    real_path[pos] = '\0';
    argv[0] = real_path;

    pos = self.find_last_of('/');
    const std::string appPath = (pos != std::string::npos) ? self.substr(0, pos) : "";

    std::string qt_plugins = std::string(appPath).append("/../plugins");
    for (auto const &qstring: QCoreApplication::libraryPaths()) {
        qt_plugins.push_back(':');
        qt_plugins.append(qstring.toStdString());
    }

    setenv("QT_PLUGIN_PATH", qt_plugins.c_str(), 1);

    if (getenv("QT5_DEBUG_RUN") != nullptr) {
        std::cerr << "QT_PLUGIN_PATH: " << qt_plugins << '\n'
                  << "EXECUTABLE: " << argv[0] << std::endl;
    }

    return execve(argv[0], argv, envp);
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(AppImageQt5run)

set(APP_NAME "appimage.qt5run")
set(CMAKE_SKIP_BUILD_RPATH  TRUE)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/bin")

set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -fPIC -DQT_CORE_LIB")
if(LINK_RUNTIME)
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
endif()

set(QT_ROOT "${CMAKE_CURRENT_LIST_DIR}/Qt_5.1.1")

include_directories(SYSTEM ${QT_ROOT}/include)
include_directories(SYSTEM ${QT_ROOT}/include/QtCore)
link_directories(${QT_ROOT}/lib)

add_executable(${APP_NAME} "main.cpp")
target_link_libraries(${APP_NAME} -Wl,--rpath-link=${QT_ROOT}/lib -lQt5Core)

sandman7920 avatar Jun 19 '18 20:06 sandman7920

Can you make an AppImage of an example Qt application on Travis CI using Qt from https://launchpad.net/~beineri?

probonopd avatar Jun 19 '18 20:06 probonopd

Tomorrow :) I need some sleep (it's midnight here)

sandman7920 avatar Jun 19 '18 21:06 sandman7920

Last wrapper has fundamental logic error. I am assuming all systems have Qt5 installed

sandman7920 avatar Jun 19 '18 21:06 sandman7920

I am assuming all systems have Qt5 installed

That, of course, is not something we can assume. Looking forward to the sample app, but no hurries.

probonopd avatar Jun 20 '18 05:06 probonopd

https://github.com/sandman7920/Qt5Demo

This demo is build on Ubuntu 16.04 with libraries from https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.10.1-xenial

I have build demos with Qt 5.10.1 on purpose. One can see the difference running demos on Ubuntu and then on Arch, Arch is with latest libraries 5.11.0 and demo apps don't have native look in plasma

Qt libraries from qt.io have less external dependencies

objdump -p qt_io/5.11.0/libQt5Gui.so.5|grep NEEDED
  NEEDED               libQt5Core.so.5
  NEEDED               libpthread.so.0
  NEEDED               libGL.so.1
  NEEDED               libz.so.1
  NEEDED               libstdc++.so.6
  NEEDED               libm.so.6
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6

objdump -p /usr/lib/libQt5Gui.so.5|grep NEEDED libpng16.so.16

  NEEDED               libQt5Core.so.5
  NEEDED               libpthread.so.0
  NEEDED               libGL.so.1
  NEEDED               libpng16.so.16
  NEEDED               libharfbuzz.so.0
  NEEDED               libz.so.1
  NEEDED               libstdc++.so.6
  NEEDED               libm.so.6
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6

sandman7920 avatar Jun 21 '18 18:06 sandman7920

Qt libraries from qt.io have less external dependencies

Interesting find!

Arch is with latest libraries 5.11.0 and demo apps don't have native look in plasma

What happens if you bundle the KDE look&feel plugin but NOT its dependencies?

probonopd avatar Jun 22 '18 04:06 probonopd

I have rebuild KDEPlasmaPlatformTheme, kio with qt.5.11 and bundle it, but strange crashes starter to happen.

sandman7920 avatar Jun 22 '18 07:06 sandman7920

I have rebuild KDEPlasmaPlatformTheme, kio with qt.5.11 and bundle it, but strange crashes starter to happen.

Can you track them down? What is crashing, and why?

probonopd avatar Jun 22 '18 15:06 probonopd