In order to create a python .exe file I have been using pyinstaller and this command:
pyinstaller --onefile -w -i favicon.ico "program.py"
This creates a /dist folder which contains the generated .exe file.
The problem is that I am not able to run this .exe file without including the following program files inside the .exe launching folder.
+ Dir
- favicon.ico
- logo.gif
- data.csv
- program.exe
How can I include the .ico, .gif and .csv INSIDE the .exe so it truly becomes "onefile"?
I'm quite new to python so I apologize in advance if the code is a little chaotic. I faced a similar problem with .csv files. I managed to pack them into .py files by once running this code:
import csv
myfinalvariable=[]
with open(PathToOriginalCsv + '\\' + 'NameOfCsv.csv', newline='') as csvfile:
myfirstvariable = csv.reader(csvfile, delimiter=',', quotechar='|')
for line in myfirstvariable:
myfinalvariable.append(' '.join(line).split())
pyfile=open('PathToDesiredFile\mynewfile.py', 'w')
pyfile.write('newcsv=%s' %(myfinalvariable))
pyfile.close
You can iterate this if you have multiple csv files. Now you have the py file with 'variables' and you can 'forget' about the csv files. Because if you put the created py file into your 'project folder' and put:
from mynewfile import newcsv, newcsv2, ...
into your code, you can modify your code to use the variables 'newcsv', 'newcsv2', etc. instead of loading the original csv files. When you use pyinstaller with the --onefile parameter, it packs the 'mynewfile.py' file into the created exe file. Pyinstaller 3.0 also packs the .ico file when using the parameter --icon=favicon.ico. Tested on Windows, Python3.4, Pyinstaller3.0. I understand this is an old question, I hope this helps someone who stumbles upon it.
By writing a shell script, that can be executed by powershell,
The file can be made and written as a .exe file
The other files can be moved into the new directory.
Now all that needs done is having the Powershell script to run.
You can package the files with pyinstaller's --add-data option. For example with your files you should try:
> pyinstaller --onefile -w -i favicon.ico "program.py" --add-data "favicon.ico:favicon.ico'\
--add-data "lgog.gif:logo.gif" --add-data "data.csv:data.csv"
On other OS it may be required to replace the \ with a ^ (or do it all on one line.)
This should package all the files into the exe.
If you want to access these files from the code you need to add a little bit extra, otherwise the program will not find them.
import os, sys
def resource(relative_path):
if getattr(sys, 'frozen', False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath('.'), relative_path)
When pyinstaller compiles a script it sets the _MEIPASS variable to the temporary path of the created files at run-time. This script harnesses that to locate these file and defaults back to the ordinary path in un-compiled mode read more.
To access the files from the code, just replace all links to the files with resource('myfile.etc'). For example, using youe data.csv file
with open(resource('data.csv'), 'r') as csvfile:
# do stuff
Related
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']
I have a Python script that looks like
path = os.path.join(os.path.dirname(sys.executable), 'data.txt')
file = open(path, 'w')
file.write("something")
file.close()
When I use Pyinstaller (with the option --onefile), and open the resulting .exe-file, it does not seem to do anything. In particular, I cannot find a data.txt file. How do I fix this?
os.path.dirname(sys.executable) would point at the directory where Python.exe is if you were not running under Pyinstaller, and it's unlikely you'd want to write there. It's likely that under PyInstaller, it's some temporary directory.
Instead, just 'data.txt' (or os.path.join(os.getcwd(), 'data.txt') if you want to be pedantic) would create the file in the program's current working directory, which, if you just double-click on the EXE, would be the EXE's directory.
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']
I have recently made an app that does automatic assigment submissions using selenium. I would like to convert this .py file to a .exe so i can give it to others to use on thier computers.
This is the current directory of the project:
C:
canvas automation
chromedriver.exe
main.py
canvas_subjects.txt
In the main.py file, it uses canvas_subjects.txt file as below:
with open('c:/canvas automation/canvas_subjects.txt', 'r+') as subjects:
chrome_dir = 'C:/PythonProjects/Web Scraping/Selenium/chromedriver.exe'
driver = webdriver.Chrome(chrome_dir)
driver.minimize_window()
if subjects.read() != '':
# does the rest of the program here
When i try and convert main.py to .exe, running the .exe file produces the error:
Failed to execute main script
I'm guessing that when it runs the python script, it cannont find the following directory:
c:/canvas automation/canvas_subjects.txt
To convert this file to .exe, i have tried using pyinstaller:
C:\Users\user> pip install pyinstaller
C:\Canvas Automation> pyinstaller main.py
# also tried
C:\Canvas Automation> pyinstaller --onefile -w main.py
.exe file is created but outputs the same error.
I have tried auto-py-to-exe but it says that you need to incorporate the following code into your script so that the .exe file can find the directory but i do not understand how to use it:
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)
You can't include a txt file in an exe like that. You have 2 solutions here:
Deliver your .txt file alongside the .exe like a part of the installation
Put the content of your text file inside a .py file as a string variable.
NB1: Are you sure you need an exe? could you not distribute it as a python soft, needing python to be executed?
NB2: Either way, you really should not use absolute paths like 'c:...' if you plan to distribute your soft
If you are having trouble including .txt files, try the following command to enable it in order:
CTRL+A
Backspace
CTRL+S
ALT+F4
If problems persist, please let me know.
I finally got the PyInstaller up and running. So far it looks good, I'm able to create a single exe that does some calculations on the files in the directory where the exe is located. So I just copy the exe to a folder with the files I need to work on and double click the exe (windows 7).
But would it be possible to make an .exe were I just drag-and-drop my files onto and then it will calculate on those files ____?
Late answer (5Y), but if you drop a file into an exe created with pyinstaller, sys.argv will receive a list containing the path of all the files dragged.
The first item ([0]) is the path of the exe, i.e.:
import sys
print(sys.argv)
['C:/name_of.exe', 'C:/the_file_dragged1', , 'C:/the_file_dragged2']
There is a simple way to see how the files you drop on an executable get handled: build an exe from a file with such content:
import sys
def __main__():
with open("parameters.log", "ab") as f:
f.write(str(sys.argv))
Use it with one or more files that you drag and drop and observe the content of parameters.log: you should find that for each file its absolute path is passed as an argument. nth file will have its path in sys.argv[n].
This can actually be generalised to any executable.