OFFSET偏移
← 文章目录OFFSET / FIELD NOTESNO. 089

Python、uv 与 Matplotlib/Qt 的依赖边界

以 Qt5Agg 和 PyPI wheel 排错为例,追踪 Python 包、系统库与插件路径。

所在专题Nix 开发环境与二进制兼容 ↗
约 6 分钟阅读开始阅读

技术栈:nix-shell + uv + direnv

主要问题:Python 编译问题;matplotlib 作图问题。

shell.nix 配置示例#

{ pkgs ? import <nixpkgs> {} }:

pkgs.mkShell{
  buildInputs = with pkgs; [
    (python312.withPackages (ps: with ps; [
      matplotlib
      pyqt5  # Qt5Agg 所需 Python binding
    ]))
    qt5.qtwayland
    qt5.qtbase
    uv
  ];

  shellHook = ''
    export LD_LIBRARY_PATH=${pkgs.lib.makeLibraryPath [
      pkgs.stdenv.cc.cc.lib
      pkgs.zlib
      pkgs.libGL
      pkgs.qt5.qtbase
      pkgs.glib
    ]}
    export MPLBACKEND=Qt5Agg
    export QT_PLUGIN_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.qtPluginPrefix}
    export QT_QPA_PLATFORM_PLUGIN_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.qtPluginPrefix}/platforms
  '';
}

如需在代码中覆盖后端,应在导入 pyplot 之前指定;已设置 MPLBACKEND 时不必重复:

import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt

查找缺失依赖的方法#

1. nix-locate(按 .so 文件名反查包名)

nix-locate --whole-name libgthread-2.0.so.0

需要先装 nix-index 并建立索引:nix-index(第一次运行要几分钟)。

2. nixpkgs 网站搜索

包搜索主要检索软件包元数据,不是完整文件索引;知道候选包名后在这里确认属性:search.nixos.org

3. ldd 诊断 PyPI 包依赖

仅对可信二进制使用 ldd;对不可信文件先用 readelf -d 或 objdump -p 静态查看依赖。路径随 Python 和 PyQt 版本变化。

ldd .venv/lib/python3.12/site-packages/PyQt5/QtWidgets.so | grep "not found"

一次性列出所有缺失的 .so,再用 nix-locate 批量查。

推荐流程:ldd 找全缺失库 → nix-locate 查包名 → 一次性加进 shell.nix

阶段性总结(2026-04-08)#

此记录中的核心问题是 PyPI wheel、Qt 插件和 Nix 提供的系统库混合后,搜索路径与 ABI 不一致;这不是 uv 与 Nix 必然冲突。

  • uv 的设计哲学:通常把 Python 依赖解析并安装到 .venv;带原生扩展的 wheel 仍可能依赖环境提供的系统库。
  • nix 的设计哲学:所有依赖在 nix store(/nix/store/…),不在标准系统路径。

在纯 Python 包上没有冲突,但 matplotlib+Qt 链需要系统库:

matplotlib (PyPI)
  └── PyQt5
        └── libQt5XcbQpa.so (Qt 运行时)
              └── libqxcb.so (platform plugin)
                    └── libxcb, libX11, libxkbcommon…(全在 nix store)

踩到的坑:

  1. PyPI 的 PyQt5 自带 platform plugin,但缺几乎所有 X11 库
  2. 加进 LD_LIBRARY_PATH 后版本不匹配,出现 undefined symbol
  3. 把 PyQt5 移入 nix 管理,但 uv 的 .venv 覆盖了 PATH
  4. 用 --python $(which python3) 指定 nix 的 python,但 matplotlib 还在 venv 里

这份排错记录采用的收敛方案:把 matplotlib、PyQt 和 Qt5 系统库交给同一 Nixpkgs 管理,降低 ABI 混用。uv 也能用于含原生依赖的项目,但需要正确的动态库环境、wheel 兼容性与 Python 路径策略;不能仅靠全局叠加 LD_LIBRARY_PATH。

版本与两套依赖路径#

本篇保留 2026-04-08 记录里的 Python 3.12 / Qt5 示例;后端、插件路径和包属性以所用版本为准。纯 withPackages 路线直接使用该 Python;若另建 uv 虚拟环境,要明确哪些包来自 PyPI、哪些系统库来自 Nix,必要时设计 --system-site-packages,而不是让两套 Qt/PyQt 混在搜索路径里。nix-locate 查询前要建立或取得适配系统的文件索引。

参考与延伸

  1. Nix Python 开发环境 ↗
  2. Matplotlib 后端 ↗
  3. nix-ld 的 Python 边界 ↗

根据技术笔记重新整理,示例使用通用名称。

沿专题继续阅读

Nix 开发环境与二进制兼容

  1. 01Nix-shell、devShell 与 direnv 的递进用法
  2. 02Nix 开发环境:多语言、数据库与全栈示例
  3. 03Python、uv 与 Matplotlib/Qt 的依赖边界
  4. 04nix-ld:预编译 ELF、加载器与库路径
  5. 05Overlays、override 与 overrideAttrs
CONTINUE READING / 下一篇Nix 命令详解:构建、环境、profile 与系统

完整保留新旧 CLI、存储、系统安装和 Home Manager 命令,并校正易混参数。