Qt: Difference between revisions

Sandro (talk | contribs)
DHCP (talk | contribs)
m style fixes
 
(4 intermediate revisions by 4 users not shown)
Line 2: Line 2:


== Development ==
== Development ==
If you want to develop qt applications in nixos you have to use nix-shell or direnv.
{{warning|With the deprecation of <code>qt5.full</code> & <code>qt6.full</code>, for 25.11 and up (see [https://github.com/NixOS/nixpkgs/blob/32b61ba4d9088ef926dd27065daad604ca6b58aa/pkgs/development/libraries/qt-6/default.nix#L152 change]) this recommendation is outdated. For now please primarily refer to the [[#Explicit Dependencies|Explicit Dependencies]] Section below.}}
 
To develop Qt applications in NixOS you may use nix-shell or direnv.
For using nix-shell just run this command in the terminal:
For using nix-shell just run this command in the terminal:


<code>nix-shell -p qt5Full -p qtcreator --run qtcreator</code>
<syntaxhighlight lang=console>
$ nix-shell -p qt5Full -p qtcreator --run qtcreator
</syntaxhighlight>


Tip: if it finds no Qt Kits, <code>rm -rf ~/.config/QtProject*</code> and start again. Sometimes it finds a kit, but cannot find a suitable qt version for it, in this case you can also type <code>which qmake</code> in your nix-shell and add a new entry in the <code>QT-Versions</code> tab in <code>Tools->Options->Kits</code>.
Tip: if it finds no Qt Kits, <code>rm -rf ~/.config/QtProject*</code> and start again. Sometimes it finds a kit, but cannot find a suitable qt version for it, in this case you can also type <code>which qmake</code> in your nix-shell and add a new entry in the <code>QT-Versions</code> tab in <code>Tools->Options->Kits</code>.
Line 11: Line 15:
For using direnv, create a '''shell.nix''' file in the root of your project and paste these lines into it:
For using direnv, create a '''shell.nix''' file in the root of your project and paste these lines into it:


<syntaxHighlight lang=nix>
{{file|shell.nix|nix|3=
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
{ pkgs ? import <nixpkgs> {} }:
   pkgs.mkShell {
   pkgs.mkShell {
Line 20: Line 23:
     ];
     ];
}
}
</syntaxHighlight>
}}
 
Also create '''.envrc''' file and paste: <code>use_nix</code> into it.


Happy qt coding :)
Also, create '''.envrc''' file and paste: <code>use_nix</code> into it.


=== Explicit Dependencies ===
=== Explicit Dependencies ===


However if fetching the entirety of <code>pkgs.qt6.full</code> is not appealing and you know which parts of Qt you need, your first instinct might be adding something like <code>pkgs.qt6.qtdeclarative</code> for creating QML-based Qt programs to <code>buildInputs</code>, '''however''' that will not work and you will get compile errors for missing libraries. <code>pkgs.qt6.full</code> is actually [https://github.com/NixOS/nixpkgs/blob/nixos-24.11/pkgs/development/libraries/qt-6/default.nix#L94-L144 creating an environment that contains all Qt libraries] that allows <code>qmake</code> and tools to find those libraries, so you must do the same and <code>pkgs.qt6.env</code> will help make one. For example:
If fetching the entirety of <code>pkgs.qt6.full</code> is not appealing and you know which parts of Qt you need, your first instinct might be adding something like <code>pkgs.qt6.qtdeclarative</code> for creating QML-based Qt programs to <code>buildInputs</code>, '''however''' that will not work and you will get compile errors for missing libraries. <code>pkgs.qt6.full</code> is actually [https://github.com/NixOS/nixpkgs/blob/nixos-24.11/pkgs/development/libraries/qt-6/default.nix#L94-L144 creating an environment that contains all Qt libraries] that allows <code>qmake</code> and tools to find those libraries, so you must do the same and <code>pkgs.qt6.env</code> will help make one. For example:


<syntaxhighlight lang="nix">
{{file|shell.nix|nix|3=
# shell.nix
{ pkgs ? import <nixpkgs> {} }:
{ pkgs ? import <nixpkgs> {} }:
let
let
Line 49: Line 49:
     ];
     ];
}
}
</syntaxhighlight>
}}


== Packaging ==
== Packaging ==
Line 132: Line 132:


Call it with
Call it with
<syntaxHighlight>
<syntaxhighlight lang=nix>
{ pkgs ? import <nixpkgs> {} }:
{ pkgs ? import <nixpkgs> {} }:
pkgs.libsForQt5.callPackage ./derivation.nix {}
pkgs.libsForQt5.callPackage ./derivation.nix {}
</syntaxHighlight>
</syntaxhighlight>


For actual python applications, you may also use something like that (to test) :
For actual python applications, you may also use something like that (to test) :
<syntaxHighlight>
<syntaxHighlight lang=nix>
python3.pkgs.buildPythonApplication {
python3.pkgs.buildPythonApplication {
   pname = "blabla";
   pname = "blabla";
Line 189: Line 189:


You can find [https://gist.github.com/tobiasBora/04d0febda0b3f09707b5e1b7b85390a5 here] a minimal example to use QML, cmake, nix and Qt5, and [https://gist.github.com/tobiasBora/6f114cca1affb5528c872ca01d7e28c1 here] is the same example with qmake instead and [https://gist.github.com/tobiasBora/812701e8741814393f3df7b23a11eb4b here] is the same with meson instead. There is nothing special to nix there, but note that if you provide the qml file using something like <code>qrc:///main.qml</code>, then you need to write a qrc file that lists all the resources that must be included in the qt resource manager. This file is then used to compile the resources and include them in the binary (you have to compile the binaries, either automatically with cmake or qmake, or manually using rcc). With cmake you compile it using <code>qt5_add_resources(SOURCES qml.qrc)</code> ([https://doc.qt.io/qt-5/qtcore-cmake-qt5-add-resources.html doc]) as illustrated in the above example (make sure to use a variable as the source and to reuse the same variable in <code>add_executable</code>).
You can find [https://gist.github.com/tobiasBora/04d0febda0b3f09707b5e1b7b85390a5 here] a minimal example to use QML, cmake, nix and Qt5, and [https://gist.github.com/tobiasBora/6f114cca1affb5528c872ca01d7e28c1 here] is the same example with qmake instead and [https://gist.github.com/tobiasBora/812701e8741814393f3df7b23a11eb4b here] is the same with meson instead. There is nothing special to nix there, but note that if you provide the qml file using something like <code>qrc:///main.qml</code>, then you need to write a qrc file that lists all the resources that must be included in the qt resource manager. This file is then used to compile the resources and include them in the binary (you have to compile the binaries, either automatically with cmake or qmake, or manually using rcc). With cmake you compile it using <code>qt5_add_resources(SOURCES qml.qrc)</code> ([https://doc.qt.io/qt-5/qtcore-cmake-qt5-add-resources.html doc]) as illustrated in the above example (make sure to use a variable as the source and to reuse the same variable in <code>add_executable</code>).


== Troubleshooting ==
== Troubleshooting ==


=== This application failed to start because it could not find or load the Qt platform plugin ??? in "" ===
=== This application failed to start because it could not find or load the Qt platform plugin ??? in "" ===
==== qt5 ====
 
{{warning|This recommendation is deprecated for 19.09 and up, see {{issue|65399}}. Failing packages should be updated to use <code>wrapQtAppsHook</code>.}}
{{warning|This recommendation is deprecated for 19.09 and up, see {{issue|65399}}. Failing packages should be updated to use <code>wrapQtAppsHook</code>.}}
Qt5 seems (?) to look for plugins in the <code>PATH</code>. This will fail from a systemd user unit for example, because their path is nearly empty by default. As an example, here is a workaround to have <code>usbguard-applet</code> launched from a systemd user unit:
{{File|/etc/nixos/configuration.nix|nix|<nowiki>
  systemd.user.services.usbguard-applet = {
    description = "USBGuard applet";
    partOf = [ "graphical-session.target" ];
    wantedBy = [ "graphical-session.target" ];
    path = [ "/run/current-system/sw/" ]; ### Fix empty PATH to find qt plugins
    serviceConfig = {
      ExecStart = "${pkgs.usbguard}/bin/usbguard-applet-qt";
    };
  };
</nowiki>}}
====  qt.qpa.plugin: Could not find the Qt platform plugin "xcb" in "" ====
Here is a concrete example:


<pre>
<pre>
Line 220: Line 203:
The package will need to be fixed to use [the new https://github.com/NixOS/nixpkgs/issues/65399 <code>wrapQtAppsHook</code>]. The hook wraps every qt application with adding <code>QT_PLUGIN_PATH</code> and <code>XDG_DATA_DIRS</code> as well as <code>XDG_CONFIG_DIRS</code>.See [https://github.com/NixOS/nixpkgs/blob/nixos-19.09/pkgs/development/libraries/qt-5/hooks/wrap-qt-apps-hook.sh wrap-qt-apps-hook.sh in nixpkgs]
The package will need to be fixed to use [the new https://github.com/NixOS/nixpkgs/issues/65399 <code>wrapQtAppsHook</code>]. The hook wraps every qt application with adding <code>QT_PLUGIN_PATH</code> and <code>XDG_DATA_DIRS</code> as well as <code>XDG_CONFIG_DIRS</code>.See [https://github.com/NixOS/nixpkgs/blob/nixos-19.09/pkgs/development/libraries/qt-5/hooks/wrap-qt-apps-hook.sh wrap-qt-apps-hook.sh in nixpkgs]


==== Debugging methods ====
=== Debugging methods ===
As a general rule, exporting <code>QT_DEBUG_PLUGINS=1</code> make qt print where it looks for plugins.
As a general rule, exporting <code>QT_DEBUG_PLUGINS=1</code> make qt print where it looks for plugins.


If a plugin exists in a directory but is ignored with a message like <code>QLibraryPrivate::loadPlugin failed on "/nix/store/...-teamspeak-client-3.1.6/lib/teamspeak/platforms/libqxcb.so" : "Cannot load library /nix/store/...-client-3.1.6/lib/teamspeak/platforms/libqxcb.so: "</code> it can be that the library cannot be <code>dlopen()</code>ed because of dependencies/rpath issues and needs <code>patchelf</code>ing. Exporting <code>LD_DEBUG=libs</code> may prove helpful in this scenario.
If a plugin exists in a directory but is ignored with a message like <code>QLibraryPrivate::loadPlugin failed on "/nix/store/...-teamspeak-client-3.1.6/lib/teamspeak/platforms/libqxcb.so" : "Cannot load library /nix/store/...-client-3.1.6/lib/teamspeak/platforms/libqxcb.so: "</code> it can be that the library cannot be <code>dlopen()</code>ed because of dependencies/rpath issues and needs <code>patchelf</code>ing. Exporting <code>LD_DEBUG=libs</code> may prove helpful in this scenario.