emptybrother7
emptybrother7

Reputation: 1

How can I fix _Tkinter.TclError: couldn't open PNG?

I am new to python and have been messing with Tkinter and PyInstaller.

When I package my script using;

pyinstaller --onefile --noconsole window.py  

and try to open it I get this error;

Traceback (most recent call last):
File "window.py", line 10, in <module>
File "tkinter\__init__.py", line 4093, in __init__
File "tkinter\__init__.py", line 4038, in __init__
_tkinter.TclError: couldn't open "orange.png": no such file or directory

Maybe I am missing something,
Here is my code;

import tkinter as tk
from tkinter import PhotoImage

casement = tk.Tk() # Window
casement.title('Squeeze') # Window Title
casement.geometry('800x800') # Window Size
casement.resizable(True, True) # Winow Resizable x, y
casement.configure(bg='white') # Window Background Color
icon=PhotoImage(file="orange.png") # Window Icon File Var
casement.iconphoto(True,icon) # Window Icon

icon = tk.PhotoImage(file='orange.png') # Button Icon
button = tk.Button(casement, image=icon, text="Open Image", compound=tk.LEFT, bg='orange') # Button
button.pack(ipadx=5, ipady=5) #Button Placement

casement.mainloop()

Upvotes: 0

Views: 985

Answers (1)

JRiggles
JRiggles

Reputation: 6810

When you create a single-file executable with Pyinstaller it will place resources in a specially created "MEI" directory in your user temp folder (named something like "_MEI1234"). You'll need to tell Pyinstaller where to look for assets when you run the build command like so:

pyinstaller -w -F -i ".\src\assets\icons\appicon.ico" "<your filename.py here>" --add-data ".\src\assets\;assets"

This way, Pyinstaller understands that it needs to include everything from your source assets folder (src\assets\ in my example, but yours is probably different). Then, everything in your assets folder will be included in the MEI directory used by the executable.

To access these resources when your Python app runs as an exe, you can retrieve the sys._MEIPASS and prepend it to the resource path. I've put together a function that handles exactly this below!

import sys
from pathlib import Path


def fetch_resource(rsrc_path):
    try:
        base_path = sys._MEIPASS
    except AttributeError:  # running as script, return unmodified path
        return rsrc_path  
    else:  # running as exe, return MEI path
        return base_path.joinpath(rsrc_path)

Then, for any external assets you can retrieve them by calling fetch_resource() on them like this

icon = PhotoImage(file=fetch_resource("my_assets/orange.png"))

Just make sure that whatever the root directory containing my_assets/orange.png is included with the build via --add-data ".\my_source_code_dir\my_assets\;my_assets" (and again, use whatever path names are appropriate to your particular project)

Upvotes: 2

Related Questions