PyInstaller EXE Changes Back To Original Icon - python

I have created a standalone .exe file with PyInstaller using this command: pyinstaller --onefile -i "icon0.ico" test.py -w. When I open the dist folder the exe gets put into it shows the icon I used but the moment I copy or move it from that folder the icon disappears and it returns back to the stock PyInstaller icon.
Oddly enough if I rename the file the icon stays like it is supposed to, but I can't use this as a solution since I have other files that depend on my exe being a specific filename. I used Resource Hacker to view the icon contents, I completely replaced the stock icon with my icon but after saving it nothing changed, still the same old stock PyInstaller icon. Yes, my .ico file had all the different 256x256, 128x128, 64x64, 48x48, 32x32 and 16x16 sizes.
What can I do to fix this?
PyInstaller version: 3.4
Python version: 3.7.2

I usually use:
pyinstaller --onefile -w --icon=*icon name*.ico test.py

Solution:
pyinstaller --noconfirm --onefile --name=filename --icon=icon.ico script.py
Works well, but the .ico file must be in the same directory as the .exe file. Any other options work as well, but haven't tried removing --onefile.
Note: --name required. I am not certain why.

I found a solution to this issue, it might apply to your case too. See herefor the related question.
I had same issue, I tried both putting pyinstaller ... --icon=icon/path/icon.ico ... main.py and editing pyinstaller.spec file,
exe = EXE(pyz,
...
console=False , icon='C:\\icon\\path\\icon.ico')
But none of these solutions seems to work.
So, as mentioned on the link above, changing/renaming the directory /dist/ or renaming the .exe file immidiately changes the icon.

Assuming python 3.10, you need to make the icon known within the pyInstaller environment. My batch file is:
<full_path>\pyinstaller -wF --onefile --add-binary myicon.ico;. --icon myicon.ico app.py 2> build_log.txt
and in the python file app.py add:
import os, sys
if getattr(sys, 'frozen', False):
# If the application is run as a bundle, the PyInstaller bootloader
# extends the sys module by a flag frozen=True and sets the app
# path into variable _MEIPASS'.
application_path = sys._MEIPASS
else:
application_path = os.path.dirname(os.path.abspath(__file__))
then refer to the icon with:
os.path.join(application_path,'myicon.ico')

Related

subprocess still needs file after add-data in pyinstaller [duplicate]

I'm trying to add an image to the one file produced by Pyinstaller. I've read many questions/forums like this one and that one and still it's not working.
I know that for one file operation, Pyinstller produce a temp folder that could be reached by sys.MEIPASS. However I don't know where exactly in my script I should add this sys.MEIPASS.
Kindly show the following:
1- Where and how sys.MEIPASS should be added? In the python script, or in the spec file?
2- What is the exact command to use? I've tried
pyinstaller --onefile --windowed --add-data="myImag.png;imag" myScript.py
or
pyinstaller --onefile --windowed myScript.py
and then add ('myImag.png','imag') to the spec file and then run
pyinstller myScript.spec
None has worked.
Note: I have python 3.6 under windows 7
When packaged to a single file with PyInstaller, running the .exe will unpack everything to a folder in your TEMP directory, run the script, then discard the temporary files. The path of the temporary folder changes with each running, but a reference to its location is added to sys as sys._MEIPASS.
To make use of this, when your Python codes reads any file that will also be packaged into your .exe, you need to change the files location to be located under sys._MEIPASS. In other words, you need to add it to your python code.
Here is an example that using the code from the link you referenced to adjust the file path to correct location when packaged to a single file.
Example
# data_files/data.txt
hello
world
# myScript.py
import sys
import os
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def print_file(file_path):
file_path = resource_path(file_path)
with open(file_path) as fp:
for line in fp:
print(line)
if __name__ == '__main__':
print_file('data_files/data.txt')
Running PyInstaller with the following options packages the file:
pyinstaller --onefile --add-data="data_files/data.txt;data_files" myScript.py
builds myScript.exe which runs correctly and can open and read the packaged data file.
I tried changed my python script's working directory, and it seems to work:
import os
import sys
os.chdir(sys._MEIPASS)
os.system('included\\text.txt')
my pyinstaller command:
pyinstaller --onefile --nowindow --add-data text.txt;included winprint.py --distpath .
I used a command prompt command instead of a .spec
The command excludes shapely and then adds it back in (I guess this invokes a different import process). This shows how to add folders instead of just files.
pyinstaller --clean --win-private-assemblies --onefile --exclude-module shapely --add-data C:\\Python27\\Lib\\site-packages\\shapely;.\\shapely --add-data C:\\Python27\\tcl\\tkdnd2.8;tcl main.py
a simpler way of accessing the temp folder if by doing this:
bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__)))
data_path = os.path.abspath(path.join(bundle_dir, 'data_file.dat'))
Got it from read the docs
I put together a simple function that will grab the resource from a local path when running your *.py file as a script (i.e., in a debugger or via the command line) or from the temp directory when running as a pyinstaller single-file executable
import sys
from pathlib import Path
def fetch_resource(resource_path: Path) -> Path:
try: # running as *.exe; fetch resource from temp directory
base_path = Path(sys._MEIPASS)
except AttributeError: # running as script; return unmodified path
return resource_path
else: # return temp resource path
return base_path.joinpath(resource_path)
This way, if your script has any hardcoded paths you can use them as-is, e.g.:
self.iconbitmap(fetch_resource(icon_path)) and icon_path will be updated appropriately based on the environment.
You'll need to tell pyinstaller to --add-data for any assets you wish to use this way, e.g. to add your entire 'assets' folder:
pyinstaller -w -F --add-data "./src/assets/;assets"
Or you can add things directly to the datas list in your pyinstaller spec file (in the a = Analysis block)
datas = ['(./src/assets)', 'assets']

Python project to exe with some files [duplicate]

I'm trying to add an image to the one file produced by Pyinstaller. I've read many questions/forums like this one and that one and still it's not working.
I know that for one file operation, Pyinstller produce a temp folder that could be reached by sys.MEIPASS. However I don't know where exactly in my script I should add this sys.MEIPASS.
Kindly show the following:
1- Where and how sys.MEIPASS should be added? In the python script, or in the spec file?
2- What is the exact command to use? I've tried
pyinstaller --onefile --windowed --add-data="myImag.png;imag" myScript.py
or
pyinstaller --onefile --windowed myScript.py
and then add ('myImag.png','imag') to the spec file and then run
pyinstller myScript.spec
None has worked.
Note: I have python 3.6 under windows 7
When packaged to a single file with PyInstaller, running the .exe will unpack everything to a folder in your TEMP directory, run the script, then discard the temporary files. The path of the temporary folder changes with each running, but a reference to its location is added to sys as sys._MEIPASS.
To make use of this, when your Python codes reads any file that will also be packaged into your .exe, you need to change the files location to be located under sys._MEIPASS. In other words, you need to add it to your python code.
Here is an example that using the code from the link you referenced to adjust the file path to correct location when packaged to a single file.
Example
# data_files/data.txt
hello
world
# myScript.py
import sys
import os
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def print_file(file_path):
file_path = resource_path(file_path)
with open(file_path) as fp:
for line in fp:
print(line)
if __name__ == '__main__':
print_file('data_files/data.txt')
Running PyInstaller with the following options packages the file:
pyinstaller --onefile --add-data="data_files/data.txt;data_files" myScript.py
builds myScript.exe which runs correctly and can open and read the packaged data file.
I tried changed my python script's working directory, and it seems to work:
import os
import sys
os.chdir(sys._MEIPASS)
os.system('included\\text.txt')
my pyinstaller command:
pyinstaller --onefile --nowindow --add-data text.txt;included winprint.py --distpath .
I used a command prompt command instead of a .spec
The command excludes shapely and then adds it back in (I guess this invokes a different import process). This shows how to add folders instead of just files.
pyinstaller --clean --win-private-assemblies --onefile --exclude-module shapely --add-data C:\\Python27\\Lib\\site-packages\\shapely;.\\shapely --add-data C:\\Python27\\tcl\\tkdnd2.8;tcl main.py
a simpler way of accessing the temp folder if by doing this:
bundle_dir = getattr(sys, '_MEIPASS', path.abspath(path.dirname(__file__)))
data_path = os.path.abspath(path.join(bundle_dir, 'data_file.dat'))
Got it from read the docs
I put together a simple function that will grab the resource from a local path when running your *.py file as a script (i.e., in a debugger or via the command line) or from the temp directory when running as a pyinstaller single-file executable
import sys
from pathlib import Path
def fetch_resource(resource_path: Path) -> Path:
try: # running as *.exe; fetch resource from temp directory
base_path = Path(sys._MEIPASS)
except AttributeError: # running as script; return unmodified path
return resource_path
else: # return temp resource path
return base_path.joinpath(resource_path)
This way, if your script has any hardcoded paths you can use them as-is, e.g.:
self.iconbitmap(fetch_resource(icon_path)) and icon_path will be updated appropriately based on the environment.
You'll need to tell pyinstaller to --add-data for any assets you wish to use this way, e.g. to add your entire 'assets' folder:
pyinstaller -w -F --add-data "./src/assets/;assets"
Or you can add things directly to the datas list in your pyinstaller spec file (in the a = Analysis block)
datas = ['(./src/assets)', 'assets']

Pyisntaller No Such File Or Directory

I have created a script with pygame and it requires one file which is a font named "blocky.ttf" inside the "assets" folder. I have given a relative path inside my python script.
The problem occurs when I use pyinstaller to convert to it to exe. When I open the exe file it shows an error that this font file doesn't exist in the temp folder something like "MEI" and then some numbers.
I am using this to get the path of the font:
try:
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
font_path = os.path.join(base_path, 'assets\\blocky.ttf')
I tried to copy my assets folder to the "dist" folder. No good news.
I have also tried different solutions from StackOverflow but nothing worked.
This is the command I am using to convert to exe:
pyinstaller --onefile -w 'main.py'
Python: 3.9.6
Pyinstaller: 4.4
OS: Windows 10
Auto-py-to-exe solved my problem.
It is a GUI which generates pyinstaller command based on the options you select and run it. No headache of CLI.
Selecting the required files and folder is really easy. Just browse and select.
Here is the link:
https://pypi.org/project/auto-py-to-exe/

How to specify pyinstaller resource files correctlly?

I want to make a runnable .py application by double click in Linux.
First, I tried a simple example:
#!/usr/bin/env python3
import cv2
img= cv2.imread('/home/andrei/WTZ/code/Computer-Vision-with-Python/DATA/test_image.jpg')
cv2.imshow('It works?', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
It just opens an image, nothing special.
I installed pyinstaller and I ran the following command in the terminal:
pyinstaller --onefile --add-data="/home/andrei/WTZ/code/Computer-Vision-with-Python/DATA/test.jpg;/home/andrei/WTZ/code/Computer-Vision-with-Python/DATA/test.jpg" test.py
And I encounter the following error:
pyinstaller: error: argument --add-data: invalid add_data_or_binary value: '/home/andrei/WTZ/code/Computer-Vision-with-Python/DATA/test.jpg;/home/andrei/WTZ/code/Computer-Vision-with-Python/DATA/test.jpg'
What am I doing wrong?
Ran this on my machine
pyinstaller --add-binary C:\Users\jezequiel\Desktop\diagram_new.png;.\images d.py
and the .PNG file was copied to a subdirectory named images in the folder containing the d.exe file.
So recommended approach is the first make a main.spec file -
pyi-makespec --windowed main.py
you should then get a main.spec file that you can call pyinstaller with - so you don't need to pass in all resource files via command line.
Then, in your main.spec file (which is actually python code), you should have something like
a = Analysis(
...
)
Assuming your resources are in a resources directory in the root of your project, you can add them all to your build by adding a line that recursively walks through this directory and adds all files. Your app should then be able to reference these files.
a = Analysis(
...,
datas = [(os.path.join(dp, f), dp) for dp, dn, filenames in os.walk('./resources') for f in filenames],
)

how pyinstaller exe file work on other computers with included files

I'm trying to make an executable file, using pyinstaller, with included files (json, logo, driver) which will work on any computer.
I've included several files in the executable through this line -
pyinstaller.exe --onefile --add-data "jsonfile.json;." --add-data "chromedriver.exe;." --add-data "logo1.ico;." --windowed --icon=logo1.ico script.py
SUCCESS BUT,
when I opened it on another computer, does not work.
Also, I moved the files from the current directory on my computer and also not working - so something to do with the path I assume.
This code is for paths for any file included -
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
To that function, the path on my computer is sent.
Any ideas what went wrong?
4 Things, none of which actually solve your problem, but might help
put all your assets in an asset subfolder
explicitly call out the target
--add-data "data/jsonfile.json;./data/jsonfile.json"
you can try something like os.startfile(sys._MEIPASS) to open the folder and look at it (note you should put a sleep or a pause after because it will clean up the files when the program crashes or closes)
when you call resource path make sure you call it with the relative path
resource_path("data/myfile.json")

Categories