Search code examples
pythonimagepyinstaller

Including image files inside executable using pyinstaller - conflicting sources


I was trying to create a single exe file using pyinstaller and for the purpose of my app, I needed to include some images. After some reasearch, the consensus across the web seem to be that implementation of a function as shown here: example 1 or here: example 2 should help when after creating executable you get an error saying unable to find files.

For me this approach didnt work. Eventually, on SECOND page of google results (thats how desperate I was), I found this: example 3.

To my surprise, adding image files to the derectory of the executable, worked.

Question remains, as well as my confusion, how can i do it without having to have extra files next to the executable? Or i really cant and should go down the not-single-file exec? or maybe something else?

Thank you


Solution

  • I have been using *.spec file for this purpose, find it easy to set everything up and configure.

    # -*- mode: python ; coding: utf-8 -*-
    block_cipher = None
    a = Analysis(['wsgi.py'],
                 pathex=['D:\\path_to_folder_with_entry_point'],
                 binaries=[],
                 datas=[],
                 hiddenimports=['win32timezone','mysql'],
                 hookspath=[],
                 hooksconfig={},
                 runtime_hooks=[],
                 excludes=[],
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher,
                 noarchive=False)
    =======MAGIC STARTS HERE============
    a.datas += [('logo-dark-theme.jpg','D:\\folder\\projectFolder\\static\\logo-dark-theme.jpg', ".")] 
    

    The above entry lets pyinstaller know where to find the file you want to bundle up within your executable. The "." means that the program you wrote, will find this file in the root directory of the project, after extracting necessery files to the runtime directory. Below is the rest of the *.spec file.

    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    
    exe = EXE(pyz,
              a.scripts,
              a.binaries,
              a.zipfiles,
              a.datas,
              name='programName',
              debug=False,
              bootloader_ignore_signals=False,
              strip=False,
              upx=True,
              upx_exclude=[],
              runtime_tmpdir='C:\\Program Files (x86)\\location of your runtime directory',
              console=False,
              disable_windowed_traceback=False,
              target_arch=None,
              codesign_identity=None,
              entitlements_file=None )
    

    Now that the spec file is ready, you have to account for the file location in your code.

    In the file that you use to create your flask app, you need to include this:

    base_dir = '.'
    if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
        base_dir = os.path.join(sys._MEIPASS)
    

    and this:

    def create_app():
        app = Flask(__name__, static_folder=os.path.join(base_dir), template_folder=os.path.join(base_dir))
    

    Finally, add new path in the html:

    <img src="{{ url_for('static', filename='logo-dark-theme.jpg') }}">
    

    This process can be followed to add all and any files you want to add to the executable. I have added this way images, html and css files among others.

    Onward!