This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 2 years ago.
I am trying to make this UI from a stimulus as part of an assessment for school. I tried to import the provided school logo and banner on the top frame of the page and put images on the canvas but have yet to achieve any results. When I run the code, the pictures won't load at all. The code that I was working with is as followed:
from tkinter import *
import random
import time
import sqlite3
from tkinter import simpledialog
from tkinter import messagebox
from tkcalendar import *
from tkinter import ttk
import math
from PIL import Image, ImageTk
import winsound
#-------------Frames setup--------------------------
class VendingApp(Tk):
def __init__(self):
Tk.__init__(self)
self._frame = None
self.switch_frame(Home)
def switch_frame(self, frame_class):
#Destroys current frame and replaces it with a new one.
new_frame = frame_class(self)
if self._frame is not None:
self._frame.destroy()
self._frame = new_frame
self._frame.pack()
####-----------------------Home page---------------------------
class Home(Frame):
def __init__(self, master):
Frame.__init__(self, master)
topFrame = Frame(self,width = 1024, height = 100, bd = 2, bg = "black")
topFrame.pack()
canvas_for_logo = Canvas(topFrame, height=100, width=100, bg = 'green') ##logo image
canvas_for_logo.grid(row=0, column=0, sticky='ne')
img_logo = Image.open("pic/sitelogo.png")
img_logo = img_logo.resize((40,40), Image.ANTIALIAS)
logo = ImageTk.PhotoImage(img_logo)
canvas_for_logo.create_image(0, 0, anchor=NW, image=logo)
canvas_for_banner = Canvas(topFrame, bg='red', height=100, width=924) #banner image
canvas_for_banner.grid(row=0, column=1, sticky='nw')
img_banner = Image.open("pic/banner.jpg")
img_banner = img_banner.resize((40,40), Image.ANTIALIAS)
banner = ImageTk.PhotoImage(img_banner)
canvas_for_banner.create_image(0, 0, anchor=NW, image=banner)
MidFrame = Frame(self,width = 1024, height = 628, bd = 2)
MidFrame.pack()
MidFrame.grid_propagate(False)
BottomFrame = Frame(self,width = 1024, height = 50, bd = 2, bg = "black")
BottomFrame.pack()
BottomFrame.grid_propagate(False)
if __name__ == "__main__":
root = VendingApp()
#Sets the size of the window
root.geometry("1024x768")
#Renames the TITLE of the window
root.title("Vending machine")
root.geometry("1024x768")
root.resizable(False, False)
root.mainloop()
I decided to make a separate file to test if the image would load without class, and it did. Codes are as followed:
from tkinter import ttk
from tkinter import*
import time
from PIL import Image, ImageTk
root = Tk()
canvas_for_logo = Canvas(root, height=100, width=100)
canvas_for_logo.pack()
img = Image.open("pic/sitelogo.png")
img = img.resize((105,105), Image.ANTIALIAS)
logo = ImageTk.PhotoImage(img)
canvas_for_logo.create_image(0, 0, anchor=NW, image=logo)
canvas_for_banner = Canvas(root, bg='red', height=100, width=924) #banner image
canvas_for_banner.pack()
img_banner = Image.open("pic/banner.jpg")
img_banner = img_banner.resize((924,100), Image.ANTIALIAS)
banner = ImageTk.PhotoImage(img_banner)
canvas_for_banner.create_image(0, 0, anchor=NW, image=banner)
root.mainloop()
Can someone please tell me what I did wrong? All replies are much appreciated. Thank you.
This is a typical Tkinter bug. I don't want to go into details cause I don't fully understand why it happens either, but it has something to do with the garbage collector and the fact that it doesn't consider the objects you have created for storing those images like being in use, so it deletes them; or something like that.
Luckily, it has an easy solution: you can either create an internal list variable, let say, self._images that stores each image you are using, something like:
self._images = list()
(...)
self._images.append(logo)
(...)
self._images.append(banner)
Or, you could assign to each canvas instance an attribute image (or img, it doesn't really matters) that stores the image instance it is going to carry. In your code, it will look similar to:
canvas_for_logo.image = logo
(...)
canvas_for_banner.image = banner
This way, you can avoid the garbage collector deleting what it shouldn't, cause now it acknowledges that this instances are being in use.
Related
I've created a Tkinter app to convert the mailing lists we use at my company. All the functionality seems to work fine I just can't get the logo image to work!
I have created an app without using a Class and it is exactly how I'd want it.
The way I'd want the app to look:
However, I can only get the converter to work with this version using a Class:
Current working version:
The code for the working version is below (the one without any logo), I've excluded all the excel conversion code as it's quite long.
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("SPB Mailing List Cleaner")
self.pack(fill='both', expand=1)
self.filepath = tk.StringVar()
convertButton = tk.Button(self, text='Convert',
command=self.convert, bg="#00a69d", fg="white", height="2", width="15")
convertButton.place(x=242, y=200)
filepathText = tk.Entry(self, textvariable=self.filepath)
filepathText.pack()
filepathText.place(x=237, y=250)
def convert(self):
pass # left out code
def show_file_browser(self):
self.filename = filedialog.askopenfilename()
return self.filename
def first_browser(self):
file = self.show_file_browser()
self.filepath.set(file)
form = tk.Tk()
form.geometry("600x300")
form.resizable(0, 0)
app = Window(form)
form.mainloop()
This is the code for the image in the first screenshot (the one with the logo) ('The way I'd want the app to look').
import tkinter as tk
from PIL import Image, ImageTk
from tkinter.filedialog import askopenfile
from tkinter import filedialog as fd
import os
root = tk.Tk()
canvas = tk.Canvas(root, width = 600, height = 300)
canvas.grid(columnspan=3, rowspan=3)
#logo
logo = Image.open('logo.png')
logo = ImageTk.PhotoImage(logo)
logo_label = tk.Label(image = logo)
logo_label.image = logo
logo_label.grid(column=1, row=0)
#instructions
instructions = tk.Label(root, text="Select an appropriate '.xlsx' file for cleaning.")
instructions.grid(columnspan=3, column=0, row=1)
def open_file():
browse_text.set("loading...")
file = askopenfile(initialdir=os.path.normpath("C://"), parent=root, mode='rb', title="Choose a file", filetypes=[("Excel files", ".xlsx .xls")])
if file:
print(file.name)
#browse button
browse_text = tk.StringVar()
browse_btn = tk.Button(root, textvariable=browse_text, command=lambda:open_file(), bg="#00a69d", fg="white", height="2", width="15")
browse_text.set("Select File")
browse_btn.grid(column=1, row=2)
canvas = tk.Canvas(root, width = 600, height = 150)
canvas.grid(columnspan=3)
root.mainloop()
The question is how do I get the logo to work with the version that doesn't have a logo, i.e make the image work within a class.
I would really appreciate any feedback/help on this. I had a look at some posts that describe a similar issue but I'm quite new to coding so can't wrap my head around it all.
Things you have to do:
load the image in class.
Currently Tkinter only supports , the GIF, PGM, PPM, and PNG file formats as of Tkinter 8.6
To support other file formats such as PNG,JPG, JPEG, or BMP, you can use an image library such as Pillow to convert them into the supported format.
image = Image.open('./assets/python.jpg')
python_image = ImageTk.PhotoImage(image)
self.label=Label(image=python_image)
The another solution is to use to make the image variable a class variable by adding self. so final thing would look like self.img_var_name=image.
Alternatively you can append the images in a list, or use
image = image)
The goals is to increase the refrence count of image variable.
See Why does Tkinter image not show up if created in a function? for more details.
I just had to put the image code inside the init_window function.
def init_window(self):
#logo
logo = Image.open('logo.png')
logo = ImageTk.PhotoImage(logo)
logo_label = tk.Label(image = logo)
logo_label.image = logo
logo_label.place(x=160, y=20)
#instructions
instructions = tk.Label(form, text="Select an appropriate '.xlsx' file for cleaning.")
instructions.place(x=180, y=280)
self.master.title("SPB Mailing List Cleaner")
self.pack(fill = 'both', expand = 1)
self.filepath = tk.StringVar()
convertButton = tk.Button(self, text = 'Convert',
command = self.convert, bg="#00a69d", fg="white", height="2", width="15")
convertButton.place(x = 242, y = 200)
I am trying to get an image to display in a tkinter canvas.
I know the Canvas works with shapes and text, but image isn't working.
I am using PIL ImageTK.PhotoImage, however it also doesn't work creating a Tkinter.PhotoImage using a '.ppm' image.
The 'png' image is stored in the same directory as the python file.
import tkinter as tk
from PIL import Image, ImageTk
class Window:
def __init__(self):
self.window = tk.Tk()
self.window.title("COMP")
self.window.geometry("1200x600")
topframe = tk.Frame(self.window, highlightbackground='black', highlightthickness=1)
topframe.pack(side='top')
self.noteview = NoteView(topframe, self.songString)
class NoteView:
def __init__(self, frame):
self.canvas = tk.Canvas(frame, width=60, height=200)
self.canvas.pack()
self.canvas.create_text(15, 190, text='hi')
im = Image.open('png.png')
self.image = ImageTk.PhotoImage(im)
self.canvas.create_image(20, 20, anchor='nw', image=self.image)
w = Window()
TypeError: 'PhotoImage' object is not callable
The proble is that the tkinter somehow bings the image to the window, not in your case. You should use self.canvas.image = ImageTk.PhotoImage(im) and self.canvas.create_image(20, 20, anchor='nw', image=self.canvas.image). Hope that's helpful!
I am trying to get a .gif animation to work next to a picture with buttons on it. but i seem to be having a issue, I am importing these modules
"import Tkinter" and "from PIL import Image, ImageTk, ImageSequence"
But, as soon as I make "import Tkinter"---"from Tkinter import *"
It says Tkinter is not defined, I have searched.. and searched.... and I cannot for the death of me find a solution.
I have to use "from Tkinter import *" because I don't know where to find the spesifics for a substetute for " * " I also need to use "Label", "bg" and "relief"
here is my code:
import Tkinter
from PIL import Image, ImageTk, ImageSequence
class App:
def __init__(self, parent):
self.parent = parent
self.canvas = Tkinter.Canvas(parent, width = 400, height = 500)
self.canvas.pack()
self.sequence = [ImageTk.PhotoImage(img)
for img in ImageSequence.Iterator(
Image.open(
r'C:\Users\Damian\Pictures\Gif folder\Originals\Bunnychan.gif'))]
self.image = self.canvas.create_image(200,200, image=self.sequence[0])
self.animating = True
self.animate(0)
def animate(self, counter):
self.canvas.itemconfig(self.image, image=self.sequence[counter])
if not self.animating:
return
self.parent.after(33, lambda: self.animate((counter+2) % len(self.sequence)))
root = Tkinter.Tk()
root.title('App')
app = App(root)
root.mainloop()
I have another piece that I am going to merge with this code:
import webbrowser
from Tkinter import *
from PIL import ImageTk,Image
Url1 = 'https://www.nedbank.co.za'
Url2 = 'https://www.facebook.com'
def openUrl1():
webbrowser.open(Url1, 2)
def openUrl2():
webbrowser.open(Url2, 2)
root = Tk()
root.title('App')
root.minsize(width = 400, height = 400)
root.maxsize(width = 400, height = 400)
image = Image.open("C:\\Users\\Damian\\Pictures\\Lores.png")
photo = ImageTk.PhotoImage(image)
label = Label(image = photo)
label.image = photo
label.place(x = 0, y = 0)
color1 = 'white'
button1 = Button(
text = color1,
bg = color1,
relief = "raised",
width = 220,
height = 85,
command = openUrl1
)
Original = Image.open("C:\\Users\\Damian\\Pictures\\NedbankLogoNew.png")
im_pm = ImageTk.PhotoImage(Original)
button1.config(image = im_pm)
button1.place(x = 0, y = 0)
root.mainloop()
The recommended way to import tkinter is this:
import Tkinter as tk
Once you do that, you use tkinter as you normally would, but you need to prepend tk. to all of the widgets and constants. You can do import Tkinter, of course, but that means you have to prefix everything with Tkinter. which is a bit too wordy IMO.
For example:
import Tkinter as tk
root = tk.Tk()
label = tk.Label(root, relief=tk.RAISED)
...
Personally I don't recommend using the constants. You can just use a string (eg: relief="raised", sticky="nsew", etc.), which gives exactly the same results.
This question already has answers here:
Why does Tkinter image not show up if created in a function?
(5 answers)
Closed 5 years ago.
I have the following code:
from tkinter import *
import os
from PIL import ImageTk, Image
#Python3 version of PIL is Pillow
class Scope:
def __init__(self, master):
self.master = master
f = Frame(root)
self.greet_button = Button(f, text="Back", command=self.back)
self.greet_button.pack(side= LEFT)
self.greet_button = Button(f, text="Detect", command=self.detect)
self.greet_button.pack(side= LEFT)
self.close_button = Button(f, text="Continue", command=master.quit)
self.close_button.pack(side=LEFT)
photo = PhotoImage(file='demo.gif')
cv = Label(master, image=photo)
cv.pack(side= BOTTOM)
f.pack()
def greet(self):
print("Previous image...")
root = Tk()
my_gui = Scope(root)
root.mainloop()
My first problem is that when I run this, all the buttons and the window show up, but there is no image. There is a square place holder indicating that the image should be in that box, but no image actually shows. I'm able to display the image if I just type the following:
root = Tk()
photo = PhotoImage(file='demo.gif')
label = Label(root, image=photo)
label.pack()
root.mainloop()
So, I know it's possible. But I don't know what I'm doing wrong with my GUI code. I've tried debugging this quite a bit and nothing seemed to work.
A second problem is, I am completely unable to display a jpg file in a GUI. I've tried using every tutorial and nothing quite does the trick. Ideally, I'd like to just be able to display a jpg image, if that's not possible, I'll settle for displaying a gif.
Your reference to photo gets destroyed / carbage collected by Python after the class is called, so, there is nothing the label could show.
In order to avoid this, you have to maintain a steady reference to it, i.e. by naming it self.photo:
from tkinter import *
import os
from PIL import ImageTk, Image
#Python3 version of PIL is Pillow
class Scope:
def __init__(self, master):
self.master = master
f = Frame(root)
self.greet_button = Button(f, text="Back") #, command=self.back)
self.greet_button.pack(side=LEFT)
self.greet_button = Button(f, text="Detect") #, command=self.detect)
self.greet_button.pack(side=LEFT)
self.close_button = Button(f, text="Continue", command=master.quit)
self.close_button.pack(side=LEFT)
self.photo = PhotoImage(file='demo.gif')
cv = Label(master, image=self.photo)
cv.pack(side=BOTTOM)
f.pack()
def greet(self):
print("Previous image...")
root = Tk()
my_gui = Scope(root)
root.mainloop()
PS: Your code snippet was not running properly because two functions were missing.
I would like to be able to swap out an image on a Tkinter label, but I'm not sure how to do it, except for replacing the widget itself.
Currently, I can display an image like so:
import Tkinter as tk
import ImageTk
root = tk.Tk()
img = ImageTk.PhotoImage(Image.open(path))
panel = tk.Label(root, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
root.mainloop()
However, when the user hits, say the ENTER key, I'd like to change the image.
import Tkinter as tk
import ImageTk
root = tk.Tk()
img = ImageTk.PhotoImage(Image.open(path))
panel = tk.Label(root, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
def callback(e):
# change image
root.bind("<Return>", callback)
root.mainloop()
Is this possible?
The method label.configure does work in panel.configure(image=img).
What I forgot to do was include the panel.image=img, to prevent garbage collection from deleting the image.
The following is the new version:
import Tkinter as tk
import ImageTk
root = tk.Tk()
img = ImageTk.PhotoImage(Image.open(path))
panel = tk.Label(root, image=img)
panel.pack(side="bottom", fill="both", expand="yes")
def callback(e):
img2 = ImageTk.PhotoImage(Image.open(path2))
panel.configure(image=img2)
panel.image = img2
root.bind("<Return>", callback)
root.mainloop()
The original code works because the image is stored in the global variable img.
Another option to do it.
Using object-oriented programming and with an interactive interface to update the image.
from Tkinter import *
import tkFileDialog
from tkFileDialog import askdirectory
from PIL import Image
class GUI(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
w,h = 650, 650
master.minsize(width=w, height=h)
master.maxsize(width=w, height=h)
self.pack()
self.file = Button(self, text='Browse', command=self.choose)
self.choose = Label(self, text="Choose file").pack()
self.image = PhotoImage(file='cualitativa.gif')
self.label = Label(image=self.image)
self.file.pack()
self.label.pack()
def choose(self):
ifile = tkFileDialog.askopenfile(parent=self,mode='rb',title='Choose a file')
path = ifile.name
self.image2 = PhotoImage(file=path)
self.label.configure(image=self.image2)
self.label.image=self.image2
root = Tk()
app = GUI(master=root)
app.mainloop()
root.destroy()
Replace 'cualitativa.jpg' for the default image you want to use.
Another solution that might be of help.
In my case, I had two tk.Tk() windows. When using ImageTk.PhotoImage(), the object defaults to setting its tk window to being the first one created. A simple fix to this is to pass the tk window that you want as such ImageTk.PhotoImage(img, master=your_window)
import tkinter as tk
from PIL import ImageTk, Image
if __name__ == '__main__':
main_window = tk.Tk()
second_window = tk.Tk()
main_canvas = Canvas(second_window)
main_canvas.pack()
filename = 'test.png'
img = Image.open(filename)
img = img.resize((300, 100), Image.ANTIALIAS)
logo = ImageTk.PhotoImage(img, master=second_window)
logo_label = Label(master=main_canvas, image=logo)
logo_label.image = logo
logo_label.pack()
main_window.mainloop()