pythontkinterexephoto

python pyinstaller bundle image in GUI to onefile EXE?


I've successfully created an EXE file from python tkinter GUI which includes images. see the following code:

lblLogo=Label(main)
lblLogo.grid(row=3,column=11,rowspan=4)

try:
    filelocation="C:/Documents/Python/ScreenPartNumberProgram/"
    KasonLogo=PhotoImage(file=filelocation+"KasonLogo.png")
    KasonLogo=KasonLogo.zoom(25,20)
    KasonLogo=KasonLogo.subsample(50,50)
    lblLogo.config(image=KasonLogo)
    icon=PhotoImage(file=filelocation+"KasonLogo.png")
    main.tk.call('wm','iconphoto',main._w,icon)
except:
    print("warning: could not load photos")
    lblLogo.config(text="[KasonLogo.png]")

the exe file opens and works perfectly on my computer. I used this command to get the .exe:

pyinstaller --onefile --noconsole myscript.py

The only problem is this file requires the image to be in the same folder or it doesn't show on the label. How can I get the image bundled into the EXE without requiring an external file?

Thanks in advance

edit: I looked at the links provided,did more research, and still haven't solved the issue. The program works only if the image is in the same folder. I'd really like to make it work without requiring an external image file. the resource_path technique doesn't seem to work for me either...

I also tried modifying my code to use PIL's ImageTk and same issue. program loads image only if the external image file is in the folder with it.


Solution

  • HOLY COW ! After a few more days of headaches I FINALLY found an approach that works... here is what I did to get the program to work without requiring an external image file:

    step 1: encode the image file (note that it must be a GIF. you can convert it using paint):

    import base64
    with open("MyPicture.gif", "rb") as image_file:
        encoded_string = base64.b64encode(image_file.read())
    print(encoded_string)#print string to copy it (see step 2)
    

    step 2: the next step is to copy and paste the string into a separate myimages.py file and store it as variable. for example:

    imageString = b'R0lGODlhyADIAPcAAAA .....blah blah really long string.......'
    

    step 3: then import the myimages.py into main program and call the variable

    from myimages import *
    pic=imageString#GIF decoded to string. imageString from myimages.py
    render = PhotoImage(data=pic)
    myLabel.config(image=render)
    

    step 4: in the spec file: include the myimages.py as data

    # -*- mode: python -*-
    
    block_cipher = None
    
    #instructions: pyinstaller --onefile --noconsole myProgram.spec
    
    file_name = 'myProgram'
    add_files=[('myimages.py','module')]
    
    a = Analysis([file_name+'.py'],
                 pathex=['C:\\Program Files (x86)\\Python36-32\\Scripts'],
                 binaries=[],
                 datas=add_files,
                 hiddenimports=[],
                 hookspath=[],
                 runtime_hooks=[],
                 excludes=exclude_list,
                 win_no_prefer_redirects=False,
                 win_private_assemblies=False,
                 cipher=block_cipher)
    pyz = PYZ(a.pure, a.zipped_data,
                 cipher=block_cipher)
    exe = EXE(pyz,
              a.scripts,
              a.binaries,
              a.zipfiles,
              a.datas,
              name=file_name,
              debug=False,
              strip=False,
              upx=True,
              runtime_tmpdir=None,
              console=False )
    

    step 5: finally you can compile to EXE:

    pyinstaller --onefile --noconsole myProgram.spec
    

    SIGH If anyone has a better solution, please post it here. until then I'm glad something works...