PyInstaller & NSIS Zip2Exe - Referencing files outside of your PC - python

I created an application that references multiple shapefiles. Example below:
df = gpd.GeoDataFrame.from_file(r'C:\PATH_ON_OWN_PC\FILE_NAME.shp')
As you can see from the above, the program references files from my own PC.
I then used NSIS to create a setup.exe file so the program can be downloaded and used with other PCs. I included all the datasets in the zip file.
The issue is when the application runs the script it's still referencing the path on my own PC, which obviously won't work.
Is there a way to alter the code so it reads the files from the downloadable .exe file, so it reads the files no matter the PC it's downloaded from.
Thanks!

You should be able to use something like this so that your .exe will work on other computers. Here is a generic example that points to a file inside of my Scripts dir, where my python.exe is located, for one of my Conda envs. You can replace the second and third params in os.path.join() to match up with the path of your shapefiles.
import os
import sys
python_exe_dir = os.path.dirname(sys.executable)
sample_file_path = os.path.join(python_exe_dir, 'Scripts', 'gdal2tiles.py')
print(sample_file_path)
'C:\\Users\\matth\\anaconda3\\envs\\gpd_0\\Scripts\\gdal2tiles.py'
So in your case, you'd end up with something like this.
df = gpd.GeoDataFrame.from_file(sample_file_path)

Related

How to bundle Python apps with asset dependencies for end users?

I have a Tkinter app that uses images included in the same folder as the .py file. pyinstaller script.py produces an executable that runs but does not open any windows. This is because it is looking for images that don't exist in the same subdirectory. When I copy the important images to the dist folder Pyinstaller creates, the application runs correctly.
However, I would like to have a single executable that I can share with other users that doesn't also require them to have the images stored. The images should be bundled with the software somehow, like how commercial software (usually) doesn't require you to download assets separately from the program itself.
Is there a way to bundle Python programs and the assets they use into single-click applications?
Note that I am using Python 3 on Linux Mint. I am also something of a novice, so don't be surprised if I'm missing something obvious here.
It appears I've solved my own problem.
Instead of having the images included in the same folder as main.py and using the resulting short relative filepath to reach them, install the images in an appropriate space in the system directory tree (I used /home/$USER$/.$PROGRAMNAME$/) and have the program access the files using the absolute path to that directory. This will allow you to copy the program anywhere on your computer you want and have it run without a problem.
However, if you want to share it with someone else, you'll need to also include an installation script that places the assets in the correct directory on their computer.

Pyinstaller onedir option - exe file outside the directory

Recently I was experimenting with pyinstaller to create an executable file from my Python script. Everything works as expected.
I tested two options: --onefile, which takes quite a long time (like 20-30sec) to start because it depacks everything into a temporary directory.
The --onedir option is much faster (4sec) to start but it's not very comfortable to use. When I move exe file outside this directory program no longer works.
My question is: is there a possibility to make the exe file point to this directory location? I want to keep all the pyinstaller files in one place and allow users to have the exe file in any location they want.
Thanks for help.
Let's just see a real-life production case. Whenever you download say a pirated game, or and original copy of software, generally they are compressed together. When you unzip them, a new folder is extracted and inside that folder there are a lot of other folders. What you do to run the software is you simply double click the .exe file.
Your situation is the same. If you move the exe file outside the original extracted folder then it simply doesn't work. So, the work around way is to create a shortcut to the exe file.
Hope this clarifies your doubt :)
Shortcut. Create .exe shortcut. This way original .exe will be still in parent directory but shortcut can be placed anywhere
Most of the answers are about creating shortcuts, but its not the true solution. What we want is a clean folder having one dir and the exe outside that dir.
Unfortunately this is not possible at the moment. This issue is there since 2010 and was not fixed till date. Here is the link to that issue:
https://github.com/pyinstaller/pyinstaller/issues/1048
All they say is to create your own bootloader.
Nobody was able to give the PR for that.
I also found a blog on separating the exe from onedir with a hook file. I tried this but was unsuccessful with the latest version of Pyinstaller.
At last you have to do something hacky.
I found a way to do this:
Make a folder Modules in the same directory where executable is present.
Copy and paste all the heavy modules inside that folder.
Add the search path for that module folder inside your program:
# add this code in the top (before imports)
if getattr(sys, 'frozen', False):
app_path = os.path.join(os.path.dirname(sys.executable),"Modules")
sys.path.append(app_path) # search this path for modules
Under pyinstaller option --exclude-module, write the names of all those modules you excluded.
Use the one-file option but don't pack any other assets like images. Add all those external assets/folders outside.
Add this option: --runtime-tmpdir "Temp". With this, the executable will unpack the required files in the same directory under a new folder "Temp".
That's all, now you will get a very small sized exe file with mainly two required folders "modules" and the "temp". The booting time will also increase, and it will look a lot cleaner.
I think you should have some other files which is being required by that exe file & hence when you move exe file outside of directory it's giving you error. One of the example can be that the exe program require chrome driver & you have placed it within that directory. If you move exe program outside then you need to place the chrom driver also in the new position. I hope it will help you to detect , otherwise we can use exe program anywhere if it does not require any dependency of other files.
A workaround allowing a clean folder structure for the user that only contains the main exe and the libs folder is to create a second Python program containing only one instruction : call the executable of the main Python program within the over-crowed folder with libs. The main program being distributed without the --onefile option, the execution remains faster.
The principle is simple : create a new Python project with a single script your_program_launcher.py with this content :
import os
if __name__ == '__main__':
os.chdir(".{0}your_program_folder".format(os.sep))
os.system("your_program.exe")
The script is very simple and limit itself to move on the main program folder (to avoid resource access problems) and call the main program executable.
You just have to distribute this launch program with --onefile and possibly an icon (pyinstaller -i icon.png --onefile your_program_launcher.py) but this script doesn't use any libs (except os) so this executable will be very light and its execution immediate. Then you will have to put this program in the parent folder so you get a clean folder with this launch executable and the folder containing libraries and main program exe, without using a .bat file which is less natural.
dist
| your_program_launcher.exe
|
|____your_program_folder
| |_____lib1
| |_____lib2
| |_____libn
| | your_program.exe
Since the file is still an executable that needs the files in the subfolder, the user won't be able to move this executable anywhere he wants, but at least the user won't be lost in the folder containing all the libraries.

Moving files in Python doesn't work once made executable using pyinstaller

I have a script in Python that works fine, it creates about 1000 files once the script is ran.
So I implemented some code to move the files to a new folder once the script is ran.
import glob, os, shutil
source_dir = 'C:/Users/george/Desktop/my aemo app/a'
dst = 'C:/Users/george/Desktop/my aemo app/b'
files = glob.iglob(os.path.join(source_dir, "*.csv"))
for file in files:
if os.path.isfile(file):
shutil.copy2(file, dst)
This works fine when I run it in idle, however once made executable and I send the program to someone else, it doesn't move the files for them?
I imagine the problem with sending to another user is that their username probably isn't "george" and therefore their file structure is different than what your program is looking for. I would try using environment variables or relative pathways to move files around on a program that is to be distributed.
os documentation:
https://docs.python.org/2/library/os.html#process-parameters
relative pathways would be something like ../../file/deepFile/whatYoureLookingFor if you want to go up 2 directories from where you run the program and then dive down another path into file

How to find path to current .py file in Spyder (Anaconda)?

Set-up
I run a script on my computer, located in the directory Users/path/to/my/script.py.
In the script, I use the path to the script, e.g.,
sub_path = 'Users/path/to/my/'
os.chdir(sub_path + 'other_script/')
As you can see, I define sub_path in the code 'manually'.
Problem
I don't want to define the sub_path manually, I'd rather have Python do it for me.
I'm looking for something similar to the code I use to obtain the current working directory: os.getcwd(), but then a code to obtain the directory of the current file.
I mainly find answers similar to this one, which says,
os.path.abspath(os.path.dirname(__file__))
but in the Spyder & Anaconda set-up, this generates a NameError: name '__file__' is not defined.
What can I do?
You if you want to move back one folder/directory you use the .. in your file path.
os.chdir('../other_scripts/')
will work. You may fine it helpful to view this or the wiki.
If you want to move from where you currently are you can use './new_dir/'. If you want to automate how to find other files you may want to read here which says to use os.walk. This may be the same question.
Mark8888 pointed out to run the whole script (run file (F5)) instead of just pieces of the script
this way multiple approaches should work to get the script file location and change the current working directory
import os
# directory of script file
print(os.path.abspath(os.path.dirname(__file__)))
# change current working directory
os.chdir(os.path.abspath(os.path.dirname(__file__)))
# current working directory
print(os.getcwd())
also
import os
import sys
# directory of script file
print(os.path.abspath(os.path.dirname(sys.argv[0])))
# change current working directory
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
# current working directory
print(os.getcwd())
I add the following lines to any script I run in case I need to access data relative to the location of the script
import sys
script = sys.argv[0]
print(script)
'C:/SomeFolder/A_Subfolder/CurrentlyRunningScript.py' # changed obviously
First, save your Jupyter Notebook. Second, locate the directory your Jupyter Notebook is stored in. Thirdly, ensure that your Jupyter Notebook and CSV file are in the same place.

Created an .exe with py2exe, but when I copy paste from "dist" folder to desktop, doesn't run properly

I'm still new to Python and py2exe. I've created an executable from py2exe, but when I try to copy the single .executable from the "dist" folder it was created in, and paste it else where (let's say my desktop), When I open the executable after it's moved from the folder it opens quickly disappears (it's a console application). The program has several input/output, so this should not be the case. Its DOES function correctly if I leave it the original "dist" folder.
Is there something I'm missing? What do I need to do if I just want to take the single file (.exe) and put it on a different machine (that doesn't have Python installed) and get it to run correctly?
Thanks in advance!
In the dist directory is there a set of .dll files needed to run the exe file.
As others said, you need the .dll files to run the program. It works however if you leave the program in the dist folder (or create a copy of dist) and create a shortcut. You can move the shortcut wherever you like and it will work, since the file itself is still in the folder with the .dlls.
Put your dist folder into the system search path, and it should work fine.
This happened to me also in times past.

Categories

Resources