tkinter canvas image not displaying - python

I have a simple canvas being created in a function, and i would like an image displayed on the canvas.
def start(root):
startframe = tkinter.Frame(root)
canvas = tkinter.Canvas(startframe,width=1280,height=720)
startframe.pack()
canvas.pack()
one = tkinter.PhotoImage('images\one.gif')
canvas.create_image((0,0),image=one,anchor='nw')
when i run the code i get a blank 1280x720 window, no image.
i have looked at the following website: http://effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm but i do not understand how to apply their example to my situation (i dont know what to create a reference to or how to create a reference, if that is my problem). I have also looked at some stack overflow questions but they did not help either.

Escape backslashes in path string correctly. (or use r'raw string literal').
Prevent PhotoImage object being garbage collected.
specify the filename using file=... option.
def start(root):
startframe = tkinter.Frame(root)
canvas = tkinter.Canvas(startframe,width=1280,height=720)
startframe.pack()
canvas.pack()
# Escape / raw string literal
one = tkinter.PhotoImage(file=r'images\one.gif')
root.one = one # to prevent the image garbage collected.
canvas.create_image((0,0), image=one, anchor='nw')
UPDATE
The two statements one = ... and root.one = one can be merged into one statement:
root.one = one = tkinter.PhotoImage(r'images\one.gif')

How about canvas.update()? I was suffering a similar problem. I am using grid, so instead of .pack I needed to use .update.

I had the situation when image didn`t show up, when I call it in function, but otherwise everything was okay. That is my function
def ask_for_file():
f_types = [('PNG Images', '*.png')]
filename = askopenfilename(filetypes=f_types)
img = ImageTk.PhotoImage(file=filename)
canvas.config(height=img.height(), width=img.width())
canvas.create_image(0, 0, anchor=NW, image=img)
My solution was to add img = None as global variable and change it inside function. It worked
img = None
def ask_for_file():
global img
f_types = [('PNG Images', '*.png')]
filename = askopenfilename(filetypes=f_types)
img = ImageTk.PhotoImage(file=filename)
canvas.config(height=img.height(), width=img.width())
canvas.create_image(0, 0, anchor=NW, image=img)

Related

Yet Another tkinter Image Garbage Collection Issue - RESOLVED: wrong parameter type [duplicate]

I have a label with an image, and a button which should update the label / delete the image in the label, so I can put a new image into the same label via label.config.
I tryed to use something like that: whenever you click on the button the it should remove the image with label.config(image = None) but it doesnt work, if I load new images into the label the old ones are still there:
# here is the label initialized
global brand_preview
brand_preview = Label(root, image = None)
brand_preview.place(x = 10, y = 60)
# thats the button which have to clear the label image
self.top_brand = Button(root, text = "clear", bg = "snow3", command=clear_label_image)
self.top_brand.place(x = 550, y = 60)
# thats how I load a photoimage into the label
photoimg_brand = ImageTk.PhotoImage(im_thumb)
brand_preview.image = photoimg_brand
brand_preview.config(image = photoimg_brand)
# Thats how the button works
def clear_label_image():
brand_preview.config(image = None)
brand_preview.image = None
All I want now that if we I click the Button the brand_preview loses the image / the image gets deleted
EDIT:
The main issue is solved, but that only works if the button only has to delete the image. If I want to delete and add a new one it doesnt work
def clear_label_image():
brand_preview.config(image = "")
photoimg_brand = ImageTk.PhotoImage(im_thumb)
brand_preview.image = photoimg_brand
brand_preview.config(image = photoimg_brand)
You're very close - the image parameter just needs an empty string rather than None.
def clear_label_image():
brand_preview.config(image='')
After some Googling, I found this solution
def clear_label_image():
#brand_preview.config(image = None)
brand_preview.image.blank()
brand_preview.image = None
This definitely clears the image from the button. I haven't tried changing to a new image.
I just copied it from the Web, and it worked. I created the image with
photoimg_brand = tk.PhotoImage(file='/usr/share/httpd/icons/world1.gif')
I did this with python 2.7, and my only import was import Tkinter as tk
If you are using label to show the image then you can do this:
label.pack_forget()
label should be global

What is difference between label.configure(image=value) and label.image=value?

from tkinter import *
from tkinter.filedialog import *
wd = Tk()
def func_open():
fname = askopenfilename(parent=wd, filetypes=( ("gifs","*.gif"), ("alls","*.*") ))
photo1 = PhotoImage( file = fname )
pLabel.configure( image = photo1 )
pLabel.image=photo1
temp = PhotoImage()
pLabel = Label(wd, image = temp )
pLabel.pack(expand=1)
mainMenu = Menu(wd)
wd.config(menu=mainMenu)
fileMenu = Menu(mainMenu)
mainMenu.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Open",command=func_open)
wd.mainloop()
2 lines of code above,
pLabel.configure( image = photo1 )
pLabel.image=photo1
if i remove one of these, func_open() can't print image file.
To me, it seems like both line says same thing,
as pLabel.configure( image = photo1 ) put image through argument photo1
and pLabel.image=photo1 is directly put photo1 to pLabel's image.
I tried search that .configure() method but i couldn't get any understandable imformation.
Widgets have internal configuration options that control its appearance and behavior. When you call the configure method, you are giving one of those options a value. Thus, pLabel.configure( image = photo1 ) sets the image option to the value of the photo1 variable.
When you do pLabel.image=photo1, you are creating a new instance variable on the pLabel python object named image and setting it to the value of the photo1 variable. The underlying tkinter widget has no knowledge of this attribute, and isn't affected by this attribute.
This is a common idiom for saving a reference to the image. The use of the word image is completely arbitrary, using pLabel.xyzzy=photo1 or pLabel.save_this=photo1 would solve the exact same problem.
For more information see Why does Tkinter image not show up if created in a function?

How can I bring a Canvas to front?

I overlapped 5 Tk.Canvas objects and each will have different images. I want to bring each canvas to front of every other canvases to draw pictures in the most-front canvas.
class window_tk():
def __init__(self,main):
self.main=main
self.canvas_org = tk.Canvas(self.main, bg='white')
self.canvas_layer1 = tk.Canvas(self.main, bg='red')
self.canvas_layer2 = tk.Canvas(self.main, bg='green')
self.canvas_layer3 = tk.Canvas(self.main, bg='blue')
self.canvas_layer4 = tk.Canvas(self.main, bg='black')
self.btn_load = tk.Button(self.main,text = "Load Image",command = self.load_ct)
self.btn_layer1 = tk.Button(self.main,text = "Draw in L1",command = self.bring_1)
self.btn_layer2 = tk.Button(self.main,text = "Draw in L2",command = self.bring_2)
self.btn_layer3 = tk.Button(self.main,text = "Draw in L3",command = self.bring_3)
self.btn_layer4 = tk.Button(self.main,text = "Draw in L4",command = self.bring_4)
def bring_1(self):
self.canvas_layer1.place(x=50,y=00)
def bring_2(self):
self.canvas_layer2.place(x=50, y=00)
def bring_3(self):
self.canvas_layer3.place(x=50, y=00)
def bring_4(self):
self.canvas_layer4.place(x=50, y=00)
I thought the canvas.place() function will bring the canvas front but it was not. Which function can I use ? Or should I unpack all other canvases ?
Since Canvas has override the .tkraise() function, you need to call TCL command directly:
self.canvas.tk.call('raise', self.canvas._w)
Please see the answer given by acw1668. The lift function doesn't work for Canvas objects. His answer is correct.
All tkinter objects, Canvas included, support the following method:
w.lift(aboveThis=None)
If the argument is None, the window containing w is moved to the top of the window stacking order. To move the window just above some Toplevel window w, pass w as an argument.
This gives you full control over which widget sits on top.
https://anzeljg.github.io/rin2/book2/2405/docs/tkinter/universal.html
Now that I re-read that, I see that its language is slightly incorrect. "w" is any tkinter widget, "above_this" is another tkinter widget. The function places "w" above "above_this" in the stacking order.
You can use the following functions -
canvas.tag_raise(canvas_layer4) -> For bringing to front
canvas.tag_lower(canvas_layer4) -> For pushing back

How can I fix the repetitive root.mainloop() calls in the change() function? I want to change an image with a change in the scroll bars

There are 10 sliders, and each slider value-change changes the image in the window. But I can't find a way to do this without calling root.mainloop() in the change() function. This although causes stack overflow eventually and i checked that by printing traceback length('memory' variable).
root = tk.Tk()
class SliderClass:
def __init__(self,i,j):
global no
self.no = no
self.w = Scale(root, label="PCA_feature "+str(self.no+1),
from_=10, to=-10, tickinterval=0.1, orient=HORIZONTAL, showvalue=0)
self.w.grid(row=i,column=j)
self.w.bind("<ButtonRelease-1>", self.change)
self.w.set(np.clip(z[self.no],-10.0,10.0))
no +=1
def change(self, event):
memory = ''.join(traceback.format_stack())
print(len(memory))
z[self.no] = self.w.get()
z_inv = pca.inverse_transform(z).reshape((1,-1))
im = G.layers[2].predict(z_inv)
im = (0.5 * im + 0.5)*255
im = im[0,:,:,:].astype('uint8')
im = cv2.resize(im,(150,150))
im = Image.fromarray(im)
im = PhotoImage(im)
panel.configure(image=im)
root.mainloop()
im = Image.fromarray(im)
im = PhotoImage(im)
panel = Label(root, image = im, width=300,height=300)
panel.grid(rowspan=2,column=0)
r,c = 2,5
for i in range(r):
for j in range(1,c+1):
s = SliderClass(i,j)
sliders.append(s)
root.mainloop()
You do not need to call mainloop in your function. It should be called exactly once for the life of your program.
The fact that the image doesn't show when you remove the call to mainloop is because you aren't storing a reference to the image so it gets deleted by the garbage collector when the function goes out of scope. By running mainloop in the function you prevent that from happening, but cause even worse problems (namely, the one you're writing about)
In addition to removing the call to mainloop inside your function, you need to save a reference to the image object. A simple change would be to use an attribute like self.im rather than the local variable im.

tkinter and library PhotoImage

This code works:
img = PhotoImage(file="Image.gif")
Label(root, image=img).pack()
How come this way doesn't work?
Label(root, image=PhotoImage(file="Image.gif")).pack()
Is it not possible to have everything in one line?
The problem isn't about syntax -- it's about garbage collection. In your shortened form:
Label(root, image=PhotoImage(file="Image.gif")).pack()
the pointer to the image returned by PhotoImage() never gets saved, so the image gets garbage collected and doesn't display. In your longer form:
img = PhotoImage(file="Image.gif")
Label(root, image=img).pack()
You're holding onto a pointer to the image, so everything works fine. You can convince yourself of this by wrapping the working code in a function and making img local to that function:
from tkinter import *
root = Tk()
def dummy():
img = PhotoImage(file="Image.gif")
Label(root, image=img).pack()
dummy()
mainloop()
Now, it won't display anymore because img disappears when the function returns and your image gets garbage collected. Now, return the image and save the returned value in a variable:
def dummy():
img = PhotoImage(file="Image.gif")
Label(root, image=img).pack()
return img
saved_ref = dummy()
And your image works again! The common fix for this looks something like:
def dummy():
img = PhotoImage(file="Image.gif")
label = Label(root, image=img)
label.image_ref = img # make a reference that persists as long as label
label.pack()
dummy()
But you can see we've moved a long way away from a one-liner!
On the first version, img keeps the reference to the image.
On the second version, there is not reference to that image and pack() returns None

Categories