2025, Oct 22 08:17

PyInstaller से PyQt6 ऐप पैक करते समय matplotlib गायब? समाधान

PyInstaller से PyQt6 ऐप का one-file exe बनाते समय 'No module named matplotlib' त्रुटि का कारण और फिक्स: डिपेंडेंसियाँ, hidden imports, बिल्ड सेटअप.

एक छोटे PyQt6 डेस्कटॉप ऐप को एक ही Windows executable में पैकेज करते समय एक चकमा देने वाली, लेकिन साधारण सी समस्या सामने आ सकती है: बिल्ड के समय कोई निर्भरता गायब होना। आप hidden imports और collect-all फ्लैग्स जोड़ दें, फिर भी PyInstaller उन लाइब्रेरीज़ को पैक नहीं कर सकता जो आपके पर्यावरण में वास्तव में इंस्टॉल ही नहीं हैं। नतीजा, रनटाइम पर साफ-साफ असफलता और संदेश: No module named matplotlib.

सेटअप दोहराना और असफल बिल्ड

एप्लिकेशन PyInstaller के साथ एक-फ़ाइल .exe बनाता है, और इसकी निर्भरताएँ यह हैं:

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

PyInstaller की spec फ़ाइल में matplotlib और उसके बैकएंड्स के लिए hidden imports जोड़े गए हैं:

# -*- 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',
)

बिल्ड कमांड भी hidden imports को मजबूती से जोड़ता है और पैकेज इकट्ठा करने की कोशिश करता है:

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

इसके बावजूद, लक्ष्य मशीनों पर executable 'No module named matplotlib' के साथ फेल हो जाता है। बिल्ड लॉग भी यही दिखाता है: Hidden import 'matplotlib.backends.backend_tkagg' not found और Hidden import 'matplotlib.backends.backend_pdf' not found.

असल में गड़बड़ कहाँ है

PyInstaller मौजूदा Python वातावरण का विश्लेषण करता है और जिन इम्पोर्ट्स को वह सुलझा पाता है, उन्हें फ्रीज़ कर देता है। जो लाइब्रेरी बिल्ड समय पर इंस्टॉल नहीं है, उसे पैक करने के लिए कुछ होता ही नहीं। दिए गए सेटअप में matplotlib निर्भरताओं की सूची में शामिल नहीं है, इसलिए बिल्ड वाले वातावरण में इंस्टॉल भी नहीं है। इस कारण hidden-import फ्लैग्स बेअसर हो जाते हैं—PyInstaller स्थानीय रूप से मौजूद ही नहीं होने वाले मॉड्यूल ढूंढ नहीं सकता।

रनटाइम पर संदेश साफ़-साफ़ बताता है, और विश्लेषण चरण भी वही दोहराता है: matplotlib बैकएंड्स के hidden imports मिलते ही नहीं।

समाधान

बिल्ड वातावरण में matplotlib इंस्टॉल करें और उसे अपनी परियोजना की निर्भरताओं में जोड़ें। लाइब्रेरी मौजूद होते ही, मौजूदा spec और hidden-import कॉन्फ़िगरेशन PyInstaller को उसे ढूंढने और executable के साथ पैक करने की अनुमति दे देंगे।

pip install matplotlib

इसके बाद निर्भरताओं का सेट यह हो जाता है:

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

उसी कमांड से ऐप को फिर से बिल्ड करें—अब executable में matplotlib शामिल होगा। पैकेज उपलब्ध होते ही बिल्ड लॉग में matplotlib बैकएंड्स की कमी वाले पिछले त्रुटि संदेश नहीं दिखेंगे।

यह क्यों महत्वपूर्ण है

जब आप एक स्टैंडअलोन डेस्कटॉप ऐप वितरित करते हैं, तो हर रनटाइम इम्पोर्ट बिल्ड समय पर वातावरण में मौजूद होना चाहिए। वरना PyInstaller उसे फ्रीज़ नहीं कर पाएगा और अंतिम बाइनरी उन मशीनों पर फेल हो जाएगी जहाँ वह निर्भरता इंस्टॉल नहीं है। यह बात खासकर PyQt6 जैसी GUI स्टैक के साथ महत्वपूर्ण है, जो अक्सर विज़ुअलाइज़ेशन लाइब्रेरीज़ से एकीकृत होते हैं। बिल्ड स्व-निहित होना चाहिए, और इसकी शुरुआत पूर्ण निर्भरता सूची से होती है।

साथ ही, बिल्ड के दौरान दिखने वाले संदेशों पर ध्यान देना फायदेमंद है। उदाहरण के लिए, लॉग इंगित करता है कि qt_material को PySide या PyQt के बाद इम्पोर्ट करना चाहिए। ऐसे संदेश वितरण से पहले ही इम्पोर्ट क्रम और पैकेजिंग अपेक्षाओं को परखने में मदद करते हैं।

मुख्य बातें

बिल्ड वातावरण को अपने ऐप के वास्तविक इम्पोर्ट्स के अनुरूप रखें, सभी आवश्यक लाइब्रेरीज़ को सूचीबद्ध और इंस्टॉल करें, और फिर PyInstaller को अपना काम करने दें। यदि लॉग बताता है कि कोई hidden import नहीं मिला, तो सबसे सीधा समाधान वही है: उस मॉड्यूल को वातावरण में उपलब्ध करा दें—जैसा कि matplotlib के साथ दिखाया गया।

यह लेख StackOverflow पर प्रश्न (लेखक: Maifee Ul Asad) और Pratik Pathak के उत्तर पर आधारित है।