Python: use PyQt6 within an virtual environment (venv)

Introduction: Until today, I was not able to work with Python and PyQt6 inside a virtual environment in nixOS. No matter what I tried, I was stuck at errors like can’t find libgl.so.1 shared object. I need a venv because of pinned dependencies and external repos from github which are not in nixpkgs.


Solution: However, today I might found the solution and want to share it because it may be helpful:

  1. Create a empty FHSEnv (shell-fhs.nix).

Hint: The FHS is needed for a lot of python packages (which are not purely python) in order to be able to be installed insite a venv.

# shell-fhs.nix
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSEnv {
  name = "PyQt6";
  targetPkgs = pkgs: (with pkgs; []);
  multiPkgs = pkgs: (with pkgs; []);
  profile = ''
    export LIBRARY_PATH=/usr/lib:/usr/lib64:$LIBRARY_PATH
  '';
  runScript = "bash";
}).env
  1. Create a normal shell with PyQt6, from there start the FHSEnv (shell.nix)
# shell.nix
{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.python3
    pkgs.python3Packages.pyqt6
  ];
}
nix-shell shell.nix --command "nix-shell shell-fhs.nix"
  1. Create a .venv with system-side-packages and confirm PyQt is there
python -m venv --system-site-packages .venv
. .venv/bin/activate
pip list
# Should output something like
Package     Version
----------- -------
dbus-python 1.4.0
pip         25.0.1
PyQt6       6.9.0
PyQt6_sip   13.8.0
  1. Test it
from PyQt6.QtWidgets import QApplication, QLabel
app = QApplication([])
label = QLabel("Hello from PyQt6 on NixOS!")
label.show()
app.exec()

From there, you will be free to install other packages via pip inside your venv


I hope this helps, please share your thoughts and experience with this hack.

To solve these kinds of problems I use nix-ld. And for Python-related problems it allows me to stay away from nix-shell solutions, including FHS environments, entirely. Provided I use uv to manage my Python installation, instead of Nix.

For this, the related parts of my nixos-config look like below.

{ options, pkgs, ... }:
{
  environment.systemPackages = [ pkgs.uv ];
  programs.nix-ld = {
    enable = true;
    libraries = options.programs.nix-ld.libraries.default ++ (
      with pkgs; [
        dbus # libdbus-1.so.3
        fontconfig # libfontconfig.so.1
        freetype # libfreetype.so.6
        glib # libglib-2.0.so.0
        libGL # libGL.so.1
        libxkbcommon # libxkbcommon.so.0
        xorg.libX11 # libX11.so.6
        wayland
      ]
    );
  };
}

Finally, I install both the Python interpreter and Python packages using uv, e.g.

$ uv python install 3.13 --default --preview
$ uv run --with pyqt6 python
Python 3.13.5 (main, Jun 26 2025, 21:20:04) [Clang 20.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from PyQt6.QtCore import pyqtProperty, QCoreApplication, QObject, QUrl
>>> 

If you wonder about the above command: uv run --with is an alternative (read: “modern way”) to creating a virtual environment and installing packages manually. You can, of course, do it the traditional way with Python venv and the like.

3 Likes

Thanks, Peter, that sounds like a neat solution. I definitely need to try out nix-ld. It seems to solution for a lot of my problems with nix. (Although I heard some bad rants about nix-ld from the die-hard users, but I sometimes prefer the pragmatic solutions so stuff just works)

@bittner when setting nix-ld according to your example, there are still some libraries missing:

    from PyQt6.QtWebEngineCore import QWebEngineNavigationRequest
ImportError: libxcb-dri3.so.0: cannot open shared object file: No such file or directory

which means (how i understand it) i have find the correct libxcb in nixpkgs, need to include it into nix.ld defaults and rebuild my system. Probably only the find the next library missing… this is a really annoying loop. Do you have any tip on how to speed that up?

There is probably no magic bullet, I’m afraid. It’s the usual process of getting some software running on a new environment that behaves by its own rules.

Usually, there is a package having the name of the first part of the shared object file (e.g. libxcb in your specific case). The NixOS Wiki article I linked to above (nix-ld) has a long list of libraries to cover all kinds of use cases.

You can also take a look at how I use it with less dependencies; pkgs.xorg.libxcb is also there. I don’t really know, whether the listed dependencies are actually installed even if you don’t need them (I’m a bit worried about such unintended side-effects). Maybe there’s someone reading these lines who can tell us more about the inner workings of nix-ld. :crossed_fingers: :eyes:

EDIT: Some nice background reading is Nix-ld: A clean solution for issues with pre-compiled executables on NixOS | ~/git/blog by @Mic92.

2 Likes

If your working with a given binary file, I suspect there’s a nix index command you could pipe or script with the ldd command to enumerate all the next packages that correspondingly include the shared libraries listed.

Although, I’m not sure that helps much in the case when importing libraries at run time from an imperative language like python…

There is one important addition that I’d like to make, which is related to Qt specifically. If you encounter an error like this:

qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, vnc, wayland-egl, wayland, wayland-xcomposite-egl, wayland-xcomposite-glx, webgl, xcb.

… and nothing else, then set the environment variable QT_DEBUG_PLUGINS=1 before running the application again, e.g.

export QT_DEBUG_PLUGINS=1

This will allow you to get more error output that is otherwise suppressed. This is usually, “Cannot load library …” and “cannot open shared object file: No such file or directory”, so again the same symptoms, but you wouldn’t know otherwise. :bulb:

Still no success with with flakes (at least not when pyqt6-webengine is included), even after hours including the xth libraries and debugging…

But my newest approach works with setting PYTHONHOME (thanks to the idea from here):

  1. Make nix-shell including packages: nix-shell -p 'python312.withPackages (p: [ p.pyqt6 p.pyqt6-webengine ])'
  2. Create venv including site packages: python -m venv --system-site-packages .venv
  3. Activate: . .venv/bin/activate
  4. Get derivation path: which python (e.g. /nix/store/am5...-python3-3.12.12-env/bin/python)
  5. Set PYTHONHOME to the env: export PYTHONHOME=/nix/store/am5...-python3-3.12.12-env (excluding /bin/python)
  6. Voila! Feel free to install additional packages with pip