Displaying an image in python via Button and Canvas - python

I am a begginer in python, tkinter. I have written a code that should normally display an image in a canvas.
What happens is that the main frame (gui) is displayed with the menu bar, then when I click on load image, the gui window shrinks (to 100x100 I guess) but nothing is displayed within.
Could you please explain to me why this is happening so I can understand where the error occurs, and how to correct it?
# -*- coding:utf-8 -*-
# Imports
from tkinter import Tk, Menu, Canvas
from PIL import Image, ImageTk
# Function definitions
def deleteImage(canvas):
canvas.delete("all")
return
def loadImage(canvas, img):
filename = ImageTk.PhotoImage(img)
canvas.image = filename
canvas.create_image(0,0,anchor='nw',image=filename)
return
def quitProgram():
gui.destroy()
# Main window
gui = Tk()
# Inside the main gui window
#Creating an object containing an image
# A canvas with borders that adapt to the image within it
img = Image.open("fleur.jpg")
canvas = Canvas(gui,height=img.size[0],width=img.size[0])
canvas.pack()
# Menu bar
menubar = Menu(gui)
# Adding a cascade to the menu bar:
filemenu = Menu(menubar, tearoff=0)
menubar.add_cascade(label="Files", menu=filemenu)
# Adding a load image button to the cascade menu "File"
filemenu.add_command(label="Load an image", command=loadImage)
# Adding a delete image button to the cascade menu "File"
filemenu.add_command(label="Delete image", command=deleteImage)
filemenu.add_separator()
filemenu.add_command(label="Quit", command=quitProgram)
menubar.add_separator()
menubar.add_cascade(label="?")
# Display the menu bar
gui.config(menu=menubar)
gui.mainloop()
EDIT:
The second problem is that I want to create a canvas and the image in the main gui window, and pass them as arguments to the menu buttons (See code above, where img and canvas are created separately from the function loadImage). Seeing as putting parenthesis in the command=loadImage() created a problem on its own.
Another point that rises a question in my head : Regarding the first problem which was solved by keeping a reference to the filename=ImageTk.PhotoImage(img). Wouldn't it normally be pointless to keep a reference inside the function since it's a local variable anyway?

As stated in effbot's PhotoImage page, you have to keep a reference of your image to ensure it's not garbage collected.
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.
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. A simple way to do this is to assign the image to a widget
attribute, like this:
Your loadImage() method should look like below.
def loadImage():
img = Image.open("fleur.jpg")
filename = ImageTk.PhotoImage(img)
canvas = Canvas(gui,height=100,width=100)
canvas.image = filename # <--- keep reference of your image
canvas.create_image(0,0,anchor='nw',image=filename)
canvas.pack()

Related

Manipulating multiple tkinter windows from the main GUI

I need to build a simple image manipulation program in Python Tkinter. Is it possible to create a GUI which is capable of opening multiple TopLevels and can perform operation on the image from the most recently active Window?
This is the code so far:
def AddImage():
while True:
try:
filename = filedialog.askopenfilename(initialdir="/", title="Choose file",filetypes=[("Images", "*.jpg *.png *.tif *.bmp")])
image = Image.open(filename)
image = image.resize((300, 300), Image.ANTIALIAS)
image = ImageTk.PhotoImage(image)
window = tk.Toplevel()
window.title(filename)
window.geometry('300x300')
canvas = tk.Canvas(window, width=300, height=300, bg='#D4D4D4')
canvas.pack(expand='yes', fill='both')
canvas.create_image(0, 0, image=image, anchor='nw')
canvas.image = image
break
except AttributeError:
break
def Init():
root = tk.Tk()
my_menu = tk.Menu(root)
root.config(menu=my_menu)
my_menu.add_command(label='Open File',command=AddImage)
master = tk.Canvas(root, width=1200, height=600, bg='#D4D4D4')
master.pack()
root.mainloop()
Now, after opening a few images I want to do something with one of them, e.g. duplicate from the main interface.
As far as I understood, you are opening images in different windows and then trying to keep them on top-level of screen (in front of your root window),select them, and switch between the windows.
Lengthy method: "Windows inside root window"
By some work arounds , you can create the sub-windows containing
images inside the root window where they cannot get out of the root
window's bounds. Refer to
Window inside window
Basically, using image_window.transient(root) [where image_window is
the window with image] will keep those sub windows on top of the
root. Then, using conditional statements to keep the sub windows
inside root. Finally, you can try using window.focus_force()
wherever required in order to set a particular window in focus.
Don't open images in new windows- create frames with their own some custom bindings that allow you to move it around the root using place geometry manager. In the frame(s), open the images you want to.

How to dynamically insert a random number of images into the window

I have the following code:
import tkinter as tk
data = [['A',1],['B',2],['C',3],['D',4]]
def ranking():
root = tk.Tk()
root.focus_force()
windowWidth = root.winfo_reqwidth()
windowHeight = root.winfo_reqheight()
positionRight = int(root.winfo_screenwidth()/2 - windowWidth/2)
positionDown = int(root.winfo_screenheight()/2 - windowHeight/2)
root.geometry("+{}+{}".format(positionRight, positionDown))
for i in range(0,len(data)):
tk.Label(image=tk.PhotoImage(file="D:\\A\\" + data[i][0] + ".gif")).grid(row=data[i][1]-1, column=0)
root.mainloop()
ranking()
What I want to do is to have a random number of pictures (whatever is inserted in data at any moment), displayed within the window (in the order as indicated within the data), yet if I currently run the code it simply results in a blank window.The names of the pictures are literally A, B, C...and they display without a problem if I use something like (this is an extract from another piece of code I wrote):
img2 = tk.PhotoImage(file="D:\\A\\" + choice2 + ".gif")
label_img2 = tk.Label(image=img2)
label_img2.grid(row=2, column=1)
Any guidance would be very welcome as I am still quite new with working with tkinter!
When you add a PhotoImage or other Image object to a Tkinter widget, you must keep your own reference to the image object. If you don’t, the image won’t always show up.
The problem is that the Tkinter/Tk interface doesn’t handle references to Image objects properly; the Tk widget will hold a reference to the internal object, but Tkinter does not. When Python’s garbage collector discards the Tkinter object, Tkinter tells Tk to release the image. But since the image is in use by a widget, Tk doesn’t destroy it. Not completely. It just blanks the image, making it completely transparent…
The solution is to make sure to keep a reference to the Tkinter object, for example by attaching it to a widget attribute:
for i in range(0,len(data)):
photo = tk.PhotoImage(file="D:\\A\\" + data[i][0] + ".gif")
label = tk.Label(image=photo )
label.image = photo # keep a reference!
label.grid(row=data[i][1]-1, column=0)
Taken from Why do my Tkinter images not appear?

How to put image in another window in tkinter?

I want to put an image in the second window using tkinter, in the first window the code works good, but the second window shows nothing.
In this part I import necessary modules:
from tkinter import filedialog, Tk, Frame, Label, PhotoImage, Button
from PIL import Image
from tkinter import*
import tkinter as tk
Then create the principal window:
raiz = Tk()
raiz.title("ventana")
Then I create the frame and put the image in the frame:
miFrame = Frame()
miFrame.pack()
miFrame.config(width="1400", heigh=("1200"))
fondo=tk.PhotoImage(file="fondoF.png")
fondo=fondo.subsample(1,1)
label=tk.Label(miFrame,image=fondo)
label.place(x=0,y=0,relwidth=1.0,relheight=1.0)
Then a button that will call the second window function:
btn3 = Button(raiz, text="boton")
btn3.place(x=500, y=500)
btn3.config(command=abrirventana2)
Here we have the function which opens the second window and here (I guess) is where I want to put the image.
This part also has two buttons named mih which does nothing in the meantime and ok which calls the function to close the second window:
def abrirventana2():
raiz.deiconify()
ventana2=tk.Toplevel()
ventana2.geometry('500x500')
ventana2.title("ventana2")
ventana2.configure(background="white")
fondov=tk.PhotoImage(file="xxx.gif")
label1=tk.Label(ventana2,image=fondov)
label1.place(x=50,y=50,relwidth=5.0,relheight=5.0)
mensaje=tk.Label(ventana2,text="funciona")
mensaje.pack(padx=5,pady=5,ipadx=5,ipady=5,fill=tk.X)
boton1=tk.Button(ventana2,text='mih')
boton1.pack(side=tk.TOP)
boton2=tk.Button(ventana2,text='ok',command=ventana2.destroy)
boton2.pack(side=tk.TOP)
Function to close the second window:
def cerrarventana2():
ventana.destroy()
I use the mainloop to keep the window open
raiz.mainloop()
Note: I had already tried creating a frame in the second window, but it didn't work.
Apologies for my previously incorrect answer.
The reason the image is not showing is due to the fact that you did not create a reference to it. If you don't create a reference, the image is garbage collected, which doesn't remove it, but in a sense just renders a blank placeholder on the GUI.
In order to display the image correctly you need to add a reference to the image within the code that displays the image.
You therefore now have:
fondov=tk.PhotoImage(file="giphy.gif")
label1=tk.Label(ventana2,image=fondov)
label1.image = fondov
label1.pack()
(label1.image = fondov is the reference)
Sorry for the confusion there. This should work.

python tkinter show images in a directory

I have a folder which has some images. I want to display all the images using tkinter in a single window. Also whenever I click any image displayed in the window, I need to display the path of the image. I tried using for loop but it prints all the image file path. Here is the code I tried,
from Tkinter import *
import os
from PIL import ImageTk, Image
def getFileName(image):
print str(image)
gtk = Tk()
def showImages(folder):
for images in os.listdir(os.getcwd()):
if images.endswith("png"):
im = Image.open(images)
tkimage = ImageTk.PhotoImage(im)
imageButton = Button(gtk, image=tkimage, command=getFileName(images)
imageButton.image=tkimage
imageButton.pack()
gtk.mainloop()
Can anyone say what I'm doing wrong?
for images in os.listdir(os.getcwd()):
if images.endswith("png"):
im = Image.open(images)
tkimage = ImageTk.PhotoImage(im)
handler = lambda img = images: getFileName(img) #here modify
imageButton = Button(gtk, image=tkimage, command=handler)#here
imageButton.image=tkimage
imageButton.pack()
Because button-press callbacks are run with no arguments, if we
need to pass extra datato the handler, it must be wrapped in an object
that remembers that extra data and passes it along, by deferring the
call to the actual handler. Here, a button press runs the function
generated by the lambda, an indirect call layer that retains
information from the enclosing scope. The net effect is that the real
handler.
-- << Programming Python>> page 435

Creating Blank Images in Python (allowing pixel by pixel manipulation)

I have this code here that creates a Tkinter Canvas widget, then embeds an image within it.
import Tkinter
from PIL import ImageTk, Image
class image_manip(Tkinter.Tk):
def __init__(self):
Tkinter.Tk.__init__(self)
self.configure(bg='red')
self.ImbImage = Tkinter.Canvas(self, highlightthickness=0, bd=0, bg='blue')
self.ImbImage.pack()
self.i = ImageTk.PhotoImage(Image.open(r'test.png'))
self.ImbImage.create_image(150, 100, image=self.i)
def run():
image_manip().mainloop()
if __name__ == "__main__":
run()
I'd like to be able to create a blank image within the Canvas widget, so that I could do pixel by pixel manipulation within the widget. How would one go about this?
To create a new blank image (rather than opening one), you can use the Image.new(...) method in place of your Image.open(...). It is described in The Image Module.
Then call self.i.put(...) to do pixel-by-pixel manipulation. (i is the PhotoImage object as in your example.)
Here's some general information on The Tkinter PhotoImage Class.

Categories