2025, Oct 21 11:00

Resolve PyInstaller 'No module named matplotlib' when packaging a PyQt6 app into a one-file Windows EXE

Learn why PyInstaller fails with 'No module named matplotlib' in PyQt6 one-file Windows builds and how to fix it by installing matplotlib and bundling deps.

Packaging a small PyQt6 desktop app into a single Windows executable can stumble on a deceptively simple pitfall: a missing dependency at build time. Even when you wire up hidden imports and collect-all flags, PyInstaller cannot bundle libraries that are not actually installed in the environment. The result is a runtime failure with a clear message: No module named matplotlib.

Reproducing setup and the failing build

The application targets a one-file .exe using PyInstaller with these dependencies:

PyQt6==6.9.1
PyQt6-Qt6==6.9.1
PyQt6_sip==13.10.2
qt-material==2.17
pyinstaller==6.14.2

The PyInstaller spec wires in hidden imports for matplotlib and its backends:

# -*- mode: python ; coding: utf-8 -*-
graph = Analysis(
    ['DSS.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=['matplotlib', 'matplotlib.backends.backend_tkagg', 'matplotlib.backends.backend_pdf'],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=['PyQt5'],
    noarchive=False,
    optimize=0,
)
archive = PYZ(graph.pure)
app_exe = EXE(
    archive,
    graph.scripts,
    graph.binaries,
    graph.datas,
    [],
    name='DNA_Sequence_Similarities',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=False,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon=['icon.ico'],
)
bundle_collect = COLLECT(
    app_exe,
    graph.binaries,
    graph.zipfiles,
    graph.datas,
    strip=False,
    upx=True,
    upx_exclude=[],
    name='DNA_Sequence_Similarities',
)

The build command also forces hidden imports and attempts to collect packages:

pyinstaller DSS.py 
  --name DNA_Sequence_Similarities 
  --onefile 
  --windowed 
  --icon=icon.ico 
  --hidden-import=matplotlib 
  --hidden-import=matplotlib.backends.backend_tkagg 
  --hidden-import=matplotlib.backends.backend_pdf 
  --collect-all matplotlib 
  --collect-all qt_material 
  --collect-all PyQt6

Despite that, the executable fails on target machines with No module named matplotlib. The build log corroborates the issue: Hidden import 'matplotlib.backends.backend_tkagg' not found and Hidden import 'matplotlib.backends.backend_pdf' not found.

What actually goes wrong

PyInstaller analyzes the current Python environment and freezes the imports it can resolve. If a library is not installed at build time, there is nothing to bundle. In the shown setup, matplotlib is not part of the dependency list and therefore not installed in the environment used for building. That makes the hidden-import flags ineffective, because PyInstaller cannot find modules that do not exist locally.

The message is explicit during runtime, and the analysis stage repeats the same story: hidden imports for matplotlib backends cannot be located.

The fix

Install matplotlib in the build environment and include it in your project’s dependencies. Once the library is present, the existing spec and hidden-import configuration allow PyInstaller to discover and package it alongside the executable.

pip install matplotlib

The resulting dependency set becomes:

PyQt6==6.9.1
PyQt6-Qt6==6.9.1
PyQt6_sip==13.10.2
qt-material==2.17
pyinstaller==6.14.2
matplotlib

Rebuild the application using the same command and the executable will include matplotlib. The build log’s earlier errors about missing matplotlib backends will not appear once the package is available.

Why this matters

When shipping a standalone desktop app, every runtime import must exist in the environment at build time. Otherwise, PyInstaller cannot freeze it and the final binary will fail on machines without the dependency installed. This is especially important for GUI stacks like PyQt6 that commonly integrate with visualization libraries. The build must be self-contained, and that starts with a complete dependency list.

It is also worth paying attention to messages surfaced during the build. For example, the log indicates that qt_material must be imported after PySide or PyQt. Such messages help verify import order and packaging expectations early, before distribution.

Takeaways

Keep the build environment aligned with your application’s actual imports, ensure all required libraries are listed and installed, and then let PyInstaller do its job. If the log reports that a hidden import cannot be found, the most direct path to resolution is to make the module available in the environment, as shown with matplotlib.

The article is based on a question from StackOverflow by Maifee Ul Asad and an answer by Pratik Pathak.