Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Anyone successfully bundled data files into a single file with Pyinstaller?

I have been combing Stack Overflow and the rest of the web on how to add data files into my python application:

import Tkinter

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()

--- Everything Fine Here ---

        self.B = Tkinter.Button(self, text = 'Create Document', command = self.OnButtonClick)
        self.B.grid(column = 0, row = 6)


    def OnButtonClick(self):
        createDoc()

if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('Receipt Form')
    app.iconbitmap(os.getcwd() + '/M.ico')
    app.mainloop()

I have tried using the .spec file with no luck

Onedir works fine, however when I try to compile into a single executable, it gives an error that the file 'M.ico' isn't defined.

If anyone was able to bundle data files with pyinstaller into a single file. Please help. Thanks.

I am on a Windows 10 computer running Python 2.7 with PyInstaller 3.2

like image 225
Michael Zheng Avatar asked Aug 15 '16 07:08

Michael Zheng


People also ask

How do I add multiple data to PyInstaller?

In order to add data multiple files into your EXE file using pyinstaller, the best way is to add the list of files into your application's spec file. This line adds all the files inside assets folder inside your application's assets folder.

Does PyInstaller compile?

PyInstaller supports making executables for Windows, Linux, and macOS, but it cannot cross compile. Therefore, you cannot make an executable targeting one Operating System from another Operating System. So, to distribute executables for multiple types of OS, you'll need a build machine for each supported OS.

Does PyInstaller include all libraries?

PyInstaller does not include libraries that should exist in any installation of this OS. For example in GNU/Linux, it does not bundle any file from /lib or /usr/lib , assuming these will be found in every system.

Does PyInstaller include imports?

PyInstaller does not include imports.


1 Answers

You must specify every data file you want added in your pyinstaller .spec file, or via the command line options (.spec is much easier.) Below is my .spec file,with a "datas" section:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['pubdata.py'],
             pathex=['.', '/interface','/recommender'],
             binaries=None,
             datas=[('WordNet/*.txt','WordNet'),
             ('WordNet/*.json','WordNet'),
             ('WordNet/pdf_parsing/*.json','pdf_parsing'),
             ('WordNet/pdf_parsing/*.xml','pdf_parsing'),
             ('images/*.png','images'),
             ('database/all_meta/Flybase/*.txt','all_meta'),
             ('database/all_meta/Uniprot/*.txt','all_meta'),
             ('database/json_files/*.json','json_files'),
             ('Data.db','.')],

             hiddenimports=['interface','recommender'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='GUI',
          debug=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='GUI')
app = BUNDLE(coll,
             name='App.app',
             icon=None)

After this, if you are trying to access any data file you specified in the .spec file, in your code, you must use Pyinstaller's _MEIPASS folder to reference your file. Here is how i did so with the file named Data.db:

import sys
import os.path

        if hasattr(sys, "_MEIPASS"):
            datadir = os.path.join(sys._MEIPASS, 'Data.db')
        else:
            datadir = 'Data.db'

        conn = lite.connect(datadir)

This method above replaced this statement that was on its own:

conn = lite.connect("Data.db")

This link helped me a lot when i was going through the same thing: https://irwinkwan.com/2013/04/29/python-executables-pyinstaller-and-a-48-hour-game-design-compo/

Hope this helps!

like image 143
cyborg95 Avatar answered Oct 25 '22 09:10

cyborg95