How do I display resized images in tkinter using classes? - python

I'm trying to display a resized image, but I'm not sure how to do that using classes. This code works fine:
image = Image.open(Image_Location)
image = image.resize((200, 200), Image.ANTIALIAS)
image = ImageTk.PhotoImage(image)
But I need to do this in a class method (because I need multiple for the main project and it would take too long to do it seperately), how do I do it? This is what I have tried so far:
class Planet:
def __init__(self, name, picture):
self.name = name
self.picture = tk.PhotoImage(file=picture)
#classmethod
def resize_image(cls, picture):
image = Image.open(picture)
image = image.resize((200, 200), Image.ANTIALIAS)
image = ImageTk.PhotoImage(image)
earth = Planet("earth",Image_Location)
earth_resized = earth.resize_image(Image_Location)
test1 = Label(root, image=earth_resized)
test2 = Label(root, image=earth.picture)
test1.pack()
test2.pack()
And when I pack both labels, I get a white space (I think that is supposed to be the resized image) and the unsized image. Thanks!

You forgot to return the resized image from the method -- it should look like this:
#classmethod
def resize_image(cls, picture):
image = Image.open(picture)
image = image.resize((200, 200), Image.ANTIALIAS)
return ImageTk.PhotoImage(image)

Related

How to create box around selected image on GUI

My GUI is a slideshow of images and there is a certain correct image on each page of the slideshow. The correct image is the top-left triangle, heart balloon, the circle, and the hippo. I am completely new to python so I have been struggling with how to create box around the selected image when a user is using the slideshow. This box forms around the correct or the incorrect image, whichever is pressed. To further explain, this includes inserting a box around an image based on where the user clicks but it does not say whether the image selected is the correct image or not. Thank you for your help
import PIL.Image
import PIL.ImageDraw
import tkinter as tk
import PIL.ImageTk
import csv
from PIL import Image
MAX_HEIGHT = 500
# height of the window (dimensions of the image)
class App(tk.Frame):
def __init__(self, imageData, master=None):
tk.Frame.__init__(self, master)
self.clickStatus = tk.StringVar()
self.loadedImages = dict()
self.master.title('Slideshow')
fram = tk.Frame(self)
tk.Button(fram, text="Previous Image", command=self.prev).pack(side=tk.LEFT)
tk.Button(fram, text=" Next Image ", command=self.next).pack(side=tk.LEFT)
tk.Label(fram, textvariable=self.clickStatus, font='Helvetica 18 bold').pack(side=tk.RIGHT)
# inside or outside
fram.pack(side=tk.TOP, fill=tk.BOTH)
self.imageLabel = tk.Label(self)
# drawing the image on the label
self.imageData = imageData
self.currentIndex = 0
# start from 0th image
self.__loadImage__()
self.imageLabel.bind("<Button-1>", self.clicked_evt)
# when you click button, it opens event of clicked_evt
self.imageLabel.pack()
self.pack()
def clicked_evt(self, evt):
x, y = evt.x, evt.y
imgData = self.loadedImages[self.imageData[self.currentIndex]['image_file']]
(l, t), (r,b) = imgData['lt'], imgData['rb']
if t<=y<=b and l<=x<=r:
##self.clickStatus.set('inside')
print('Inside')
else:
##self.clickStatus.set('outside')
print('Outside')
def __loadImage__(self):
if self.imageData[self.currentIndex]['image_file'] not in self.loadedImages:
self.im = PIL.Image.open(self.imageData[self.currentIndex]['image_file'])
ratio = MAX_HEIGHT/self.im.height
# ratio divided by existing height -> to get constant amount
height, width = int(self.im.height*ratio), int(self.im.width * ratio)
# calculate the new h and w and then resize next
self.im = self.im.resize((width, height))
lt = (int(self.imageData[self.currentIndex]['left']*ratio), int(self.imageData[self.currentIndex]['top']*ratio))
rb = (int(self.imageData[self.currentIndex]['right']*ratio), int(self.imageData[self.currentIndex]['bottom']*ratio))
# modifying new ratios with new height and width
#shape = [lt, rb]
# print(shape)
#img1 = PIL.ImageDraw.Draw(self.im)
#img1.rectangle(shape, outline ="red")
if self.im.mode == "1":
self.img = PIL.ImageTk.BitmapImage(self.im, foreground="white")
else:
self.img = PIL.ImageTk.PhotoImage(self.im)
imgData = self.loadedImages.setdefault(self.imageData[self.currentIndex]['image_file'], dict())
imgData['image'] = self.img
imgData['lt'] = lt
imgData['rb'] = rb
# for next and previous so it loads the same image adn don't do calculations again
self.img = self.loadedImages[self.imageData[self.currentIndex]['image_file']]['image']
self.imageLabel.config(image=self.img, width=self.img.width(), height=self.img.height())
def prev(self):
self.currentIndex = (self.currentIndex+len(self.imageData) - 1 ) % len(self.imageData)
self.__loadImage__()
# here if i go to the first one and press back, goes to last, round robbin
def next(self):
self.currentIndex = (self.currentIndex + 1) % len(self.imageData)
self.__loadImage__()
# here if i go to the last one and press next, goes to first, round robbin
def loadData(fname):
with open(fname, mode='r') as f:
reader = csv.DictReader(f)
data = [dict(row) for row in reader]
for row in data:
row['top'], row['bottom'], row['left'], row['right'] = int(row['top']),int(row['bottom']),int(row['left']),int(row['right'])
return data
if __name__ == "__main__":
data = loadData('bounding_box.csv')
app = App(data)
app.mainloop()
As far as I understand you simply want to draw a rectangle around an image when it is clicked (explanation in code comments):
from tkinter import Tk, Canvas
from PIL import Image, ImageTk
# below code is used to create images, you can also load them from a file if you need
mode = 'RGB'
size = (150, 150)
color_lst = ['red', 'green', 'blue', 'yellow']
# best to get all the images for the slide in a single list for further easier workings
img_lst = [Image.new(mode, size, color) for color in color_lst]
# selecting function that will be called when user clicks on image
def select(id_):
canvas.create_rectangle(canvas.bbox(id_), width=5)
# here you will put other code to be executed after
# user clicks on the image like going to the next frame or sth
# additionally you could use this if you want the other rectangles to disappear
# but kinda pointless if you will switch frames and stuff
# for canvas_id in canvas_images:
# if canvas_id == id_:
# continue
# canvas.create_rectangle(canvas.bbox(canvas_id), width=5, outline='white')
root = Tk()
# here create a photoimage list from the above image list
photo_lst = [ImageTk.PhotoImage(image) for image in img_lst]
# create canvas
canvas = Canvas(root, width=400, height=400)
canvas.pack()
# set the coordinates for the four images
coordinates = [(100, 100), (300, 100), (100, 300), (300, 300)]
# create the images and append their id to a list
canvas_images = [
canvas.create_image(pos[0], pos[1], image=photo) for pos, photo in zip(coordinates, photo_lst)
]
# bind the ids from `canvas_images` to being clicked and execute simple drawing method
for c_img in canvas_images:
canvas.tag_bind(
c_img, '<Button-1>', lambda e, i=c_img: select(i)
)
root.mainloop()

PIL ImageTk AttributeError, can't display ImageTk on window

I've been working on a small program to convert PNG, JPG and JPEG files to .ICO files. This was relatively simple to do, but while I was trying to display the selected PNG image in Tkinter using PIL's ImageTk, I get a strange error.
from tkinter import *
from tkinter import filedialog
import re
from PIL import Image, ImageTk
root = Tk()
pathToImage = ''
selectedImage = ''
def make_square(im, min_size=256, fill_color = (0, 0, 0)): # Puts the selected image into a black square
x, y = im.size
size = max(min_size, x, y)
new_im = Image.new('RGB', (size,size), fill_color)
new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
return new_im
def select_image(): # Function that is run when Select PNG button is clicked
global pathToImage
pathToImage = filedialog.askopenfilename(filetypes=[('PNG Files','*.png'),('JPG Files','*.jpg'),('JPEG Files','*.jpeg')]) # Gets path to PNG, JPG or JPEG image
image = Image.open(pathToImage) # Opens image in PIL
image = make_square(im=image) # Turns image into square for ICO conversion
#!!!!!!!!!!!!!!!!!!!!! ERROR Among these 3 lines
global selectedImage # Here I try to tell Python I'm referring to the global variable selectedImage
selectedImage = (ImageTk.PhotoImage(image=pathToImage)) # selectedImage is given the value of ImageTk.PhotoImage with the source image being the path of the selected image
Label(root, image=selectedImage).pack() # Throws an error for some reason
# Rest of the code works fine
image.save('output.ico')
Label(root,text='Converted file stored in the same folder as \'PNG to ICO.py\'').pack()
Button(root,text='Select PNG', command=select_image).pack()
root.mainloop()
I've tried saving the image to display to a variable, but that doesn't seem to work either. Could anyone help point out what I did wrong? I'd really appreciate it.
There are couple of issues with your code.
In your line (ImageTk.PhotoImage(image=pathToImage)) you are passing a path (str) which is not what it should take, ImageTk.PhotoImage takes instance of Image(path). So change it to the image getting returned by make_square function.
Every time when the button is clicked, it'll create a new label if that's what you want then ignore this, if not then create your labels outside of the function select_image after you create the Button and later in the function update them.
I don't really get why you are using global when you can achieve your purpose without making variables pathToImage or selectedImage unless you want to access that image later in the program.
Here is the improved version of your code.
from tkinter import *
from tkinter import filedialog
import re
from PIL import Image, ImageTk
def make_square(im, min_size=256, fill_color = (0, 0, 0)): # Puts the selected image into a black square
x, y = im.size
size = max(min_size, x, y)
new_im = Image.new('RGB', (size,size), fill_color)
new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
return new_im
def select_image(): # Function that is run when Select PNG button is clicked
pathToImage = filedialog.askopenfilename(filetypes=[('PNG Files','*.png'),('JPG Files','*.jpg'),('JPEG Files','*.jpeg')])
image = Image.open(str(pathToImage)) # Opens image in PIL
image = make_square(im=image) # Turns image into square for ICO conversion
selectedImage = ImageTk.PhotoImage(image=image)
imglabel.img = selectedImage # create a reference of the image
imglabel['image'] = selectedImage
# selectedImage is given the value of ImageTk.PhotoImage with the source image being the path of the selected image
# Rest of the code works fine
image.save('output.ico', 'ICO')
infolabel['text'] = 'Converted file stored in the same folder as \'PNG to ICO.py\''
root = Tk()
but1 = Button(root,text='Select PNG', command=select_image)
but1.pack()
imglabel = Label(root)
imglabel.pack()
infolabel = Label(root)
infolabel.pack()
root.mainloop()

Resizing PIL rgb image in tkinter application

background
I am creating a small slideshow application that allows for displaying an image. The original image is quite large (2000 by 2000 pixels), so I want to rescale it to fit the screen. This works as expected.
The image is essentially an array, with values ranging from 0 to 16 bit.
To get PIL to display colours, I triplicate the array, manipulate each channel individually, convert to an Image object, rescale and display. Here, the rescaling does not seem to work properly.
approach
I am sharing a barebones version of my code that repoduces this issue, at least on my system.
import tkinter as tk
import numpy as np
from PIL import Image, ImageTk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
self.images = {}
self.load_image()
self.show_image()
def load_image(self):
img = np.zeros((2000,2000))
img[100:800, 100:800] = 255
self.images[0] = img
def show_image(self):
img = self.images[0]
img = np.repeat(img[:,:,np.newaxis], 3, axis=2)
img = Image.fromarray(img, 'RGB')
img = img.resize((1000,1000))
img = ImageTk.PhotoImage(img)
self.persistent_img = img
self.canvas = tk.Canvas(self.parent, height=1000, width=1000)
self.canvas.grid(row=0, column=0)
self.canvas.create_image(0,0,anchor='nw', image=img)
if __name__ == '__main__':
root = tk.Tk()
MainApplication(root)
root.mainloop()
expected
In fact, this works with the code above, if I just rescale the one-channel image:
img = np.zeros((2000,2000))
img[100:800, 100:800] = 255
self.images[0] = img
[...]
img = self.images[0]
img = Image.fromarray(img)
img = img.resize((1000,1000))
[...]
actual
What is causing this, and is there a way around? Help is highly appreciated!
Should set dtype=uint8 in np.zeros(...) for image data:
np.zeros((2000, 2000), dtype=np.uint8)

I cannot open an image using a canvas and create_image on tkinter

I am making a game for the repl.it game jam. I am trying to put an image on a canvas that was created in a different class from the class that the image is being created on. The canvas has successfully displayed text and a button with the image that I need, but create_image is not working.
This might be an issue with resolution, but I cannot check that right now, the image is 1920 x 1080 and so is the game.
I've already tried create_image.
Full program (including images)
class game(Application):
"""Where game elements are to be held"""
def __init__(self, players, location, sWidth, sHeight, canvas):
"""Initiate variables & start game"""
#Initiate variables
self.players = players
self.location = location
self.screenWidth = sWidth
self.screenHeight = sHeight
self.Canvas1 = canvas
self.LOCATIONS = Application.LOCATIONS
self.font = Application.font
#Gathering Images
self.map1BG = PhotoImage(file = "polasib.gif")
#Debugging
print("Loading Map", self.location\
, "\nPlayers:", self.players)
self.createLevel(self.location)
def createUI(self, players):
"""Creates the UI that all levels will have"""
self.Canvas1.create_text(self.screenWidth/2, self.screenHeight/16, fill = "white", \
font = (self.font, self.screenWidth//34), text = self.LOCATIONS[self.location - 1])
def createLevel(self, location):
"""Creates the elements of the level"""
if self.location == 1:
#Polasi b
print("Creating Polasi b Level")
self.createUI(self.players)
self.Canvas1.create_image(self.screenWidth/2, self.screenHeight/2, image = self.map1BG, \
anchor = NW)
Expectation: I expect the image to load (and that it will require some realignment)
Result: No image appears but everything else added (as a test) works.
Since you did not save the reference to the instance of game, it will be destroyed after exiting Application.gameScreen(). Therefore the reference of the image of create_image will be lost and no image will be shown. Try assigning the instance of game to an instance variable of Application, like below:
self.running_game = game(self.players, self.mapChosen, self.screenWidth, self.screenHeight, self.Canvas1)

How Tkinter could load png images with transparent backgrounds?

Some weeks ago i load a png image into my Tkinter Canvas and drawed with create_image, but now i can't do this anymore, i tried convert with ImageTk but png did not display
I have the following code:
load = Image.open("mouse.png")
self.img = ImageTk.PhotoImage(load)
self.draw.create_image(100,100,image=self.img,anchor = NW)
self.draw.image = self.img
I just need to present a png image
Try this
vSmallIco = (15, 15)
self.original = Image.open('.//data//img//plus.png')
resized = self.original.resize(vSmallIco, Image.ANTIALIAS)
self.plusIco = ImageTk.PhotoImage(resized)
self.medIco = ttk.Label(self.mf, image=self.plusIco, style='Main.TLabel')
this is done with this import
from PIL import Image, ImageTk
Also, please use a *.png image if you wish to use png.

Categories