I've been working to get a simple Tkinter canvas to display an image using create_image. I've read many threads that say that you need to create a reference to the object outside any function or class, otherwise the image object will be garbage collected. Unfortunately, I still cannot get this to work. Below is my code as it stands. Ignore all the colors - I use them to illustrate where the frames and canvas live on the window.
-Kirk
import Tkinter as tk
from PIL import Image
from PIL import ImageTk
imageList = []
image = Image.open('len_std.jpg')
#event handlers
def hit_sel_click():
imageList = []
test_image = ImageTk.PhotoImage(image)
imageList.append(cnv_hits.create_image(0,0,
image=test_image))
#start root
root = tk.Tk()
root.title('SimView')
root.resizable(width=False, height=False)
#target/control variables
hit_sel = tk.StringVar() #holds radio button with activity level
#build GUI
frm_hits = tk.Frame(root, height=800, width=200, bg='#FF0000')
frm_hits.grid(rowspan=3, sticky=tk.W+tk.N+tk.S+tk.E)
tk.Label(frm_hits, text='Activity:').grid()
tk.Radiobutton(frm_hits, text='Low', variable=hit_sel, value='Low',
command=hit_sel_click).grid(sticky=tk.W)
tk.Radiobutton(frm_hits, text='Medium', variable=hit_sel, value='Medium',
command=hit_sel_click).grid(sticky=tk.W)
tmp = tk.Radiobutton(frm_hits, text='High', variable=hit_sel,value='High',
command=hit_sel_click)
tmp.grid(sticky=tk.W)
tmp.select()
frm_hit_list = tk.Frame(frm_hits, bg='#002288')
frm_hit_list.grid(sticky=tk.W+tk.N+tk.E+tk.S)
scrl_hits = tk.Scrollbar(frm_hit_list, orient=tk.VERTICAL)
scrl_hits.grid(row=0, column=1, sticky=tk.N+tk.S)
cnv_hits = tk.Canvas(frm_hit_list, bg='#888800',width=200, height=200,
yscrollcommand=scrl_hits.set)
cnv_hits.grid(row=0, column=0, sticky=tk.W+tk.N+tk.E+tk.S)
scrl_hits.config(command=cnv_hits.yview)
root.mainloop()
You are using test_image to draw the image of cnv_hits. That is right, but you forgot that test_image is local to hit_sel_click() method; which thing means it is not available to your main program.
To resolve this, you have 2 choices:
Either declare test_image as global inside hit_sel_click()
Or run test_image = ImageTk.PhotoImage(image) before you declare hit_sel_click().
Nota Bene:
For the first case, you will need to run root = tk.Tk() before hit_sel_click().
In case you choose the second option, you will need to run root = tk.Tk() before test_image = ImageTk.PhotoImage(image)
If you don't do this, your program will raise a RuntimeError exception.
Related
Trying to include a slider to resize images online in a Tkinker-GUI-Application of python.
Problem: Canvas Resizing works, Inital Show of original sized image, too. But when using the slider the picture is not resized - it just shows very short in the adjusted size and is then somehow "overwritten" by the original one.
from tkinter import *
from turtle import width
from PIL import Image, ImageTk
import FileDownloader
window = Tk()
window.title ('Price Catcher')
#PIL to open a jpg and store a Tk compatible objekct
headerpic = Image.open("./10374_0.jpg")
def resizePictures(scalerValue):
print(scalerValue)
print(window.canvas.find_all())
headerpicTk = ImageTk.PhotoImage(headerpic.resize((int(scalerValue), int(scalerValue)), Image.ANTIALIAS))
window.canvas.config(width=int(scalerValue), height=int(scalerValue))
#window.canvas.delete("all")
canvas_id = window.canvas.create_image(0,0, anchor=NW, image=headerpicTk)
window.canvas.update()
#Picture Resizer
PicResizeScale = IntVar() #Control Variable to use for Picture Resizing Value of Scaler
window.scale = Scale(window, label='Bildgröße in %', orient=HORIZONTAL, resolution=10, length=300, from_=0, to=200, command=resizePictures)
window.scale.set(100)
window.scale.pack(side=TOP)
#PIL to open a jpg and store a Tk compatible objekct
headerpic = Image.open("./10374_0.jpg")
headerpicTk = ImageTk.PhotoImage(headerpic)
#Place the opject to the canvas
window.canvas = Canvas(window, width=100, height=100)
window.canvas.pack()
canvas_id = window.canvas.create_image(0,0, anchor=NW, image=headerpicTk)
window.downloadbutton = Button(window, text='Download Bild', command=FileDownloader.FileDownloader("https://www.silbertresor.de/images/product_images/info_images/10374_0.jpg", ".\\10374_0.jpg"), justify= LEFT)
window.downloadbutton.pack(side = BOTTOM)
window.exitbutton = Button(window, text='Schließen', command=exit, justify= RIGHT)
window.exitbutton.pack(side= BOTTOM)
window.mainloop()
Any hints - i'm lost :(..
PhotoImage has bug which removes image when it is assigned to local variable in function.
And inside resizePictures you assign new PhotoImage to local variable headerpicTk.
You have to add global headerpicTk to assign PhotoImage to global variable headerpicTk.
def resizePictures(scalerValue):
global headerpicTk
print(scalerValue)
# ...rest ...
That's all.
So I want to use an image as my background in Tkinter kind of like how windows has background images in desktop. This is my code but it doesn't seem to work:
root = tk.Tk()
root.attributes("-fullscreen", True)
background_image=tk.PhotoImage("image")
The code runs but it shows up as a white background.
It is pretty simple! Here is how to do it:
from tkinter import *
root = Tk()
root.geometry("400x400")
bg = PhotoImage(file = "image.png")
label1 = Label(root, image = bg)
label1.place(x = 0, y = 0)
root.mainloop()
Just make sure to edit this part (file = "image.png") and use the pathname to your image. Preferably save it on the same folder as your program is stored, though it is not required.
I'm programing a background to this program and it gives a second frame when I run it. I dont know why this happens but it does. here is the program
import tkinter as tk
import os
from PIL import ImageTk, Image
parent = os.path.dirname(os.path.realpath(__file__))
assets = os.path.join(parent,"assets")
backgrounds = os.path.join(assets,"backgrounds")
print(assets)
root = tk.Toplevel()
frame = tk.Frame(root)
frame.pack()
path = os.path.join(backgrounds,"red to blue.png")
img = ImageTk.PhotoImage(Image.open(path))
panel = tk.Label(root, image=img)
panel.pack(side="bottom", fill="both", expand="yes")
root.mainloop()
Ok so the answer was that I needed it to be tk.Tk() instead of tk.Toplevel() and it works now.
I'm developing a GUI in Tkinter and want to apply animation in the below GIF on the image when it appears.
Here is my code,
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
frame = Frame(root)
frame.pack()
canvas = Canvas(frame, width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
canvas.pack()
background = PhotoImage(file="background.png")
canvas.create_image(300,300,image=background)
my_pic = PhotoImage(file="start000-befored.png")
frame.after(1000, lambda: (canvas.create_image(50,50,image=my_pic, anchor=NW))) #and on this image, I want to give the effect.
root.mainloop()
Instead of clicking on the play button as shown in GIF, the image should automatically appears after 1 second like this animation and stays on screen. (No closing option).
I'm not 100% sure I understood the problem, but I'll describe how to animate an image.
Tkinter does not contain functions for animating images so you'll have to write them yourself. You will have to extract all subimages, subimage duration and then build a sequencer to swap subimages on your display.
Pillow can extract image sequences. WEBP images seems to only support one frame duration whereas GIF images may have different frame duration for each subimage. I will use only the first duration for GIF images even if there is many. Pillow does not support getting frame duration from WEBP images as far as I have seen but you gan read it from the file, see WebP Container Specification.
Example implementation:
import tkinter as tk
from PIL import Image, ImageTk, ImageSequence
import itertools
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
filename = 'images/animated-nyan-cat.webp'
pil_image = Image.open(filename)
no_of_frames = pil_image.n_frames
# Get frame duration, assuming all frame durations are the same
duration = pil_image.info.get('duration', None) # None for WEBP
if duration is None:
with open(filename, 'rb') as binfile:
data = binfile.read()
pos = data.find(b'ANMF') # Extract duration for WEBP sequences
duration = int.from_bytes(data[pos+12:pos+15], byteorder='big')
# Create an infinite cycle of PIL ImageTk images for display on label
frame_list = []
for frame in ImageSequence.Iterator(pil_image):
cp = frame.copy()
frame_list.append(cp)
tkframe_list = [ImageTk.PhotoImage(image=fr) for fr in frame_list]
tkframe_sequence = itertools.cycle(tkframe_list)
tkframe_iterator = iter(tkframe_list)
def show_animation():
global after_id
after_id = root.after(duration, show_animation)
img = next(tkframe_sequence)
display.config(image=img)
def stop_animation(*event):
root.after_cancel(after_id)
def run_animation_once():
global after_id
after_id = root.after(duration, run_animation_once)
try:
img = next(tkframe_iterator)
except StopIteration:
stop_animation()
else:
display.config(image=img)
root.bind('<space>', stop_animation)
# Now you can run show_animation() or run_animation_once() at your pleasure
root.after(1000, run_animation_once)
root.mainloop()
There are libraries, like imgpy, which supports GIF animation but I have no experience in usig any such library.
Addition
The duration variable sets the animation rate. To slow the rate down just increase the duration.
The simplest way to put the animation on a canvas it simply to put the label on a canvas, see example below:
# Replace this code
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
# with this code
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack(padx=10, pady=10)
display = tk.Label(canvas)
window = canvas.create_window(250, 250, anchor='center', window=display)
Then you don't have to change anything else in the program.
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.