I'm very new to Python and I need help with some basic stuff. I wrote a small program that I want do convert to .exe. The program works fine if I run it with Python.
But when I use pyinstaller I can't access external resources. My goal is to have myprogram.exe in the same directory as an assets folder. The assets folder contains images and a json file that I W/R ...
The command I'm using is just this, and no other config:
pyinstaller --onefile --name myprogram gui.py
So for the code I have this:
OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path("./assets")
def relative_to_assets(path: str) -> Path:
return ASSETS_PATH / Path(path)
Using like so:
canvas.place(x = 0, y = 0)
image_image_1 = PhotoImage(
file=relative_to_assets("image_1.png"))
image_1 = canvas.create_image(
225.0,
114.0,
image=image_image_1
)
And to load/read the JSON file:
def loadJSONFile():
data = {}
if os.path.isfile(relative_to_assets("data.json")) and os.access(ASSETS_PATH, os.R_OK):
WALLETSLISTFILE = open(relative_to_assets("data.json"),"r")
data = json.loads(WALLETSLISTFILE.read())
else:
print("Either file is missing or is not readable, creating file...")
with io.open(os.path.join(ASSETS_PATH, 'data.json'), 'w') as db_file:
db_file.write(json.dumps({}))
WALLETSLISTFILE = open(relative_to_assets("data.json"), "r")
data = json.loads(WALLETSLISTFILE.read())
return data
Project Structure with python (which works):
So what I want is to generate the gui.exe with --onefile and later join the folder "assets" in same .exe directory.
But with that code above the path to the files is incorrect and the program doesn´t starts (because I'm not handling the exception right now)
So to answear to my own question I changed my code regarding to the IO operations for the JSON file and added a few options when using pyinstaller for the other resources.
So regarding to pyinstaller:
pyinstaller --onefile --name "gui" --add-data "assets/*.png;./assets" --add-data "assets/*.ico;./assets" gui.py
So when using the .exe generated by pyinstaller and using these path definitions:
OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path("./assets")
def relative_to_assets(path: str) -> Path:
return ASSETS_PATH / Path(path)
canvas.place(x = 0, y = 0)
image_image_1 = PhotoImage(
file=relative_to_assets("image_1.png"))
image_1 = canvas.create_image(
225.0,
114.0,
image=image_image_1
)
My assets are stored in a temp folder:
C:\Users\username\AppData\Local\Temp\_MEI89482\assets
That´s ok for static resources like images and files to only read, because when the program closes the temp folder also goes away.
So to my JSON file I had to change the Path to a more persistent location. So I used the APPDATA OS env. var., like so:
import os
APPDATA_PATH = os.environ['APPDATA']
PROGRAM_FOLDER = 'MyData'
def relative_to_appdata_folder():
return os.path.join(APPDATA_PATH,PROGRAM_FOLDER)
def relative_to_json_file(path: str) -> Path:
return relative_to_appdata_folder() / Path(path)
def loadJSONFile():
data = {}
if os.path.isfile(relative_to_json_file("data.json")) and
os.access(relative_to_appdata_folder(), os.R_OK):
WALLETSLISTFILE = open(relative_to_json_file("data.json"), "r")
data = json.loads(WALLETSLISTFILE.read())
else:
print("Either file is missing or is not readable, creating file...")
with io.open(os.path.join(relative_to_appdata_folder(), 'data.json'), 'w') as db_file:
db_file.write(json.dumps({}))
WALLETSLISTFILE = open(relative_to_json_file("data.json"), "r")
data = json.loads(WALLETSLISTFILE.read())
return data
Also I needed to check if the directory exists do create the file "data.json" in the previous Path:
if not os.path.exists(relative_to_appdata_folder()):
os.makedirs(relative_to_appdata_folder())
So that's how I got it to work and my data.json file is stored here:
C:\Users\username\AppData\Roaming\MyData
If anyone has a more elegant solution let me know, I love to learn more.