Tkinter resize background image to window size
Asked Answered
M

5

18

Trying to set up a background for my tkinter window. I have a square background image, which fades to black around the edges, and then the main window has a black background. The image is placed over the background, and if the window is wider than it is tall, the image centers itself in the middle over the black background, and it all looks very nice.

However when the window is smaller than the image in width and height, it puts the center of the image in the center of the window, so you don't see the whole image, and it looks a little odd. Is there a way of resizing the image so that if the largest of the width and height of the window is smaller than the image, the image is adjusted to that size, keeping aspect ratio.

So say the background image is 600x600:

  • In a 800x400 window, the image does not resize, and centers itself vertically.
  • In a 500x400 window, the image resizes to 500x500, and still centers itself vertically.
  • In a 400x900 window, the image does not resize, and centers itself horizontally.

The centering functionality is already there, I just need the resize functionality.

Currently what I have is:

from tkinter import *

root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")

background_image = PhotoImage(file="Background.gif")

background = Label(root, image=background_image, bd=0)
background.pack()

root.mainloop()

Not sure if there is a way of doing this in tkinter? Or if perhaps I would write my own function that resizes the image according to the window size, however the image needs to resize relatively smoothly and quickly if the user resizes the window at any point.

Munshi answered 5/6, 2014 at 13:7 Comment(1)
Use pillow for image resizing.Renwick
R
33

This is example application that uses Pillow to resize image on the Label as the label changes size:

from tkinter import *

from PIL import Image, ImageTk

root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")



class Example(Frame):
    def __init__(self, master, *pargs):
        Frame.__init__(self, master, *pargs)



        self.image = Image.open("./resource/Background.gif")
        self.img_copy= self.image.copy()


        self.background_image = ImageTk.PhotoImage(self.image)

        self.background = Label(self, image=self.background_image)
        self.background.pack(fill=BOTH, expand=YES)
        self.background.bind('<Configure>', self._resize_image)

    def _resize_image(self,event):

        new_width = event.width
        new_height = event.height

        self.image = self.img_copy.resize((new_width, new_height))

        self.background_image = ImageTk.PhotoImage(self.image)
        self.background.configure(image =  self.background_image)



e = Example(root)
e.pack(fill=BOTH, expand=YES)


root.mainloop()

This is how it works using Lenna image as example:

enter image description here

Renwick answered 6/6, 2014 at 5:13 Comment(1)
you could skip resizing for some events, see fit_image() methodTauro
S
11

I have modified the above code so it is not in a class

#!/usr/bin/python3.5

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

root = Tk()
root.title("Title")
root.geometry('600x600')

def resize_image(event):
    new_width = event.width
    new_height = event.height
    image = copy_of_image.resize((new_width, new_height))
    photo = ImageTk.PhotoImage(image)
    label.config(image = photo)
    label.image = photo #avoid garbage collection

image = Image.open('image.gif')
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = ttk.Label(root, image = photo)
label.bind('<Configure>', resize_image)
label.pack(fill=BOTH, expand = YES)

root.mainloop()
Saiff answered 31/3, 2016 at 12:7 Comment(1)
I would replace ttk.Label with just Label, somehow there's a weird border left and top, even after trying to remove it with padx=0, pady=0, etc.Barrera
H
2

Just sugesting a slight change in the answer. Using self.master.winfo_width(),self.master.winfo_height() instead of 'event' makes he adjustment to size much quicker.

import tkinter as tk
from PIL import Image, ImageTk
class Layout:
     def __init__(self,master):
       self.master = master
       self.rootgeometry()
       self.canvas = tk.Canvas(self.master)
       self.canvas.pack()
       self.background_image = Image.open('image_file.PNG') 
       self.image_copy = self.background_image.copy()
       self.background = ImageTk.PhotoImage(self.background_image)
       self.loadbackground()

    def loadbackground(self):
       self.label = tk.Label(self.canvas, image = self.background)
       self.label.bind('<Configure>',self.resizeimage)
       self.label.pack(fill='both', expand='yes')


   def rootgeometry(self):
       x=int(self.master.winfo_screenwidth()*0.7)
       y=int(self.master.winfo_screenheight()*0.7)
       z = str(x) +'x'+str(y)
       self.master.geometry(z)

  def resizeimage(self,event):
       image = self.image_copy.resize((self.master.winfo_width(),self.master.winfo_height()))
       self.image1 = ImageTk.PhotoImage(image)
       self.label.config(image = self.image1)

root = tk.Tk()
a = Styling.Layout(root)
root.mainloop()
Hardtop answered 20/2, 2020 at 11:21 Comment(0)
F
1

In this code, the fit_image_to_window function resizes the image to fit the window while maintaining its aspect ratio. The update_image function is bound to the window resizing event and updates the image accordingly whenever the window size changes.

import tkinter as tk
from PIL import Image, ImageTk

def fit_image_to_window(image_path, window_size):
    # Load the image
    image = Image.open(image_path)
    image_width, image_height = image.size
    
    # Calculate the aspect ratio
    aspect_ratio = image_width / image_height
    
    # Get the window size
    window_width, window_height = window_size
    
    # Calculate new size keeping the aspect ratio
    if (window_width / window_height) > aspect_ratio:
        new_width = int(window_height * aspect_ratio)
        new_height = window_height
    else:
        new_width = window_width
        new_height = int(window_width / aspect_ratio)
    
    # Resize the image
    resized_image = image.resize((new_width, new_height), Image.LANCZOS)
    return ImageTk.PhotoImage(resized_image)

def update_image(event):
    new_image = fit_image_to_window(image_path, (event.width, event.height))
    label.config(image=new_image)
    label.image = new_image  # Avoid garbage collection

# Create the main application window
root = tk.Tk()
root.geometry("400x300")

# Image path
image_path = "<your_image>.jpg"  # Replace with your image path

# Fit the image to the window
photo = fit_image_to_window(image_path, (400, 300))

# Create a label to display the image
label = tk.Label(root, image=photo)
label.pack(fill=tk.BOTH, expand=True)

# Bind the update_image function to the window resizing event
root.bind("<Configure>", update_image)

# Run the application
root.mainloop()

Flemish answered 26/3 at 17:2 Comment(0)
E
0

i have created function for calling resize a single time with methods after et after cancel

    def on_resize(self, evt):
        
        if self.inter == 0:
            self.inter = 1
            self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
        else:
            self.minuteur = self.fenetrePrincipale.after_cancel(self.minuteur)
            self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
            
    def endResize(self):
        self.inter = 0
        self.fenetrePrincipale.background = self.fenetrePrincipale.background.resize((self.fenetrePrincipale.winfo_width(), self.fenetrePrincipale.winfo_height()))
        self.pixi = ImageTk.PhotoImage(self.fenetrePrincipale.background)
        self.canvas.configure(width=self.fenetrePrincipale.winfo_width(), height=self.fenetrePrincipale.winfo_height())
        self.canvas.create_image(0, 0, anchor=NW, image=self.pixi)

Here is the principle, after defines a timer and a function to be recalled at the end, after_cancel cleans the timer so each iteration of the function cleans the timer and starts it, at the last iteration of resize the timer remains triggered. for more information on cancel and timer with after: after detailled

Elongation answered 16/10, 2021 at 12:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.