Pyinstaller: generate -exe file + folder (in –onefile mode)
Question:
Now i’m working with Pyinstaller.
I have an script which get images from a img folder..
/python
|----/img
|----|----icon1.ico
|----|----icon2.ico
|----maint.py
My script to generate .exe is
pyinstaller.py --windowed --noconsole --clean --onefile maint.py
the problem is that only generate the .exe file but the whole folder /img is omitted.
Question: which aditional syntax do I need to put in the previous line in order to get automatically the .exe file + /img folder?
I mean: that after execution of pyinstaller.py script, with all arguments, I must see in the /dist folder: the .exe file + the /img folder with all icons or bitmaps files I have for my application
Thanks
Answers:
This is how I managed to solve the issue:
I’m working with current version of PYInstaller + Python 2.67 with Sublime Text as Editor.
-
In case your Py script requires some files, icons, images, you must include a function which retrieves these files from the project folder (in development) or form the temporary data folder (in case of deployment). This script MUST be in your code exactly in the part which you put the relatives paths in order to get the resources. Please follow exactly this guideline.
-
After the previous code, you must execute for the first time the pyinstaller command -as I post in my question-.
-
Now, open your .spec file generated after execution of the PYInstaller (located in PYinstaller/YourAppName/
) command and add, after a.binaries
line, the next line into the EXE()
function:
exe = EXE(pyz,
a.scripts,
a.binaries,
Tree('..\python\images', prefix='images\'),
....
Keep in mind that in Tree(...)
function the first argument is the folder to put outside: which means that I want to include all the content of this folder (notice that I’m putting a relative path respect to the AppStart.py file) into the file’s container of my .EXE file.
-
After that modification re-execute the pyinstaller command, but in this case pointing to my .SPEC
file:
pyinstaller.py --windowed --noconsole --clean --onefile AppStartAppStart.spec
And finally my App can be executed as executable without need to copy and paste all external folders as someone mentioned. But as always I consider the good-practical way.
Thanks for your support.
You have to solve many issues to get this working. For example:
- Getting the right resource path
- Adding data
The first issue is (as mentionned) solved by adjusting paths depending on execution mode.
def app_path(path):
frozen = 'not'
if getattr(sys, 'frozen', False):
# we are running in executable mode
frozen = 'ever so'
app_dir = sys._MEIPASS
else:
# we are running in a normal Python environment
app_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(app_dir, path)
For the second issue instead of tree i use the wildcard operator (*) to add what i need.
added_files = [
( './pics/*', 'pics' ),
( './db/*', 'db' ),
]
then in Analysis,
datas = added_files
A thorough answer is quite long. I’ve written this article to show in some minute details what i went through to solve the issue.
You can also run pyinstaller within another python script then use shutil.copytree()
to move the folders over after. Or use shutil.copyfile()
for individual files.
import PyInstaller.__main__
import shutil
PyInstaller.__main__.run([
'YourProgram.py',
'--icon=UI/Your_Icon.ico',
'--onefile',
'--noconsole',
], )
shutil.copytree('path/', 'dist/path')
There is no way this will work because spec build doesn’t support –onefile.
Per Pyinstaller error message:
option(s) not allowed: --onedir/--onefile --console/--nowindowed/--windowed/--noconsole makespec options not valid when a .spec file is given
Unless it was supported on older version. I tried every version back to 3.0 at which point I stopped because I would have been forced to break dependencies and go on a dependency chase.
Unfortunately if you include the folders individually with
--path
they become flattened in directory that your script.py
resides, unless you provide each file individually with --add-data SRC:DEST
arguments.
Now i’m working with Pyinstaller.
I have an script which get images from a img folder..
/python
|----/img
|----|----icon1.ico
|----|----icon2.ico
|----maint.py
My script to generate .exe is
pyinstaller.py --windowed --noconsole --clean --onefile maint.py
the problem is that only generate the .exe file but the whole folder /img is omitted.
Question: which aditional syntax do I need to put in the previous line in order to get automatically the .exe file + /img folder?
I mean: that after execution of pyinstaller.py script, with all arguments, I must see in the /dist folder: the .exe file + the /img folder with all icons or bitmaps files I have for my application
Thanks
This is how I managed to solve the issue:
I’m working with current version of PYInstaller + Python 2.67 with Sublime Text as Editor.
-
In case your Py script requires some files, icons, images, you must include a function which retrieves these files from the project folder (in development) or form the temporary data folder (in case of deployment). This script MUST be in your code exactly in the part which you put the relatives paths in order to get the resources. Please follow exactly this guideline.
-
After the previous code, you must execute for the first time the pyinstaller command -as I post in my question-.
-
Now, open your .spec file generated after execution of the PYInstaller (located in
PYinstaller/YourAppName/
) command and add, aftera.binaries
line, the next line into theEXE()
function:exe = EXE(pyz, a.scripts, a.binaries, Tree('..\python\images', prefix='images\'), ....
Keep in mind that in
Tree(...)
function the first argument is the folder to put outside: which means that I want to include all the content of this folder (notice that I’m putting a relative path respect to the AppStart.py file) into the file’s container of my .EXE file. -
After that modification re-execute the pyinstaller command, but in this case pointing to my
.SPEC
file:pyinstaller.py --windowed --noconsole --clean --onefile AppStartAppStart.spec
And finally my App can be executed as executable without need to copy and paste all external folders as someone mentioned. But as always I consider the good-practical way.
Thanks for your support.
You have to solve many issues to get this working. For example:
- Getting the right resource path
- Adding data
The first issue is (as mentionned) solved by adjusting paths depending on execution mode.
def app_path(path):
frozen = 'not'
if getattr(sys, 'frozen', False):
# we are running in executable mode
frozen = 'ever so'
app_dir = sys._MEIPASS
else:
# we are running in a normal Python environment
app_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(app_dir, path)
For the second issue instead of tree i use the wildcard operator (*) to add what i need.
added_files = [
( './pics/*', 'pics' ),
( './db/*', 'db' ),
]
then in Analysis,
datas = added_files
A thorough answer is quite long. I’ve written this article to show in some minute details what i went through to solve the issue.
You can also run pyinstaller within another python script then use shutil.copytree()
to move the folders over after. Or use shutil.copyfile()
for individual files.
import PyInstaller.__main__
import shutil
PyInstaller.__main__.run([
'YourProgram.py',
'--icon=UI/Your_Icon.ico',
'--onefile',
'--noconsole',
], )
shutil.copytree('path/', 'dist/path')
There is no way this will work because spec build doesn’t support –onefile.
Per Pyinstaller error message:
option(s) not allowed: --onedir/--onefile --console/--nowindowed/--windowed/--noconsole makespec options not valid when a .spec file is given
Unless it was supported on older version. I tried every version back to 3.0 at which point I stopped because I would have been forced to break dependencies and go on a dependency chase.
Unfortunately if you include the folders individually with
--path
they become flattened in directory that your script.py
resides, unless you provide each file individually with --add-data SRC:DEST
arguments.