tkinter GUI- label image not showing but is still there (kinda) - python-3.x

I'm trying to show a die at random to a tkinter GUI, but it does not work as expected.
from tkinter import *
from random import choice
def change_pic():
die1 = PhotoImage(file=("dice-1.png"))
die2 = PhotoImage(file=("dice-2.png"))
die3 = PhotoImage(file=("dice-3.png"))
die4 = PhotoImage(file=("dice-4.png"))
die5 = PhotoImage(file=("dice-5.png"))
die6 = PhotoImage(file=("dice-6.png"))
faces=[die1, die2, die3, die4, die5, die6]
label.config(image=choice(faces))
label.grid(row=1, column=1)
root = Tk()
label = Label(root)
label.grid(row=1, column=1)
change_button = Button(root, text="change", command =change_pic)
change_button.grid(row=1, column=2)
root.mainloop()
this is my code
instead of showing the die image, it just show the place where it should be, and its size.
I tried a lot of things but I cannot fix it. please help.

You choose the image for the label inside a function which puts the image in the function namespace. When the function ends the reference to the image is garbage collected.
You can fix this by saving a reference to the image in the label widget:
faces=[die1, die2, die3, die4, die5, die6]
img = choice(faces)
label.config(image=img)
label.image = img # Save a reference to the image
label.grid(row=1, column=1)

Related

Insert an image in a labelFrame title with tkinter

I wonder if it's possible to insert an image or icon in the title of a Labelframe in tkinter ?
Maybe something like this lf = Labelframe(root, image='my_img'), the same as we can do with a button.
It would be very useful.
You can insert any widget as labelwidget option of LabelFrame.
If you want an image instead of text displayed in LabelFrame:
import tkinter as tk
from PIL import Image, ImageTk
w = tk.Tk()
w.geometry("400x500")
img_open = Image.open("example_1.png")
img = ImageTk.PhotoImage(img_open)
label_title = tk.Label(w, image=img) #important Do Not grid/pack/place it!
label_frame = tk.LabelFrame(w, labelwidget=label_title)
label_frame.grid(row=0, column=0)
#fill LabelFrame with something to make it visible
bt = tk.Button(label_frame, text="Press")
bt.grid(padx=20, pady=20)
w.mainloop()
Output:
Or like I said before you can use any widget.
An example label with image and a label with text.
import tkinter as tk
from PIL import Image, ImageTk
w = tk.Tk()
w.geometry("400x500")
img_open = Image.open("example_1.png")
img = ImageTk.PhotoImage(img_open)
frame_labelwidget = tk.Frame(w) #important Do Not grid/pack/place it!
label_image = tk.Label(frame_labelwidget, image=img)
label_image.grid(row=0, column=0)
label_text = tk.Label(frame_labelwidget, text="Teamwork")
label_text.grid(row=0, column=1)
label_frame = tk.LabelFrame(w, labelwidget=frame_labelwidget)
label_frame.grid(row=0, column=0)
#fill LabelFrame with something to make it visible
bt = tk.Button(label_frame, text="Press")
bt.grid(padx=20, pady=20)
w.mainloop()
Output:
Of course that makes less sense because you can have text and an image combined in tk.Label by using compound option.
I added it only for demonstration purpose, you can add any widget you want. Instead of label_text you could add an Entry, Canvas...

Python: TypeError: 'Label' object is not callable

from PIL import Image, ImageTk
from tkinter import *
from tkinter import Label
def open_window():
menu = Toplevel(root)
menu.geometry("800x800")
menu.title("my game's menu")
menu.resizable(False, False)
menu.geometry("800x800")
lbl = Label(menu, text ="Hello!").pack
menu.mainloop()
root = Tk()
root.geometry("400x300")
Label = Label(root, text="Are you ready?")
Label.pack()
root.title("quick question")
btn = Button(root, text="Yes", command= open_window)
btn.pack(padx=20, pady = 20)
root.mainloop()
I got this error while I was working on previously seen code: File "C:\Users\User\Desktop\naujas zaidimas\scratch.py", line 11, in open_window
lbl = Label(menu, text ="Hello!").pack
TypeError: 'Label' object is not callable
Does anyone know why/how to fix it?
The code has a number of fairly obvious errors and one insidious error
This import plays no role in current code
from PIL import Image, ImageTk
Not a good or preferred way to import tkinter
This will cause problems later on
from tkinter import *
This is unnecessary with the current inport method
from tkinter import Label
def open_window():
The rule for Python functions is: Names created in functions stay in functions.
It will require a global instruction to make 'menu' available elsewhere in your code
menu = Toplevel(root)
menu.geometry("800x800")
menu.title("my game's menu")
menu.resizable(False, False)
Duplicated geometry instruction
menu.geometry("800x800")
This object has already been defined as a Label object so trying to name it throws a TypeError
lbl = Label(menu, text ="Hello!").pack()
This is unnecessary since root.mainloop() has already been executed
menu.mainloop()
root = Tk()
root.geometry("400x300")
Here is another naming problem caused by the import method chosen
Label = Label(root, text="Are you ready?")
Label.pack()
root.title("quick question")
This button will enable you to create MANY Toplevel windows
The problem is, ALL of them will be called 'menu'!?
This is the insidious error
btn = Button(root, text="Yes", command= open_window)
btn.pack(padx=20, pady = 20)
root.mainloop()
# This solution avoids all the previous problems
import tkinter as tk
root = tk.Tk()
root.title("quick question")
root.geometry("400x300")
tk.Label(root, text = "Are you ready?").pack()
# This will give tkinter time to process the given instructions.
root.update()
menu = tk.Toplevel(root)
# withdraw will make menu temporarily invisible
menu.withdraw()
menu.title("my game's menu")
menu.geometry("800x800")
menu.resizable(False, False)
tk.Label(menu, text = "Hello!").pack()
# command will now make menu window visible
btn = tk.Button(root, text = "Yes", command = menu.deiconify)
btn.pack(padx = 20, pady = 20)
root.mainloop()
from PIL import Image, ImageTk
from tkinter import *
def open_window():
menu = Toplevel(root)
menu.geometry("800x800")
menu.title("my game's menu")
menu.resizable(False, False)
menu.geometry("800x800")
lbl = Label(menu, text ="Hello!").pack()
menu.mainloop()
root = Tk()
root.geometry("400x300")
lbl1 = Label(root, text="Are you ready?").pack()
root.title("quick question")
btn = Button(root, text="Yes", command= open_window)
btn.pack(padx=20, pady = 20)
root.mainloop()
I Played around both our codes and now somewhy it works.

how to add image in tkinter?

I can't add image (gif image) to tkinter window.
from tkinter import *
from tkinter import filedialog
from PIL import ImageTk, Image
root = Tk()
def open_image():
qr_select = filedialog.askopenfilename(title = "open")
im = PhotoImage(file=qr_select)
w1 = Label(window, image = im)
w1.image = im
w1.config(image=im)
w1.pack(side="right")
def window_function():
global window
window=Tk()
window.geometry("800x550+650+250")
window.title("QR_Scanner")
btn = Button(window,text = "open a gif picture",command = open_image)
btn.pack()
root.iconify()
window.mainloop()
btn = Button(root,text = "open window",command = window_function)
btn.pack()
root.mainloop()
my error is (_tkinter.TclError: image "pyimage1" doesn't exist)
The reason you can't see your gif in the window is that you haven't made a reference to the image so it is collected in Tkinters garbage collector. Read More About This Here. To Add a reference to the image you can do this:
w1.image = im
And add it in your code here:
def open_image():
qr_select = filedialog.askopenfilename(title = "open")
im = PhotoImage(file=qr_select)
w1 = Label(root, image = im)
w1.image = im #Keep A Reference To The Image
w1.config(image=im)
w1.pack(side="right")
The reason you are getting pyimage1 doesn't exist is because you have more than one instance the Tk and there is only meant to be 1. You have to make your window a Toplevel() by replacing: window=Tk() with window=TopLevel()

Update canvas image on button click event

I'm trying to modify the displayed image on a canvas when a button is clicked on.
I have an object "Window" which will contain my window layers. this object contain a canvas named "draw_frame" and a button named "source_button". I add the command "self.load_static_source" to my button but when i click on my button nothing happen.
(Not exactely nothing because i tried to add default background in the init scope and when i click on my button after that the image on the canvas just diseappeared and the new selected image wasn't draw).
Here is my code:
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk
DARK_THEME = "grey"
LIGHT_THEME = "white"
THEME = LIGHT_THEME
class Window():
# object constructor
def __init__(self, root, theme):
# Sections #
self.toolbar_frame = LabelFrame(root, bg=theme, height="40")
self.toolbar_frame.pack(side=TOP, fill=X)
# Canvas #
self.draw_frame = Canvas(root)
self.draw_frame.pack(side=RIGHT, fill=BOTH, expand=True)
self.frame = self.draw_frame.create_image(0, 0, anchor=NW)
# Buttons #
self.source_button = Button(self.toolbar_frame, text="Source", bg=theme, command= lambda: self.load_static_source("./Sources/"))
self.source_button.pack(side=LEFT)
# Load image with tk compatibility
def load_image(self, path_):
print(path_) ### DEBUG ###
image = Image.open(path_)
return ImageTk.PhotoImage(image)
# Change canvas source with static one
def load_static_source(self, dir_):
path_ = filedialog.askopenfilename(initialdir = dir_, title = "Select file", filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
self.draw_frame.itemconfig(self.frame, image=self.load_image(path_))
root = Tk()
Window(root, THEME)
root.mainloop()
I found an other post talking about that and i can't find difference between what i did and the given solution and that's why i don't understand why that code isn't working.
Here are the exemple i found and the related post:
from Tkinter import *
#----------------------------------------------------------------------
class MainWindow():
#----------------
def __init__(self, main):
# canvas for image
self.canvas = Canvas(main, width=60, height=60)
self.canvas.grid(row=0, column=0)
# images
self.my_images = []
self.my_images.append(PhotoImage(file = "ball1.gif"))
self.my_images.append(PhotoImage(file = "ball2.gif"))
self.my_images.append(PhotoImage(file = "ball3.gif"))
self.my_image_number = 0
# set first image on canvas
self.image_on_canvas = self.canvas.create_image(0, 0, anchor = NW, image = self.my_images[self.my_image_number])
# button to change image
self.button = Button(main, text="Change", command=self.onButton)
self.button.grid(row=1, column=0)
#----------------
def onButton(self):
# next image
self.my_image_number += 1
# return to first image
if self.my_image_number == len(self.my_images):
self.my_image_number = 0
# change image
self.canvas.itemconfig(self.image_on_canvas, image = self.my_images[self.my_image_number])
#----------------------------------------------------------------------
root = Tk()
MainWindow(root)
root.mainloop()
Related post : stackoverflow topic
You need to keep a reference to the image. Here's a link to the effbot page describing it: https://effbot.org/tkinterbook/photoimage.htm
You must keep a reference to the image object in your Python program, either by storing it in a global variable, or by attaching it to another object.
The solution Xeyes wrote is right, and this page explains.
So i found the solution. A bit weird but it works. I have to save the image in a class attribute before i give it to the canvas itemconfig method.
It now looks like :
self.placeholder = self.load_image(path_)
self.draw_frame.itemconfig(self.frame, image=self.placeholder)
Instead of just :
self.draw_frame.itemconfig(self.frame, image=self.load_image(path_))

python3 & tkinter - creating a zoom button to zoom on the loaded image

I would like to create a zoom button. On clicking on that zoom button, the image would be zoomed in by a factor represented by an integer (1,2,3,4,5...). With this piece of code, by clicking on the zoom button, another panel is created underneath the already loaded picture. Inside it is blank. What would be needed is to:
1. kill the first (non-zoomed window) and 2. load the zoomed image on the updated panel
from tkinter import *
from tkinter.filedialog import askopenfilename
import tkinter as tk
event2canvas = lambda e, c: (c.canvasx(e.x), c.canvasy(e.y))
root = Tk()
#setting up a tkinter canvas with scrollbars
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set,yscrollcommand=yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
#adding the image
image_str="image.png"
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=image,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
def zoomin():
root = Tk()
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand = xscroll.set, yscrollcommand = yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=large_img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
toolbar = Frame(root, bg="blue")
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin())
insertButt.pack(side = LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill = X)
#function to be called when mouse is clicked
def printcoords(event):
#outputting x and y coords to console
print (event.x,event.y)
#mouseclick event
canvas.bind("<Button 1>",printcoords)
#mouseclick event
canvas.bind("<ButtonPress-1>",printcoords)
canvas.bind("<ButtonRelease-1>",printcoords)
root.mainloop()
I would like to thank #Symon for his stackoverflow question. I largely inspired myself from his code
Well, the reason that the function zoomin(img) does not work properly is that it returns in the first line:
def zoomin(img):
return # Function returns here
... rest of code is never executed
I suspect this is due to the function being run when you create the button, not when you press it. Try cretating the button in this way instead:
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin(img))
Now the button will call zoomin(img) when it's pressed and not when the button is created.
Zooming with Tkinter
PhotoImage zoom only allows integer values, wich makes it a bit limited. But here's an example:
from tkinter import *
root = Tk()
root.geometry('300x200')
field = Canvas(root, bg='tan1', highlightthickness=0)
field.grid(sticky='news')
photo = PhotoImage(file='test.gif')
field.create_image(0, 0, image=photo, anchor='nw')
scale = 1
def zoom(event=None):
global scale, photo
scale = scale * 2
field.delete('all')
photo = photo.zoom(x=scale, y=scale)
field.create_image(0, 0, image=photo, anchor='nw')
field.image = photo
root.bind('z', zoom) # Bind "z" to zoom function
root.mainloop()
If you want to zoom by float you'll have to import a module for that. Pillow seems popular. But I haven't worked with any of them so you'll have to research them yourself.

Resources