how to resize an image to fit the label size? (python)

10,954

Solution 1

If you don't have PIL installed, first you need to install

pip install pillow

Once installed you can now import from PIL:

from PIL import Image, ImageTk

Tk's PhotoImage can only display .gif's, whereas PIL's ImageTk will let us display various image formats in tkinter and PIL's Image class has a resize method we can use to resize the image.

I trimmed your code down some.

You can resize the image and then just configure the label, the label will expand to be the size of the image. If you gave the label a specific height and width, lets say height=1 and width=1 and you resized the image to be 500x500 and then configured the widget. It would still display a 1x1 label since you've set these attributes explicitly.

In the below code, modifiying the dict, it is not okay to modify a dict while iterating over it. dict.items() returns a copy of the dict.

There's various ways to do this, I just though a dict was convenient here.

Link to an image that's over the height / width limit - kitty.gif

from tkinter import *
import random
from PIL import Image, ImageTk

WIDTH, HEIGHT = 150, 150
flags = {
    'England': 'england.gif',
    'Wales': 'wales.gif', 
    'Kitty': 'kitty.gif'
}

def batch_resize():

    for k, v in flags.items():
        v = Image.open(v).resize((WIDTH, HEIGHT), Image.ANTIALIAS)
        flags[k] = ImageTk.PhotoImage(v)

def newcountry():

    country = random.choice(list(flags.keys()))
    image = flags[country]
    flagLabel['text'] = country
    flagpicture.config(image=image)

if __name__ == '__main__':

    root = Tk()
    root.configure(bg='black')

    batch_resize()

    flagLabel = Label(root, text="", bg='black', fg='cyan', font=('Helvetica',40))
    flagLabel.pack()

    flagpicture = Label(root)
    flagpicture.pack()

    newflagButton = Button(root, text="Next Country", command=newcountry)
    newflagButton.pack()
    root.mainloop()

Solution 2

Instead of randomly selecting a country to display its flag, we loop through the flags dictionary that is key-sorted. Unlike the random choice which will inevitably repeat the flags, this scheme runs through the countries in alphabetical order. Meanwhile, we resize all the images to a fixed pixel size based on the width and height of the root window multiplied by a scale factor. Below is the code:

import  tkinter as tk
from PIL import Image, ImageTk

class   Flags:

    def __init__(self, flags):
        self.flags = flags
        self.keyList = sorted(self.flags.keys()) # sorted(flags)
        self.length = len(self.keyList)
        self.index = 0

    def resize(self, xy, scale):
        xy = [int(x * y) for (x, y) in zip(xy, scale)]
        for k, v in self.flags.items():
            v = Image.open(r'C:/Users/user/Downloads/' + v)
            v = v.resize((xy[0], xy[1]), Image.ANTIALIAS)
            self.flags[k] = ImageTk.PhotoImage(v)

    def newCountry(self, lbl_flag, lbl_pic):
        country = self.keyList[self.index]
        lbl_flag["text"] = country
        img = self.flags[country]
        lbl_pic.config(image = img)
        self.index = (self.index + 1) % self.length # loop around the flags dictionary

def rootSize(root):
    # Find the size of the root window
    root.update_idletasks()
    width = int(root.winfo_width() * 1.5)      #   200 * m
    height = int(root.winfo_height() * 1.0)    #   200 * m
    return width, height

def centerWsize(root, wh):
    root.title("Grids layout manager")
    width, height = wh
    # Find the (x,y) to position it in the center of the screen
    x = int((root.winfo_screenwidth() / 2) - width/2)
    y = int((root.winfo_screenheight() / 2) - height/2)
    root.geometry("{}x{}+{}+{}".format(width, height, x, y))

if __name__ == "__main__":

    flags = {
        "Republic of China": "taiwan_flag.png",
        "United States of America": "america_flag.gif",
        "America": "america_flag.png",
    }

    root = tk.Tk()
    wh = rootSize(root)
    centerWsize(root, wh)
    frame = tk.Frame(root, borderwidth=5, relief=tk.GROOVE)
    frame.grid(column=0, row=0, rowspan=3)
    flag = Flags(flags)
    zoom = (0.7, 0.6)   # Resizing all the flags to a fixed size of wh * zoom
    flag.resize(wh, zoom)
    lbl_flag = tk.Label(frame, text = "Country name here", bg = 'white', fg = 'magenta', font = ('Helvetica', 12), width = 30)
    lbl_flag.grid(column = 0, row = 0)
    pic_flag = tk.Label(frame, text = "Country flag will display here")
    pic_flag.grid(column = 0, row = 1)
    btn_flag = tk.Button(frame, text = "Click for next Country Flag",
                         bg = "white", fg = "green", command = lambda : flag.newCountry(lbl_flag, pic_flag))
    btn_flag.grid(column = 0, row = 2)

    root.mainloop()
Share:
10,954
Minch
Author by

Minch

Updated on June 04, 2022

Comments

  • Minch
    Minch almost 2 years

    My aim is to create a random country generator, and the flag of the country that is picked will appear. However, if the image file is bigger than the predetermined size of the label, then only part of the image is displayed. Is there a way of resizing the image to fit the label? (All other questions like this which I have seen have been answered, mentioning the PIL or Image modules. I tested them both, and they both came up with this error:

    Traceback (most recent call last): File "C:\python\country.py", line 6, in import PIL ImportError: No module named 'PIL'

    This is my code, if it helps:

    import tkinter
    from tkinter import *
    import random
    
    flags = ['England','Wales','Scotland','Northern Ireland','Republic of Ireland']
    
    def newcountry():
    
        country = random.choice(flags)
        flagLabel.config(text=country)
        if country == "England":
            flagpicture.config(image=England)
        elif country == "Wales":
            flagpicture.config(image=Wales)
        elif country == "Scotland":
            flagpicture.config(image=Scotland)
        elif country == "Northern Ireland":
            flagpicture.config(image=NorthernIreland)
        else:
            flagpicture.config(image=Ireland)
    
    root = tkinter.Tk()
    root.title("Country Generator")
    
    England = tkinter.PhotoImage(file="england.gif")
    Wales = tkinter.PhotoImage(file="wales.gif")
    Scotland = tkinter.PhotoImage(file="scotland.gif")
    NorthernIreland = tkinter.PhotoImage(file="northern ireland.gif")
    Ireland = tkinter.PhotoImage(file="republic of ireland.gif")
    blackscreen = tkinter.PhotoImage(file="black screen.gif")
    
    flagLabel = tkinter.Label(root, text="",font=('Helvetica',40))
    flagLabel.pack()
    
    flagpicture = tkinter.Label(root,image=blackscreen,height=150,width=150)
    flagpicture.pack()
    
    newflagButton = tkinter.Button(text="Next Country",command=newcountry)
    newflagButton.pack()
    

    The code works perfectly fine apart from only showing part of the image. Is there a way to resize the images within the code itself?(I am using Python 3.5.1)