How do I bundle my tkinter based application with my custom icon? - python

I have a tkinter based application structured as follows:
import tkinter as tk
class App(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, self.master)
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.iconbitmap("my_logo.ico")
self.master.title("Example")
self.master.minsize(250, 50)
def create_widgets(self):
self.label = tk.Label(self.master, text="hello world")
self.label.pack()
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()
When I run the .py file from the command line, my logo replaces the default tkinter feather logo in the applications main window as expected. I am even able to freeze and bundle my application with pyinstaller using the following command:
pyinstaller -i my_logo.ico my_application.py
Unfortunately, when I attempt to run the .exe file generated by this process I am met with the following error:
Traceback (most recent call last):
File "my_application.py", line 22, in <module>
File "my_application.py", line 7, in __init__
File "my_application.py", line 11, in configure_gui
File "tkinter\__init__.py", line 1865, in wm_iconbitmap
_tkinter.TclError: bitmap "my_logo.ico" not defined
[5200] Failed to execute script my_application
I have scoured this site and others in search of a solution that works in my case and have found none. Any direction would be greatly appreciated!

I've found it easier for me just to store the image as a .py module, then PyInstaller handles it like any other module and the basic command line command to make the exe work without anything special:
Script to make image.py file:
import base64
with open("my_logo.ico", "rb") as image:
b = base64.b64encode(image.read())
with open("image.py", "w") as write_file:
write_file.write("def icon(): return (" + str(b) + ")"
Then import the module as the image:
import tkinter as tk
import image
class App(tk.Frame):
def __init__(self, master):
self.master = master
tk.Frame.__init__(self, self.master)
self.configure_gui()
self.create_widgets()
def configure_gui(self):
self.master.tk.call('wm', 'iconphoto', self.master._w, tk.PhotoImage(data=image.icon()))
self.master.title("Example")
self.master.minsize(250, 50)
def create_widgets(self):
self.label = tk.Label(self.master, text="hello world")
self.label.pack()
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()
Otherwise you basically have to call out adding the .ico file in the build command, then in your script need to add lines to determine where the unpacked pyinstaller-packed script's directory is, then adjust your path to the packed .ico file.
Discussed here: Bundling data files with PyInstaller (--onefile)
But I find my way easier.

Related

Tkinter Class Import Module Issue

I'm trying to keep my code clean by separating the GUI from the logic.
Inside of the 'main.py' file, I'd like to call functions from other files that are imported to build the GUI.
The problem is that I cannot figure out how to build a GUI from the 'main.py' file when I try to call another file as an imported module.
Here's what I have in the 'main.py' file:
from tkinter import *
import create_btn
class Main(Tk):
def __init__(self):
super().__init__()
self.title('Main Window')
self.geometry('600x400')
self.eval('tk::PlaceWindow . center')
if __name__ == '__main__':
app = Main()
app.mainloop()
And here is what I have in the 'create_btn.py' file:
from tkinter import *
def createBTN(self):
self.b1 = Button(root, text='B1')
self.b1.pack()
So, how exactly can I build a simple button from another file that I want to import into the 'main.py', or in other words, how do I get the 'create_btn.py' file to build the button inside of the 'main.py' file? A simple example would be greatly appreciated.
I have completely rewritten your code:
# main.py
from tkinter import *
import create_btn
class Main_app:
def __init__(self):
self.root = Tk()
self.root.title("Main Window")
self.geometry("600x400")
self.eval('tk::PlaceWindow . center')
self.button = create_btn.create(self.root)
self.root.mainloop()
a = Main_app()
# create_btn.py
from tkinter import *
class create:
def __init__(self, root):
btn = Button(root, text="Button")
btn.pack()
return btn
This way you can edit the button later in main.py

Python tkinter errors setting icon, TclError linux

I am fairly new to python and very new to GUI's so please let me know if I have made an obvious mistake.
This is also my first post so please let me know if I need to edit my question in some way.
I don't understand why I am getting errors when trying to set an icon to a tkinter GUI.
I have already tried the following posts:
tkinter TclError: error reading bitmap file
https://www.delftstack.com/howto/python-tkinter/how-to-set-window-icon-in-tkinter/
There are other stack exhange posts on this topic but they are for windows and/or their OP's normally can get the icon to apear once then it doesn't work. This isn't my situation the programs fail to run. Though I have tried some of their suggestions as well.
I have tried three different ways of coding this:
Method 1:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
root.iconphoto(True, PhotoImage(os.path.join(os.getcwd(),'favicon-3.png')))
my_gui = MainWindow(root)
root.mainloop()
Result:
runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 23, in <module>
root.iconphoto(True, PhotoImage(os.path.join(os.getcwd(),'favicon-3.png')))
File "/usr/lib/python3.8/tkinter/__init__.py", line 2116, in wm_iconphoto
self.tk.call('wm', 'iconphoto', self._w, "-default", *args)
TclError: can't use "~./envs/thermTD_project/thermTD/favicon-3.png" as iconphoto: not a photo image
File existence check:
In [42]: os.path.exists(os.path.join(os.getcwd(),'favicon-3.png'))
Out[42]: True
Method 2:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
root.iconbitmap(os.path.join(os.getcwd(),'favicon-3.ico'))
my_gui = MainWindow(root)
root.mainloop()
Result:
In [43]: runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 22, in <module>
root.iconbitmap(os.path.join(os.getcwd(),'favicon-3.ico'))
File "/usr/lib/python3.8/tkinter/__init__.py", line 2071, in wm_iconbitmap
return self.tk.call('wm', 'iconbitmap', self._w, bitmap)
TclError: bitmap "~./envs/thermTD_project/thermTD/favicon-3.ico" not defined
File existence check:
In [44]: os.path.exists(os.path.join(os.getcwd(),'favicon-3.ico'))
Out[44]: True
Method 3:
from tkinter import *
import os
class MainWindow:
def __init__(self, master):
self.master = master
master.title('TD')
#self.iconbitmap(default = 'fist.ico')
root = Tk()
img = PhotoImage(os.path.join(os.getcwd(),'favicon-3.ico'))
root.tk.call('wm','iconphoto', root._w, img)
my_gui = MainWindow(root)
root.mainloop()
Result:
In [45]: runfile('~./envs/thermTD_project/thermTD/untitled2.py', wdir='~./envs/thermTD_project/thermTD')
Traceback (most recent call last):
File "~./envs/thermTD_project/thermTD/untitled2.py", line 21, in <module>
root.tk.call('wm','iconphoto', root._w, img)
TclError: can't use "~./envs/thermTD_project/thermTD/favicon-3.ico" as iconphoto: not a photo image
File existence check:
See method 1
The code without any of the icon setting lines works as expected.
I have tried to gif, png and ico on each method. I am trying other file types now.
NB: While the filename is favicon actually they are ico, gif, png files of 64x64 pixles. Even the favicon ico only has 1 type of icon in it (if thats the right phrase). Its just I got them from a favicon which I converted to different formats using mogrify.
OS: Ubuntu 20.04.1 LTS
python 3.8.2
IDE: spyder 4.1.5
P.S. I would be gratefull to know what the syntax for including this in my init function. I am trying to do object orientated GUI's I am very much still learning.

Tkinter can't even invoke a simple "button"

Started with simple Tkinter lessons, I'm stuck in the case even that simple code doesn't work:
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button'); b.pack()
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/../anaconda3/lib/python3.6/tkinter/__init__.py", line 2366, in __init__
Widget.__init__(self, master, 'button', cnf, kw)
File "/Users/../anaconda3/lib/python3.6/tkinter/__init__.py", line 2296, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "button" command: application has been destroyed
And can't find the reason why, considering that this code is from official documentation.
On the other hand, another code works:
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
self.hi_there = tk.Button(self)
self.hi_there["text"] = "Hello World\n(click me)"
self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
self.quit = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.quit.pack(side="bottom")
def say_hi(self):
print("hi there, everyone!")
root = tk.Tk()
app = Application(master=root)
app.mainloop()
I have tried to update tk from conda: conda install -c anaconda tk, but nothing change. Can't figure out why.
The only way I was able to reproduce your error is by building the code directly in the IDLE Shell and closing the root window that pops up before creating the button.
That said it is very odd to write a GUI in the Shell like this. If you do not close the tkinter window the code works fine. However GUI development should be done in the editor in a .py file and ran all at once. Simple fix is to not close the root window before everything else in the GUI has been added.
Proper fix is to build you GUI in a .py file and then run it.
I am not sure why you are saying that the editor is not working for you. When I copy your exact code it works fine on my end:
All that said you really do not need to build your code in the Python IDLE. It would be much better to use something like PyCharm or Eclipse/PyDev. Those are my Go to IDE tools.
One thing to note about Python's IDLE is it will not run code from the editor until you have saved the .py file.
Though not 100% required in the Python IDLE the mainloop() is a requirement for tkinter to work properly. Outside of Python's IDLE most other IDE environments requite the mainloop() so it is good practice to always include it.
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button')
b.pack()
root.mainloop()
I think you forgot to add root.mainloop() at the end.
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button'); b.pack()
root.mainloop()

Create directory in a selected path using Python Tkinter 2.7

I would like to create a class in Tkinter Python 2.7 that creates a new directory using the name introduced by the user in a field after the directory location was chosen from filedialog.
As an example I would like something like this:
User introduces the name of the directory and the following structure should be created:
$HOME\a\<name_introduced_by_the_user_in_the_field>\b
$HOME\a\<name_introduced_by_the_user_in_the_field>\c
I was thinking to start simple and create a simple directory, but I am getting an error.
Here is what I have tried:
class PageThree(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self,text="Insert the name of your project",font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.projectnamevar=tk.StringVar()
projectname=tk.Entry(self,textvariable=projectnamevar)
projectname.pack()
button1 = tk.Button(self, text="Create the directory", command=self.create_dir)
button1.pack()
def create_dir(self):
call(["mkdir",projectnamevar.get()])
Error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib64/python2.7/lib-tk/Tkinter.py", line 1470, in __call__
return self.func(*args)
File "program.py", line 118, in create_dir
call(["mkdir",self.projectnamevar.get()])
AttributeError: PageThree instance has no attribute 'projectnamevar'
How can I accomplish the whole stuff?
P.S. I am quite new to programming
Your Variable projectnamevar cannot be found in the class as you have not saved it as such, try it with
self.projectnamevar = tk.StringVar()
Also, you might want to use the os module instead of calling it on the system, you can use it like this
import os
path = "~/my/path/"
os.mkdir(path)
import os, sys
if sys.version_info[0] == 3:
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
from tkinter.ttk import *
elif sys.version_info[0] == 2:
print ("The Script is written for Python 3.6.4 might give issues with python 2.7, let the author know")
print ("Note Python 2.7 CSV has a empty line between each result. couldn't find a fix for that")
from Tkinter import *
import tkMessageBox as messagebox
import tkFileDialog as filedialog
from ttk import Combobox
class temp:
def __init__(self):
self.top = Tk()
self.lab = Label(self.top, text='UserFiled')
self.en = Entry(self.top, width =25)
self.but = Button(self.top, text='Submit',command = self.chooseFolder)
self.lab.grid(row=0, column=0)
self.en.grid(row=0, column=1)
self.but.grid(row=0, column=2)
self.top.mainloop()
def chooseFolder(self):
directory = filedialog.askdirectory()
print(directory)
newPath = os.path.join(directory, self.en.get())
if not os.path.exists(newPath):
os.chdir(directory)
os.mkdir(self.en.get())
obj = temp()

_tkinter.TclError: encountered an unsupported criticial chunk type "exIf"

So I am trying to put some pictures into my window and whenever I run the program it gives me this error:
_tkinter.TclError: encountered an unsupported criticial chunk type "exIf"
I tried putting it into other formats, such as .jpg, .png and .gif, but they don't work. Can you please help me?
This is my code:
from tkinter import *
from tkinter import ttk
class Window:
def __init__(self, master):
self.master = master
master.iconbitmap('ta.ico')
master.title('Tamagochi')
master.minsize(width=480, height=240)
master.maxsize(width=480, height=240)
self.pic1 = PhotoImage(file='pic1.png')
self.pic2 = PhotoImage(file='pic2.png')
self.pic3 = PhotoImage(file='pic3.png')
self.pic4 = PhotoImage(file='pic4.png')
self.smFrame = ttk.Frame(master)
self.smButton1 = ttk.Button(self.smFrame, text='Start', command=self.start)
self.smButton2 = ttk.Button(self.smFrame, text='Options', command=self.options)
self.smButton3 = ttk.Button(self.smFrame, text='Quit', command=self.quit)
self.smPhoto1 = ttk.Label(self.smFrame, image=self.pic1)
self.smFrame.pack()
self.smPhoto1.grid()
self.smButton1.grid(pady=40, padx=200)
self.smButton2.grid(pady=0, padx=200)
self.smButton3.grid(pady=40, padx=200)
def start(self):
pass
def options(self):
pass
def quit(self):
exit()
root = Tk()
Window(root)
root.mainloop()
This is the full error:
Traceback (most recent call last):
File "C:/Users/NemPl/Desktop/ProLan/Python/Python programi/Tamagochi/Tamagochi.py", line 35, in <module>
Window(root)
File "C:/Users/NemPl/Desktop/ProLan/Python/Python programi/Tamagochi/Tamagochi.py", line 14, in __init__
self.pic3 = PhotoImage(file='pic3.png')
File "C:\Users\NemPl\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 3539, in __init__
Image.__init__(self, 'photo', name, cnf, master, **kw)
File "C:\Users\NemPl\AppData\Local\Programs\Python\Python36-32\lib\tkinter\__init__.py", line 3495, in __init__
self.tk.call(('image', 'create', imgtype, name,) + options)
_tkinter.TclError: encountered an unsupported criticial chunk type "exIf"
PhotoImage is a tkinter class which, unfortunately, can not deal with .png files.
The solution is to rely on the Python Imaging Library1 which offers support to several image formats and transforms them to image objects that can be "understood" by tkinter:
from PIL import Image, ImageTk
self.img1 = Image.open("pic1.png")
self.pic1 = ImageTk.PhotoImage(self.img1)
1. You can install PIL as described here.
Just wanted to share a solution that solved for me!
I figured out that other '.png' images were working with the same code, so the problem was happening with the image file.
What solved my problem was simply modifying the image (just saved it from a image editor).

Categories

Resources