Using a class constructor to handle images tkinter - python

Hi i'm trying to create a class constructor to handle images in tkinter, since i'll be using many images in my code so I need a way to use images without using so much line of code.
I keep getting the error:
return _modes[mode]
KeyError: 'picture.png'
Here's the code:
from tkinter import *
import tkinter
from random import randint
from PIL import Image
from PIL import ImageTk
root = tkinter.Tk()
root.geometry('700x700')
class PhotoHandler:
def __init__(self,imagepath):
self.imagepath = imagepath
self.image = Image.open(self.imagepath)
self.image = ImageTk.PhotoImage(self.imagepath)
def returnn(self):
return self.image
search = PhotoHandler('picture.png').returnn()
root.mainloop()

You passed a string to the tkinter image. It expects a PIL image or mode. Since you passed a string, it thinks you're using an invalid mode. Pass the image instead:
class PhotoHandler:
def __init__(self,imagepath):
self.imagepath = imagepath
self.image = Image.open(self.imagepath)
self.image = ImageTk.PhotoImage(self.image)
def returnn(self):
return self.image
search = PhotoHandler('picture.png').returnn()
# add label for background image
background_label = tkinter.Label(root, image=search)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
Here's the documentation:
https://pillow.readthedocs.io/en/4.2.x/reference/ImageTk.html

Related

_tkinter.TclError: image "..." doesn't exist _ ImageGrab.grab()

Summary of code purpose: change tk.Button['image'] to either selected file, or screen snip
I'm getting this error:
_tkinter.TclError: image "<PIL.Image.Image image mode=RGB size=600x400 at 0x34045F0>" doesn't exist
But I can't understand why it doesn't exist since if I useprint(self.image_selected) I get
<PIL.Image.Image image mode=RGB size=600x400 at 0x3D16650>
Does it need to be a global variable? Doesn't adding self take care of that?
CODE
import tkinter as tk
from tkinter import filedialog
from PIL import ImageGrab, ImageTk
class MCVE():
def __init__(self, master):
self.master = master
self.ButtonOne = tk.Button(text="start",width=30,height=30,command = lambda: self.GetImg(master))
self.ButtonOne.pack()
self.image_selected = None
def GetImg(self, master):
self.newWin = tk.Toplevel(self.master)
self.ButtonTwo = tk.Button(self.newWin,text="snip", command = lambda: self.Snip(self.master))
self.ButtonThree = tk.Button(self.newWin, text="open", command = lambda: self.FileO(self.master))
self.ButtonTwo.pack()
self.ButtonThree.pack()
def Snip(self, master):
self.image_selected = ImageGrab.grab(bbox=(0,0,600,400))
self.changeImg()
def FileO(self, master):
ret = filedialog.askopenfilename() # filedialog.askopenfilename(initialdir='/home/user/images/')
if ret:
self.image_selected = ImageTk.PhotoImage(file=ret)
self.changeImg()
def changeImg(self):
if self.image_selected:
print(self.image_selected)
#self.ButtonOne['image'] = self.image_selected
self.ButtonOne.config(image=self.image_selected)
def main():
root = tk.Tk()
MCVE(root)
root.mainloop()
if __name__ == '__main__':
main()
I tested the program, and as you stated, the 'snip' button gave me the error. I could fix it by changing the format of the image to a ImageTk.PhotoImage.
def Snip(self, master):
self.image_selected = ImageTk.PhotoImage(ImageGrab.grab(bbox=(0,0,600,400)))
self.changeImg()
I have to say, though, that the photo showed on the button icon after selecting the image was weird, but I guess it was because i tested it with the wrong resolution! ;)

Class for picture view that change pic on mouse click

I want to make a class that has a picture and it is changed to the next one by mouse click.I'm new to oop, my idea here was to make class similar to real life where there is new class instance for every new picture, is it possible to do it this way? Here is my code
import tkinter as tk
from PIL import Image,ImageTk
class Picture():
_count=1
def __init__(self,window):
self.id=Picture._count
Picture._count+=1
self.img=Image.open(r'C:\ImgArchive\img%s.png' % self.id)
self.pimg = ImageTk.PhotoImage(self.img)
self.lab=tk.Label(window,image=self.pimg)
self.lab.pack()
self.lab.bind('<1>',self.click)
def click(self,event):
self.lab.destroy()
self=self.__init__(window)
window = tk.Tk()
window.title('Album')
window.geometry('1200x900')
pic=Picture(window)
window.mainloop()
It works fine, but i'm not sure that old instances of my class is deleted, are they? And i use self.lab.destroy() because if i dont new picture appears down, like this
instead of this
So why it happens?What is elegant way for it?
Below example produces a simple image viewer tested with path of C:\Users\Public\Pictures\Sample Pictures, let me know if anything's unclear:
import tkinter as tk
from PIL import Image, ImageTk
#required for getting files in a path
import os
class ImageViewer(tk.Label):
def __init__(self, master, path):
super().__init__(master)
self.path = path
self.image_index = 0
self.list_image_files()
self.show_image()
self.bind('<Button-1>', self.show_next_image)
def list_files(self):
(_, _, filenames) = next(os.walk(self.path))
return filenames
def list_image_files(self):
self.image_files = list()
for a_file in self.list_files():
if a_file.lower().endswith(('.jpg', '.png', '.jpeg')):
self.image_files.append(a_file)
def show_image(self):
img = Image.open(self.path + "\\" + self.image_files[self.image_index])
self.img = ImageTk.PhotoImage(img)
self['image'] = self.img
def show_next_image(self, *args):
self.image_index = (self.image_index + 1) % len(self.image_files)
self.show_image()
root = tk.Tk()
mypath = r"C:\Users\Public\Pictures\Sample Pictures"
a = ImageViewer(root, mypath)
a.pack()
root.mainloop()

using subsample with PhotoImage in Tkinter and PIL

i folks,
I'm trying to use the subsample method in the PhotoImage class to resize an image I have in a list that links to a Label widget in Tkinter but python says there is no such method. I know I could use Image.resize before calling Photoimage but since I'd like to resize the image at any time I don't know what to do once it's been converted to PhotoImage. Could someone please show me what I'm doing wrong? I'm relatively new to Tkinter and very new to PIL...Thanks...
from Tkinter import *
from PIL import Image, ImageTk
class imgList(object):
def __init__(self,imgs):
self.list=[]
for i in imgs:
image=Image.open(i)
photo=ImageTk.PhotoImage(image)
self.list.append(photo)
def resize(self,num,fact):
self.list[num]=self.list[num].subsample(fact)
#image=image.resize((50,100),Image.ANTIALIAS)
#photo=ImageTk.PhotoImage(image)
#photo.subsample(2,2)
#self.list[num]=photo
class Slot(object):
def __init__(self,root,canvas,appimg,x,y):
self.root=root
self.canvas=canvas
self.appimage=appimg
self.l=Label(self.root,image=self.appimage)
self.l.place(x=x,y=y)
class View(object):
def __init__(self,canvas):
self.canvas=canvas
class win1(View):
def slotbind(self,event):
print("heloo")
self.imglist.resize(0,2)
def draw(self,root,tv):
self.canvas.delete(ALL)
self.root=root
#self.photolist=pl
self.imglist=imgList(["pic1.bmp"])
self.tv=tv
self.s1=Slot(self.root,self.canvas,self.imglist.list[0],10,10)
self.s1.l.bind("<1>",self.slotbind)
self.qbtn=Button(self.root,text="quit",command=self.quit)
self.qbtn.place(x=270,y=100)
self.qbtn.lift()
def quit(self):
self.qbtn.destroy()
self.s1.l.destroy()
self.tv[1].draw(self.root,self.tv)
class win2(View):
def draw(self,root,tv):
self.canvas.delete(ALL)
self.root=root
self.tv=tv
imglist=imgList(["pic3.bmp","pic4.bmp"])
self.s1=Slot(self.root,self.canvas,imglist.list[1],270,10)
self.qbtn=Button(self.root,text="quit2",command=self.quit)
self.qbtn.place(x=500,y=100)
self.qbtn.lift()
def quit(self):
self.qbtn.destroy()
self.s1.l.destroy()
self.tv[0].draw(self.root,self.tv)
class win3(View):
def draw(self):
pass
class App(object):
def __init__(self, width=640, height=480):
self.width = width
self.height = height
self.root = Tk()
self.root.title("tkinter_test01")
self.root.geometry("%sx%s"%(self.width, self.height))
self.canvas = Canvas(self.root, width=self.width, height=self.height)
self.theviews=[win1(self.canvas),win2(self.canvas),win3(self.canvas)]
self.theviews[0].draw(self.root,self.theviews)
self.canvas.pack()
self.root.mainloop()
app=App()
Bad news, subsample and zoom are not available in ImageTk.PhotoImage, and only in Tkinter.PhotoImage. The later only accepts PPM, PGM, GIF, and if you are using Python with Tk 8.6b2 or later (very unlikely at this time) then there is also support for PNG images.
Maybe this code will solve the problem - I tried it succesfully:
# http://effbot.org/imagingbook/image.htm
import Tkinter
# from PIL
import Image, ImageTk
im = Image.open('z:/g1.jpg')
# imc=im.copy()
imc=im.transpose(Image.ROTATE_270)
(x0,y0,x1,y1)=imc.getbbox() # returns (0,0,w,h)
print 'x0=%d y0=%d x1=%d y1=%d\n' % (x0,y0,x1,y1)
tkroot=Tkinter.Tk()
# tki=ImageTk.PhotoImage(im)
# tkl=Tkinter.Label(tkroot, image=tki)
# tkl.pack()
imc.thumbnail((1+x1/5,1+y1/5)) # changes image in place!
tkic=ImageTk.PhotoImage(imc)
tklc=Tkinter.Label(tkroot,image=tkic)
tklc.pack()
tkroot.mainloop() # Start the GUI

Python Tkinker - showing a jpg as a class method not working

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()

Pixel Manipulation via GUI in Python (poss PIL, Tkinter)

Here's the issue -
I'm writing a program that will iterate through a series of image manipulations - both effecting the whole image as well as just portions of the image. I need to demonstrate these changes to the user (myself) so I can see what's going wrong with my manipulations as they take place.
I originally tried to use PIL and Tkinter to do this - but I couldn't even load an image into the GUI - here's a bit of code formed from the corners of the web, via many google searches:
from Tkinter import *
import Image, ImageDraw, ImageTk
import time
class Test(Frame):
def __init__(self):
Frame.__init__(self)
self.c = Canvas(self, width=574, height=431, bg="red")
self.c.pack()
Button(self, text="Process", command=self.procImg).pack()
Button(self, text="Quit", command=self.quit).pack()
def procImg(self):
t = time.time()
self.flashImg = Image.open("./in_img/resize.bmp")
#self.flashImg = flashImg.resize((574, 431))
self.flashImg = self.flashImg.convert("L")
#self.photo = ImageTk.BitmapImage(flashImg)
self.c.photo = ImageTk.PhotoImage(self.flashImg)
self.c.create_image(574, 431, anchor=NW, image=self.c.photo)
self.c.create_rectangle(50, 50, 100, 100, fill="blue")
self.update()
print time.time()-t
t = Test()
t.pack()
t.mainloop()
So the above code is pretty bad, I know - but I wanted to post something to prove that I have at least been working at this.
Can anyone suggest to me a new way of approaching this problem using Python? I'd rather not learn a different language - I'm new to the Tkinter library so if something else is better suited for this, I have no issues learning a new library.
Also, FYI, the "resize.bmp" image is a resized .JPG from a digital camera. I tried that one too and it didn't work - I really need to find a way to flash bitmaps from memory to the screen in a GUI so I can adjust parameters as the processing is going on.
Thanks for your help!
The image is probably there. It's just not visible.
Instead of :
self.c.create_image(574, 431, anchor=NW, image=self.c.photo)
try :
self.c.create_image(0, 0, anchor=NW, image=self.c.photo)
Also, if you keep a reference to the canvas image item, you can swap different images in and out.
eg. (self.canvasItem) below :
from Tkinter import *
from PIL import Image, ImageTk, ImageDraw, ImageOps, ImageEnhance
class ImageButcher(Tk):
def __init__(self):
Tk.__init__(self)
#create ui
f = Frame(self, bd=2)
self.colour = StringVar(self)
self.colourMenu = OptionMenu(f, self.colour,
*('red','green','blue','white'))
self.colourMenu.config(width=5)
self.colour.set('red')
self.colourMenu.pack(side='left')
self.rectangleButton = Button(f, text='Rectangle',
command=self.draw_rectangle)
self.rectangleButton.pack(side='left')
self.brightenButton = Button(f, text='Brighten',
command=self.on_brighten)
self.brightenButton.pack(side='left')
self.mirrorButton = Button(f, text='Mirror',
command=self.on_mirror)
self.mirrorButton.pack(side='left')
f.pack(fill='x')
self.c = Canvas(self, bd=0, highlightthickness=0,
width=100, height=100)
self.c.pack(fill='both', expand=1)
#load image
im = Image.open('IMG_1584.JPG')
im.thumbnail((512,512))
self.tkphoto = ImageTk.PhotoImage(im)
self.canvasItem = self.c.create_image(0,0,anchor='nw',image=self.tkphoto)
self.c.config(width=im.size[0], height=im.size[1])
self.img = im
self.temp = im.copy() # 'working' image
def display_image(self, aImage):
self.tkphoto = pic = ImageTk.PhotoImage(aImage)
self.c.itemconfigure(self.canvasItem, image=pic)
def on_mirror(self):
im = ImageOps.mirror(self.temp)
self.display_image(im)
self.temp = im
def on_brighten(self):
brightener = ImageEnhance.Brightness(self.temp)
self.temp = brightener.enhance(1.1) # +10%
self.display_image(self.temp)
def draw_rectangle(self):
bbox = 9, 9, self.temp.size[0] - 11, self.temp.size[1] - 11
draw = ImageDraw.Draw(self.temp)
draw.rectangle(bbox, outline=self.colour.get())
self.display_image(self.temp)
app = ImageButcher()
app.mainloop()

Categories

Resources