Does anybody know a way to embed an icon in a Python script such that when I create my standalone executable (using pyinstaller) I don't need to include the .ico file? I know this is possible with py2exe, but in my case I have to use Pyinstaller, as I was not successful using the former. I am using Tkinter.
I know about iconbitmap(iconName.ico) but that doesn't work if I wanna make a onefile executable.
Actually the function iconbitmap can only receive a filename as argument, so there needs to be a file there. You can make a Base64 version of the icon (A string version) following the link, uploading the file and copying the result in your source file as a variable string. Extract it to a temporal file, finally passing that file to iconbitmap and deleting it. It's quite simple:
import base64
import os
from Tkinter import *
##The Base64 icon version as a string
icon = \
""" REPLACE THIS WITH YOUR BASE64 VERSION OF THE ICON
"""
icondata= base64.b64decode(icon)
## The temp file is icon.ico
tempFile= "icon.ico"
iconfile= open(tempFile,"wb")
## Extract the icon
iconfile.write(icondata)
iconfile.close()
root = Tk()
root.wm_iconbitmap(tempFile)
## Delete the tempfile
os.remove(tempFile)
Hope it helps!
You probably don't need this but somebody else might find this useful, I found you can do it without creating a file:
import Tkinter as tk
icon = """
REPLACE THIS WITH YOUR BASE64 VERSION OF THE ICON
"""
root = tk.Tk()
img = tk.PhotoImage(data=icon)
root.tk.call('wm', 'iconphoto', root._w, img)
Solution by ALI3N
Follow these steps:
Edit your .spec file like this:
a = Analysis(....)
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,
a.binaries + [('your.ico', 'path_to_your.ico', 'DATA')],
a.zipfiles,
a.datas,
name=....
)
Add this to your script:
datafile = "your.ico"
if not hasattr(sys, "frozen"):
datafile = os.path.join(os.path.dirname(__file__), datafile)
else:
datafile = os.path.join(sys.prefix, datafile)
Use it this way:
root = tk.Tk()
root.iconbitmap(default=datafile)
Because this wont work after You compile your script with Pyinstaller:
root = tk.Tk()
root.iconbitmap(default="path/to/your.ico")
My Info: python3.4, pyinstaller3.1.1
This worked for me:
from tkinter import PhotoImage
import base64
img = """
REPLACE THIS WITH YOUR BASE64 VERSION OF THE ICON
"""
img= base64.b64decode(img)
root = Tk()
img=PhotoImage(data=img)
root.wm_iconphoto(True, img)
import Tkinter as tk
icon = """
REPLACE THIS WITH YOUR BASE64 VERSION OF THE ICON
"""
root = tk.Tk()
root.iconphoto(True, PhotoImage(data=icon))
Convert a .png file instead of an icon, also using utf-8 encoding with the same code above worked great for me!
Related
I want to make an application from tkinter and I converted it from py into exe files using pyinstaller, and I want the application can be use for public. But the problem is if I use it in other computer it doesn't work, because there are files that support the application. Is it possible if I made that application and use it to public? And it it's possible, what's the specific code that I can use in pyinstaller? Thanks
If your application needs attached files to work, you can store all of them in a .zip file for example, along with your .exe, on an online server.
You can use GitHub Pages for this, because you can access the raw files' contents unlike secured file storages that only allow you to download them using a browser.
Then, you can create an installer for all the files that you stored in your .zip file. That installer is what you will give to the public. Below is an example that is demonstrated through the use of this GitHub repo: https://github.com/d-002/install-wizard/
The rudimentary installer that I created will download the .zip from the server containing everything needed, then it will unzip it into a directory that the user set before, then remove the temporary .zip. Try it out:
from urllib.request import *
from zipfile import ZipFile
from os import chdir, mkdir, remove
from os.path import join, exists
from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter.messagebox import showinfo, showerror, showwarning
def browse():
global path
path['text'] = askdirectory()
def install():
if len(path['text']): # check if valid directory
try:
realpath = join(path['text'], 'application name')
if not exists(realpath):
mkdir(realpath) # create a subfolder with the application name
chdir(realpath) # move to that directory
# download the .zip containing all needed files
data = urlopen('https://d-002.github.io/install-wizard/application.zip').read()
with open('application.zip', 'wb') as f:
f.write(data)
# unzip the file
zip = ZipFile('application.zip')
zip.extractall()
zip.close()
# remove the temporary zip file
remove('application.zip')
showinfo('Done', 'Completed successfully')
except Exception as e:
showerror('Error', 'An error occured: %s' %e)
else:
showwarning('Error', 'Please enter a valid path')
def make_gui():
global tk, path
tk = Tk()
Label(tk, text='Path:').grid(row=0, column=0)
path = Label(tk, text='')
path.grid(row=0, column=1)
Button(tk, text='Browse', command=browse).grid(row=0, column=2)
Button(tk, text='Install', command=install).grid(row=1, column=1, columnspan=3, sticky='ew')
make_gui()
tk.mainloop()
I use tkinter make interface to select folder directory and i get C:/Users/dtung/Desktop/ and i want to convert it to C:\Users\dtung\Desktop\ because i use autoit to select file. this is my code
import_file_path = filedialog.askdirectory()
list = os.listdir(path=import_file_path)
import_file_path.replace("/","\\")
replace function does't work with any string or character(dont have any error it just does't work), when i print it out i just received old string.
I don't understand your problem. The code above works fine for me.
import os
from tkinter import filedialog
import_file_path = filedialog.askdirectory()
list = os.listdir(path=import_file_path)
print('Before', import_file_path)
import_file_path = import_file_path.replace("/", os.sep)
print(os.path.join(import_file_path))
print('After', import_file_path)
prints out this on my Windows machine
Before C:/Users/Mega/Documents/HuaweiMusic
C:\Users\Mega\Documents\HuaweiMusic
After C:\Users\Mega\Documents\HuaweiMusic
By the way, why do you need to mix Autoit and Python?
You can use this code to fix the problem:
import os
from tkinter import filedialog
import_file_path = filedialog.askdirectory()
list = os.listdir(path=import_file_path)
import_file_path = import_file_path.replace("/", "\\")
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.
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...
I ran into the same issue when I was wanting to include a .ico file to use as the icon for my tkinter app. Here's how I got it to work:
When building the .exe file with PyInstaller, I specified the --add-data option with the full path to my .ico.
When the app starts up, it has to unpack everything from the .exe in a temp directory, so I get the path to that temp directory and use it to specify the temp location of my .ico file.
Before the mainloop is kicked off, and using 'tempfile' to get the temp file directory and 'glob' to check for a file pattern match:
tempdir = tempfile.gettempdir()
icon_path = tempdir + '\\' + 'next_subdirectory_pattern' + '\\' + '*.ico'
icon_matches = glob.glob(icon_path)
if len(icon_matches) > 0:
logo = icon_matches[0]
else:
logo = ''
tk.Tk.iconbitmap(self, default=logo)
Probably not the most eloquent way to do it, but it works.
Here's how I did it...
To bundle the image with the executable:
Use the following command to save your icon to a folder called "icon" that will be generated in the "C:\Users<username>\AppData\Local\Temp_MEI123456" folder generated by pyinstaller at runtime:
--add-data=C:\absolute\path\to\your\ICON.ico;icon
My full command line, the string I type into the python terminal (i'm using pycharm IDE) to generate my EXE, looks like this:
pyinstaller -w -F -i=C:\absolute\path\to\your\ICON.ico --add-data=C:\absolute\path\to\your\ICON.ico;icon -n=MyEXEsNameV1 main.py
(For debug it is helpful to omit -w and add --debug=all)
doc reference: https://pyinstaller.readthedocs.io/en/stable/usage.html#options-group-what-to-bundle-where-to-search
To access the image in my python script main.py:
I used the following code to setup the tk window:
import tkinter as tk
from os import path
# main widget
self.root = tk.Tk(master)
self.root.title('Window Title Bar Name V1.0')
self.root.minsize(1234, 1234)
# get icon
try:
# Get the absolute path of the temp directory
path_to_icon = path.abspath(path.join(path.dirname(__file__), 'icon/GAUGE-ICON.ico'))
# set the icon of the tk window
self.root.iconbitmap(path_to_icon)
except:
pass
doc reference: https://pyinstaller.readthedocs.io/en/stable/runtime-information.html
In addition to accepted answer I want to add modified version:
from PIL import Image, ImageTk
from res import *
#res is a module with your base64 image string and in my case variable is icon_base64
from base64 import b64decode
pic_as_bytes=ImageTk.BytesIO(icon_base64)
my_image=Image.open(pic_as_bytes)
Please note this code will work in Python3, but could not in Python2 cause it needs differrent aproach and more conversions (the link below has exactly an example for Python2).
For more info please refer to https://github.com/Ariel-MN/Tkinter_base64_Images/blob/master/tkinter_base64_image-ico.py
I tried the following methods. The 2nd option worked for me.
Use the command you mentioned to generate exe and copy the exe file into the source folder of your project (Where the python file is).
I fix it using auto-py-to-exe which uses pyinstaller. There you
can see the command generated.
pyinstaller --noconfirm --onedir --windowed --add-data "path/assets/" "path/gui.py"
First, try using --onefile instead of --onedir. Mine didn't work
when I used --onefile. So I had to covert --onedir.
I am new in programming, and I would like to make a picture resizer with gui.
I have problems with making the gui.
I grabbed the problematic part of the code:
from tkinter import *
from tkinter import filedialog
import os, backend, glob2, cv2
loaded_pics=[]
picture_read=[]
window = Tk()
browsed_dir = StringVar()
browsed_dir.set(filedialog.askdirectory(initialdir="/",title='Please select a directory'))
file_path = browsed_dir.get()#+"/"
for filename in os.listdir(file_path):
if filename.endswith(('.jpg', '.jpeg', '.gif', '.png')):
loaded_pics.append(filename)
print(loaded_pics)
try:
for images in loaded_pics:
imgs = cv2.imread(images, 1)
print(imgs)
except:
print("ERROR")
window.destroy()
window.mainloop()
So, I have got a list of .png/.jpg/.bmp files, I can print the list, but I cannot read them with cv2.imread(), when I print(imgs), I got "None"-s.
(I have not managed to make this with glob2. It is work well with current directory, but I could not make it with filedialog.)
I hope someone can help.
Thanks in advance!
You are building a list of filenames in loaded_pics. But this doesn't include the name of the directory, file_path. So when you call imread, your image is actually located at file_path/filename, but you are only passing filename. So cv2 cannot find your file, and returns None.
I'm starting to learn Python, and I thought to create a converter from a file to another (for example, from png to avi or between other file extensions) under Windows OS for now.
I wrote a script which works fine and it completes the conversion process, but I want improve it in functionality (and then in graphics); I'm using Tkinter and I thought to load the files with the possibility to drag-and-drop them as input for the next conversion command, instead of open a folder in which to put files as "input source". I found this topic (python drag and drop explorer files to tkinter entry widget) and I used it in this way:
import sys
import os
import Tkinter
from tkdnd_wrapper import TkDND
import shlex, subprocess
from subprocess import Popen, PIPE
import glob
import shutil
root = Tkinter.Tk()
dnd = TkDND(root)
entry = Tkinter.Entry()
entry.grid()
def handle(event):
inputfilespath = event.data
event.widget.insert(0, inputfilespath)
filesdir = os.path.dirname(os.path.realpath(inputfilespath))
files = glob.iglob(os.path.join(filesdir, "*.myext"))
for inputfilespath in files:
if os.path.isfile(inputfilespath):
subprocess1 = subprocess.Popen([...conversion command given as list, not string...], shell=True)
print "\n\nConversione in corso..."
subprocess1.wait()
subprocess1.terminate()
print "\n\nProcesso terminato!"
dnd.bindtarget(entry, handle, 'text/uri-list')
root.mainloop()
The problems:
If filename has a space, there is no conversion, and process ends without even notify any error too. "inputfilespath" wants to be the generic name for all the input files which I selected, and (for what I read) I can't (?) use quotes for an environment variable hoping to include filename's whitespace...
I tried to copy different files (with the same file extension and without whitespaces into the filename) in the same folder, and if I drag-and-drop only one of them on the Entry widget, the process starts fine (and this is great!), but it continues also for all the other no-selected files with the same extension in the same folder, whereas if I drag-and-drop multiple files on the Entry widget, no conversion occurs....
It seems that filenames containing whitespace are being wrapped in braces
(Tcl list style). To get a usable filelist, you should be able to do
something like:
import Tkinter
from untested_tkdnd_wrapper import TkDND
def handle(event):
files = root.tk.splitlist(event.data)
for filename in files:
event.widget.insert('end', filename)
root = Tkinter.Tk()
lb = Tkinter.Listbox(root, width=50)
lb.pack(fill='both', expand=1)
dnd = TkDND(root)
dnd.bindtarget(lb, handle, 'text/uri-list')
root.mainloop()
Just use the tkinter file dialog, then just have it insert the files into the entry box.
Example:
filedialog = tkFileDialog.askopenfilenames(*options*)
entry.insert(END, filedialog)
Example Using StringVar:
entryVar = StringVar()
entry = Entry(textvariable=entryVar)
filedialog = tkFileDialog.askopenfilenames(*options*)
entryVar.set(filedialog
Hope this helps!