I'm trying to build a tkinter button with an image as background inside an object. It doesn't make any sense why the second implementation doesn't work !
Here are 3 very simple examples ; Who can explain the reason why the second implementation is not working?
(Python 3.6.4 :: Anaconda, Inc.)
1. Button created globally.
Works like a charm...
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
def cb():
print("Hello World")
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=cb, image=image)
b.pack()
w.mainloop()
2. Button created inside the object A with a background image
The button doesn't work when clicked and doesn't display the image :(. There's clearly a problem but I don't understand it...
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
class A():
def __init__(self, w):
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb, image=image)
b.pack()
def cb(self):
print("Hello World")
a = A(w)
w.mainloop()
3. Button created inside the object A without a background image
The button works properly, but I would like to display the image as well
from tkinter import *
from PIL import Image, ImageTk
from numpy import random
w = Tk()
class A():
def __init__(self, w):
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb)#, image=image)
b.pack()
def cb(self):
print("Hello World")
a = A(w)
w.mainloop()
You have 2 problems here.
The first problem is the image is not being saved after __init__. You probably know you need to save a reference to the image for it to be used in tkinter. You may not know that in a class if you do not assign the image to a class attribute it will not save the image after __init__.
So to fix the first issue you need change this:
image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
To this:
# add self. to make it a class attribute and keep the reference alive for the image.
self.image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
The 2nd issue you may not notice here is that your text will not display while loading an image. This is because you need to add the argument compound in order for tkinter to display both an image and text in the button. That said you also need to update the image argument to include the new self.image.
So change this:
b = Button(w, text="text", command=self.cb, image=image)
To this:
# added compound and change text color so you can see it.
b = Button(w, compound="center" , text="text", fg="white", command=self.cb, image=self.image)
Results:
I think I understood what happened. Thanks to the linked question what's happening in the second case is that your image gets garbage collected once the __init__ method is finished. As a result your image is not available anymore to the root application, so it cannot be binded to it.
The way to solve it is to make it a class attribute:
class A():
def __init__(self, w):
self.image = ImageTk.PhotoImage(image=Image.fromarray(random.random((50,50))))
b = Button(w, text="text", command=self.cb, image=self.image)
b.pack()
def cb(self):
print("Hello World")
Related
I've got a couple of images in a folder and each of these gets resized and turned into an image for Tkinter. I want to use these images in other python scripts as a enum.
This is how I create the images:
import os
from PIL import Image, ImageTk
from enum import Enum
#Resize All Images and turn them into TkImages
def ResizeImages():
PieceImages = []
for images in os.listdir('Resources'):
cImage = Image.open('Resources/' + images)
cImage = cImage.resize((80, 80), Image.ANTIALIAS)
PieceImages.append(ImageTk.PhotoImage(cImage))
return PieceImages
#Called from outside to create PNGS list AFTER Tkinter.Tk() was called
def InitializeImages():
global PNGS
PNGS = ResizeImages()
#This is the part where I'm lost
class Black(Enum):
global PNGS
Queen = PNGS[3]
King = PNGS[4]
Tower = PNGS[5]
I want to create a Tkinter Button with these images on like this:
#PieceImages is the .py file above
import PieceImages import Black
form tkinter import *
root = Tk()
root.geometry('800x800')
someButton = tk.Button(root, image=Black.Tower).place(x=100, y=100)
My Question is:
Is it even possible to create enums of this type and if so, how?
I'd recommend staying away from global variables; you can pass arguments to a class directly:
class Black():
def __init__(self, pngs):
self.name1 = pngs[0]
self.name2 = pngs[1]
Instantiate by passing pngs:
black = Black(pngs)
Then black.name1, black.name2 etc will be your desired images.
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.
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()
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.
I'm trying to show a jpg image as background for a GUI thing I'm building.
I can get it to work in a single method:
from Tkinter import *
from PIL import Image, ImageTk
class MakeGUI(object):
master = None
w = None
def __init__(self):
self.canvasSizeY = 400 #height
self.canvasSizeX = 640 #width
def setupCanvas(self):
"""
preps the canvas for drawing.
"""
self.master = Tk()
self.w = Canvas(self.master, width=self.canvasSizeX, height=self.canvasSizeY)
self.w.config(bg='white')
image = Image.open("background.jpg")
photo = ImageTk.PhotoImage(image)
self.w.create_image(0,0, image=photo, anchor=NW)
self.w.pack()
mainloop()
def main():
makeGUI = MakeGUI()
makeGUI.setupCanvas()
if __name__ == '__main__':
main()
But when I try and make the canvas in one method, and show the canvas in another, it doesn't show the jpg (when I've been testing, I've created and shown & text and rectangles using this approach):
from Tkinter import *
from PIL import Image, ImageTk
class MakeGUI(object):
master = None
w = None
def __init__(self):
self.canvasSizeY = 400 #height
self.canvasSizeX = 640 #width
def setupCanvas(self):
"""
preps the canvas for drawing.
"""
self.master = Tk()
self.w = Canvas(self.master, width=self.canvasSizeX, height=self.canvasSizeY)
self.w.config(bg='white')
image = Image.open("background.jpg")
photo = ImageTk.PhotoImage(image)
self.w.create_image(0,0, image=photo, anchor=NW)
def showImage(self):
"""
pushes the image to the screen
"""
self.w.pack()
self.w.mainloop()
def main():
makeGUI = MakeGUI()
makeGUI.setupCanvas()
if __name__ == '__main__':
main()
I want to use the GUI dynamically to show some text as I work through some editing, so I'm interested to understand what I've got wrong before I get too far into the build in case its a showstopper...
The most obvious problem is that in the second case you are never calling showImage. Even after you do call that function, your image probably won't show up. Images will be garbage-collected if there isn't a reference to them. It may seem like there's a reference because you're adding it to a canvas, but that isn't enough.
You'll need to do something like:
self.photo = ImageTk.PhotoImage(image)
Finally, I recommend that you take the call to mainloop out of showImage. mainloop must always be called exactly once, so most typically it is the last line of code in your program, or the last line of code in your main function.
A more common way to make a Tkinter application is to subclass either the Tk object or a Frame object, rather than having your main application be a generic object. For example:
class MyApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
...
self.setupCanvas(...)
...
if __name__ == "__main__":
app = MyApp()
app.mainloop()