PAYATHON 2026

如何定位并解决 VSCode 中 Python conda 环境的 Qt 库版本冲突?

排障赵师傅

这个错误说明 Python 进程中的 Qt 原生组件存在版本冲突。Matplotlib 使用 Qt 图形后端时会间接加载 PyQt,所以即使代码中没有 import PyQt5,执行 plt.imshow(t2) 也可能触发 Qt 初始化,导致进程退出。

排查时,先确认 VSCode 实际使用的 Python,再检查进程加载的 Qt DLL 和插件。conda list 只记录包的安装状态,不能据此判断报错中的 Qt 5.15.8 来自哪里。这个版本可能对应其他目录中的 Qt 组件,也可能是某个二进制组件编译时使用的 Qt 版本,不一定意味着机器上装了第二套完整的 Qt。

为什么会在 plt.imshow() 处崩溃?

Matplotlib 的绘图后端负责创建窗口和显示图像。如果当前后端是 QtAgg 或 Qt5Agg,第一次创建图形时就可能加载 Qt。因此,执行 plt.imshow(t2) 时可能触发后端初始化。

仅凭这个错误,无法判断 t2 的 NumPy 数据是否有问题,也无法认定 GDAL、rasterio 等某个导入就是冲突来源。

给出的环境列表有两处需要检查:

  • 环境中安装了 pyqt 5.15.11 和 qt-main 5.15.15。
  • 部分包来自 conda-forge。还需要核对其他包的渠道,确认 Qt 相关组件的构建组合是否兼容。

PyQt 和 Qt 是不同的项目,不能仅凭 5.15.11 与 5.15.15 这两个版本号不同,就判定它们不兼容。

先确认 VSCode 使用的解释器和后端

用出现问题时的运行方式执行以下代码。原来使用 Notebook,就在对应内核中检查;原来使用调试器,就使用同一调试配置。

import os
import sys

print("Python:", sys.executable, flush=True)
print("Prefix:", sys.prefix, flush=True)
print("CONDA_PREFIX:", os.environ.get("CONDA_PREFIX"), flush=True)

import matplotlib

print("Matplotlib:", matplotlib.__file__, flush=True)
print("matplotlibrc:", matplotlib.matplotlib_fname(), flush=True)
print("Backend:", matplotlib.get_backend(), flush=True)

预期的解释器路径是:

C:\Users\jp\AppData\Local\miniconda3\envs\netcdf\python.exe

如果输出的路径不一致,在 VSCode 中通过 Python: Select Interpreter 选择该环境。Notebook 还需要单独选择对应的内核。

CONDA_PREFIX 可以辅助判断终端是否激活了环境,实际运行的是哪个 Python 则需要结合 sys.executable 确认。终端、调试器和 Notebook 内核可能使用不同的启动环境。

部分 Matplotlib 版本在查询自动选择的后端时,也可能触发后端加载。如果这段代码提前退出,记下最后一条输出,再继续检查 DLL 和插件路径。

定位 Qt 5.15.8 的来源

查看 PyQt 模块、编译版本和运行时版本

在新的 Python 进程中单独运行:

from PyQt5 import QtCore

print("QtCore module:", QtCore.__file__, flush=True)
print("PyQt version:", QtCore.PYQT_VERSION_STR, flush=True)
print("Qt build version:", QtCore.QT_VERSION_STR, flush=True)
print("Qt runtime version:", QtCore.qVersion(), flush=True)
print(
    "Qt plugins:",
    QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PluginsPath),
    flush=True,
)

各项输出的含义是:

  • QtCore.__file__:加载的 Python 扩展模块的路径。
  • QT_VERSION_STR:该绑定编译时使用的 Qt 版本。
  • qVersion():当前加载的 Qt 运行库版本。
  • PluginsPath:Qt 配置中的插件目录。

如果编译版本和运行时版本分别对应报错中的两个版本,可以沿着这条线索继续排查。不过,仅凭版本字符串不同,还无法确定哪个 DLL 有问题,需要结合实际加载路径判断。

如果导入 QtCore 时就崩溃,跳过这一步,改用下面的日志和进程监控方法。

检查环境变量和磁盘上的候选文件

在已激活 netcdf 环境的 PowerShell 中执行以下命令:

conda activate netcdf

Get-Item Env:CONDA_PREFIX,Env:PYTHONPATH,Env:QT_PLUGIN_PATH,Env:QT_QPA_PLATFORM_PLUGIN_PATH,Env:QT_API,Env:MPLBACKEND -ErrorAction SilentlyContinue

$env:PATH -split ';'

检查输出中是否包含其他 conda 环境、独立 Qt 安装或其他软件的目录。尤其要检查 QT_PLUGIN_PATH 和 QT_QPA_PLATFORM_PLUGIN_PATH,它们可能把插件搜索路径指向当前环境之外。

接着搜索当前环境中的常见组件:

Get-ChildItem -Path $env:CONDA_PREFIX -Recurse -File `
    -Include Qt5Core.dll,Qt5Gui.dll,Qt5Widgets.dll,qwindows.dll,qt.conf `
    -ErrorAction SilentlyContinue |
    Select-Object FullName,
        @{Name='FileVersion'; Expression={$_.VersionInfo.FileVersion}}

优先检查这些位置:

<环境目录>\Library\bin
<环境目录>\Library\plugins
<环境目录>\Lib\site-packages\PyQt5

不同发行方式的目录布局可能不同。pip 分发的绑定可能在 site-packages 内携带 Qt 文件,conda 包则可能把运行库和插件放在 Library 下。

也要检查项目目录,以及 PATH 中其他软件的目录。如果发现 qt.conf,查看它是否把插件路径重定向到了其他位置。

还可以用以下命令辅助查找:

where.exe Qt5Core.dll

它只检查当前目录和 PATH 等范围,无法覆盖所有 DLL 加载机制。因此,没有输出不能排除冲突,找到某个文件也不代表进程实际加载了它。

用插件日志和实际加载路径确认来源

启用 Qt 插件诊断,重新运行能复现问题的脚本:

$env:QT_DEBUG_PLUGINS = "1"
python -u .\plot_test.py 2> .\qt-plugins.log
Remove-Item Env:QT_DEBUG_PLUGINS

plot_test.py 中应保留能触发崩溃的代码。检查日志中的以下信息:

  • Qt 搜索了哪些插件目录。
  • 找到了哪些 qwindows.dll 等插件。
  • 是否出现当前 conda 环境之外的路径。
  • 是否记录了插件版本或加载失败信息。

QT_DEBUG_PLUGINS 主要用于诊断插件加载。如果日志仍无法解释冲突,可以使用 Windows 的 Process Monitor:

  1. 按运行脚本的 python.exe 进程 PID 过滤。
  2. 查看 Load Image 事件。
  3. 检查 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll 和 Qt 插件的完整路径。

如果进程能够保持运行,也可以用 Process Explorer 查看已加载的 DLL。对于瞬间退出的进程,Process Monitor 更方便保留崩溃前的加载记录。

判断冲突来源需要查看相关组件的实际路径。文件的版本属性只能辅助判断,有些二进制文件并不提供完整的版本信息。

根据定位结果修复

修正解释器或外部路径干扰

如果 DLL 或插件来自其他环境、独立 Qt 安装或其他软件目录,需要修正对应的启动配置。

检查 VSCode 的 .env、.vscode/launch.json、终端配置,以及用户和系统环境变量。如果某个 Qt 路径变量确实指向无关目录,可以先在当前 PowerShell 会话中清除,测试是否恢复正常:

Remove-Item Env:QT_PLUGIN_PATH -ErrorAction SilentlyContinue
Remove-Item Env:QT_QPA_PLATFORM_PLUGIN_PATH -ErrorAction SilentlyContinue

这只影响当前终端及其启动的子进程。VSCode 中已经运行的 Python、调试会话或 Notebook 内核不会自动更新,需要重新启动。

不要直接把某个 qwindows.dll 或 Qt5Core.dll 复制到其他目录中覆盖。Qt 的多个 DLL、插件及其依赖需要配套,单独替换容易造成新的加载错误。

核对包渠道和安装位置

先查看完整的渠道信息:

conda list --show-channel-urls

再查看 Python 包的安装位置:

python -m pip show PyQt5 PyQt5-Qt5 PySide2 PySide6

未安装的包会提示找不到。pip show 有时也能显示由 conda 安装的 Python 包,因此不能仅凭它的输出就认定曾经通过 pip 安装过这些包。

如果确认 Qt 相关包来自不同渠道,或当前环境曾被多次覆盖安装,通常可以创建一个使用单一渠道的新环境。这比逐个猜测版本更容易验证。

创建一致的新环境

保留原环境,先导出环境记录:

conda env export -n netcdf > netcdf-before.yml

然后创建新环境,让求解器选择兼容的依赖组合:

conda create -n netcdf-clean --override-channels -c conda-forge --strict-channel-priority python numpy pandas xarray rioxarray rasterio gdal netcdf4 matplotlib pyqt

这个命令没有固定 Python 和 Qt 的版本。如果项目要求保留 Python 3.14,可以把 python 改成 python=3.14;能否求解成功,取决于当时渠道提供的包。如果求解失败,需要根据依赖冲突调整约束,不要强行拼装不同来源的 DLL。

激活新环境后,先运行最小绘图脚本:

import numpy as np
import matplotlib.pyplot as plt

plt.imshow(np.arange(100).reshape(10, 10))
plt.colorbar()
plt.show()

然后逐步加入原来的导入语句,每次测试都启动新的 Python 进程。如果单独绘图正常,加入某个导入后才崩溃,就继续检查该导入涉及的原生组件。改变导入顺序可以提供线索,但不能代替依赖修复。

最后,在 VSCode 中选择新环境,并重启运行会话。

只需要输出图片时,可以先绕过 Qt

如果只需要保存图片,可以在导入 pyplot 之前使用 Agg 后端:

import matplotlib
matplotlib.use("Agg")

import matplotlib.pyplot as plt

plt.imshow(t2)
plt.colorbar()
plt.savefig("result.png", dpi=150, bbox_inches="tight")
plt.close()

在新启动的进程中执行这段代码。如果其他代码已经导入了 pyplot 或初始化了 Qt,再切换后端可能无法消除冲突。

保留原有的数据处理代码和导入语句后,如果使用 Agg 能成功保存图片,就可以进一步检查交互式图形后端及其依赖。Agg 不提供交互窗口,也不会修复环境中已有的 Qt 冲突。

备注:内容仅供参考。