Display image button using function - python

I want to create many buttons. So, i have created a function and i call this function to create a button. The buttons have an Image so i have added on the parameter the link of the image when i call the function.
Knowing the Runtime error for Tkinter Image, i have used a list to save Image links.
The issue is that it display only one button. may be a problem with the list ?
from tkinter import *
app = Tk()
a = []
i = 0
def CreateButton (file, row):
global i
global ButtonCreationImg
global a
a.insert(i, file)
ButtonCreationImg = PhotoImage(file = a[i])
ButtonCreation = Button(app, image=ButtonCreationImg, border='0')
ButtonCreation.grid(row=row, column=0, columnspan=4, ipadx=0)
i += 1
CreateButton("bouton_1.png", 6)
CreateButton("bouton_2.png", 8)
app.mainloop()

I expect problem is with bug in PhotoImage and Garbage Collector which removes PhotoImage from memory when it is created in function and it is assigned to local variable - and which is removed by Garbage Collector when it exits from function.
You can read about this problem in Note on effbot.org: PhotoImage
I use button.img to assign PhotoImage to class so it will keep it in memory.
I also made other changes in code but they are not important for this problem.
import tkinter as tk
from PIL import ImageTk
# --- functions ---
def create_button(all_files, filename, row):
all_files.append(filename)
button_image = ImageTk.PhotoImage(file=all_files[-1])
button = tk.Button(app, image=button_image, border='0')
button.img = button_image # solution for bug with PhotoImage
button.grid(row=row, column=0, columnspan=4, ipadx=0)
# --- main ---
all_files = []
app = tk.Tk()
create_button(all_files, "bouton_1.png", 6)
create_button(all_files, "bouton_2.png", 8)
app.mainloop()

Related

How to properly use buttons in tkinter with same window

This is my first day using tkinter, and I wanted to know how to properly code what I have done.
What I wanted to make was a kind of diaporama so I wanted to find a way to display pictures in a canvas and have two buttons so I could go to the previous picture or the following one. The way I stored pictures is using a list lof numpy arrays, with l of size n, so I have n pictures and they are the same size. So I wrote the following code:
from tkinter import *
import numpy as np
from PIL import Image, ImageTk
import sys
sys.setrecursionlimit(10000) #I increased the recursion limit because I had some issues
l = [(255*np.random.rand(50,50)).astype(np.uint8) for i in range(105)] #this is a random list in case you want to test the code
def next(i,n):
if i == n-1:
return 0
else:
return i+1
def previous(i,n):
if i==0:
return n-1
else:
return i-1
window = Tk()
window.geometry("200x100+900+500")
def display(i):
#This is to clear my window at each iteration because I would like to keep the same window
for widget in window.winfo_children():
widget.destroy()
array = l[i] #this is the i-th picture
img = ImageTk.PhotoImage(image=Image.fromarray(array),master = window)
canvas = Canvas(window, width=48, height=48)
canvas.place(x=10, y=20)
canvas.create_image(0,0, anchor="nw", image=img)
Label(window, text="Picture n°"+str(i), fg="black").place(x=5, y=0)
Button(window, text ='Next',command=lambda: display(next(i,len(l)))).place(x=140, y=35)
Button(window, text ='Previous',command = lambda: display(previous(i,len(l)))).place(x=70, y=35)
window.mainloop()
display(0)
I know that is bad code and not the way it should be written. It is working fine but I need help to improve the code please.
You should only put the code of updating the image inside display() and create the interface outside the function. Then recursion is not needed.
Also a global variable is required to keep track the current index to the image list. This variable should be updated when Next or Previous button is clicked.
Below is a modified example:
from tkinter import *
import numpy as np
from PIL import Image, ImageTk
l = [(255*np.random.rand(50,50)).astype(np.uint8) for i in range(105)] #this is a random list in case you want to test the code
current = 0
def display(dir):
global current
# update "current" based on "dir"
current = (current + dir) % len(l)
# show the "current" image
image = ImageTk.PhotoImage(Image.fromarray(l[current]))
imgbox.config(image=image, text=f"Picture n°{current}")
imgbox.image = image # keep a reference to avoid "image" from being garbage collected
window = Tk()
window.geometry("200x100+900+500")
# use a label for showing image and text together
imgbox = Label(window, fg="black", compound="bottom", width=70)
imgbox.place(x=5, y=0)
Button(window, text ='Next', command=lambda: display(+1)).place(x=150, y=35)
Button(window, text ='Previous', command=lambda: display(-1)).place(x=80, y=35)
display(0) # show first image
window.mainloop()

Tkinter using memory when updating image; how do I stop the memory usage?

I have an image to display on the main page which I am updating every 10 seconds. I open the image, set the label than then make a reference back to the image so that it does not get garbage collected and disappear.
As soon as I take the "plant_stat_panel.image = plant_stat_img" it stops chewing up the memory. What is the correct way to update an image and cleanup after yourself to stop the memory consumption?
from tkinter import *
from PIL import ImageTk, Image
root= Tk()
def img_updater():
plant_stat_img = ImageTk.PhotoImage(Image.open("/home/pi/wateringsys/html/temp.png"))#/home/pi/html/
plant_stat_panel = Label(root, image = plant_stat_img)
plant_stat_panel.image = plant_stat_img
plant_stat_panel.grid(row = 5,column = 0, columnspan=2, sticky = W )
root.after(10000, img_updater)
root.after(0,img_updater)
root.mainloop()
At the moment you are making a new Label every 10 seconds and covering up (not deleting) the older ones. You need to make the Label once, and update the Label, not create a new one every time.
from tkinter import *
from PIL import ImageTk, Image
root= Tk()
plant_stat_panel = Label(root)
plant_stat_panel.grid(row = 5,column = 0, columnspan=2, sticky = W )
def img_updater():
plant_stat_img = ImageTk.PhotoImage(Image.open("/home/pi/wateringsys/html/temp.png"))#/home/pi/html/
plant_stat_panel.config(image = plant_stat_img)
plant_stat_panel.image = plant_stat_img
root.after(10000, img_updater)
img_updater()
root.mainloop()
As soon as you make overwrite the old image, the python garbage collector will automatically free the memory. In python, you should not need to worry about memory allocation.

Python tkinter: Replacing an image button with an image label

Hi I'm trying to make a code that would replace an image button with an image label when the button is pressed. But the window isn't updating so the new image doesn't became visible. Can anybody help me? If it is even possible to do that way.
There is the code I'm trying:
from tkinter import *
import time
gifdir = "./"
class Game:
def __init__(self):
self.__window = Tk()
igm = PhotoImage(file=gifdir+"empty.gif")
Button(self.__window, image=igm, command= self.change_picture)\
.grid(row=1, column=2, sticky=E)
def change_picture():
igm = PhotoImage(file=gifdir+"new.gif")
Label(self.__window, image=igm,)\
.grid(row=1, column=2, sticky=E)
self.__window.mainloop()
def main():
Game()
main()
When I add this code to the end:
self.__window.update_idletasks()
time.sleep(1)
the new picture is shown for a one second but I need to see it all the time and still be able to press other buttons.
I modified your code, as your code is very strangely designed, and incorrect IMO. This is the modified version:
from tkinter import *
import time
class Game:
def __init__(self):
self.__window = Tk()
self.gifdir = "./"
self.igm = PhotoImage(file=self.gifdir+"empty.gif")
self.btn = Button(self.__window, image=self.igm, command = self.change_picture)
self.btn.grid(row=1, column=2, sticky=E)
self.__window.mainloop()
def change_picture(self):
self.igm = PhotoImage(file=self.gifdir+"new.gif")
self.btn.configure(image = self.igm)
def main():
Game()
main()
In this new version, pressing the button, will change the image on it. Basically, in your class, you need to keep references to created widgets. Especially keeping a reference for PhotoImage is important, as if the reference is not kept, garbage collector will remove the image, when instance of PhotoImage will go out of scope in change_picture.

tkinter doesn't display image from outside the main file in a label [duplicate]

I expect the same output for both of the scripts below.
But I don't get the image on the button when I execute Script 1. However, Script 2 works well.
Script 1
from Tkinter import *
class fe:
def __init__(self,master):
self.b=Button(master,justify = LEFT)
photo=PhotoImage(file="mine32.gif")
self.b.config(image=photo,width="10",height="10")
self.b.pack(side=LEFT)
root = Tk()
front_end=fe(root)
root.mainloop()
Script 2
from Tkinter import *
root=Tk()
b=Button(root,justify = LEFT)
photo=PhotoImage(file="mine32.gif")
b.config(image=photo,width="10",height="10")
b.pack(side=LEFT)
root.mainloop()
The only reference to the image object is a local variable. When __init__ exits, the local variable is garbage collected so the image is destroyed. In the second example, because the image is created at the global level it never goes out of scope and is therefore never garbage collected.
To work around this, save a reference to the image. For example, instead of photo use self.photo.
its work
x1=Button(root)
photo=PhotoImage(file="Re.png")
x1.config(image=photo,width="40",height="40",activebackground="black"
,bg="black", bd=0,command=sil)
x1.place(relx=1,x=5, y=-5, anchor=NE)
but this is useless
def r():
x1=Button(root)
photo=PhotoImage(file="Re.png")
x1.config(image=photo,width="40",height="40",activebackground="black",
bg="black", bd=0,command=sil)
x1.place(relx=1,x=5, y=-5, anchor=NE)
r()
logo = PhotoImage(file = 'mine32.gif')
small_logo = logo.subsample(5, 5)
self.b.config(image = small_logo , compound = LEFT )
Unrelated answer, but this is the answer I was looking for when I first came here. Use this to resize the image before adding it to the button.
from PIL import Image, ImageTk
image = Image.open("path/to/image.png")
image = image.resize((25, 25), Image.ANTIALIAS)
self.reset_img = ImageTk.PhotoImage(image)
self.button = tk.Button(frame, image=self.reset_img)
from tkinter import *
root= Tk()
btnPlay = Button(root)
btnPlay.config(image=imgPlay, width="30", height="30")
btnPlay.grid(row=0, column=0)
root.mainloop()

python tkinter not display image in label

I am new to python.I have tried a code on how to display Texbox,image and button.But the image not displays
Please rectify my code to display the image!
My code:
import Tkinter
from Tkinter import *
class myproject(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self)
self.button2()
self.text()
self.image()
def button2(self):
button2 = Tkinter.Button(self, text = "hello")
button2.grid(column=5,row=7)
def text(self):
text = Tkinter.Text(self, height=3, width=31)
text.grid(column=1,row=3)
text.insert(END, "Wiilliam Skakespeare")
def image(self):
logo = PhotoImage(file="linux.gif")
w1 = Tkinter.Label(self, image=logo)
w1.grid(column=5,row=7)
app = myproject(None)
app.mainloop()
You need to save the PhotoImage as a class variable so the reference can stay in memory. The following method for image() should work:
def image(self):
self.logo = Tkinter.PhotoImage(file="linux.gif")
w1 = Tkinter.Label(self, image=self.logo)
w1.grid(column=5,row=7)
This page provides a more in-depth explanation: Effbot PhotoImage. Specifically this section:
Note: When a PhotoImage object is garbage-collected by Python (e.g.
when you return from a function which stored an image in a local
variable), the image is cleared even if it’s being displayed by a
Tkinter widget.
To avoid this, the program must keep an extra reference to the image object.

Categories

Resources