How can I get elements in Tkinter windows to fill the frame?

I am new to Python GUI development with Tkinter. I am trying to make the app window a certain size and I want the webcam view in the window to fill the size of the main window.

When I set the window size, the webcam does not fill the entire window.

How can I make the elements fill the entire window when I customize the size of the window?

Here is my code:

import tkinter as tk
from PIL import Image, ImageTk
import cv2

class MainWindow():
    def __init__(self, window, cap):
        self.window = window
        self.cap = cap
        self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
        self.interval = 20 # Interval in ms to get the latest frame

        # Create canvas for image
        self.canvas = tk.Canvas(self.window, width=self.width, height=self.height)
        self.canvas.grid(row=0, column=0)
        self.canvas.pack(fill="both", expand=True)

        # Update image on canvas
        self.update_image()

    def update_image(self):
        # Get the latest frame and convert image format
        self.image = cv2.cvtColor(self.cap.read()[1], cv2.COLOR_BGR2RGB) # to RGB
        self.image = Image.fromarray(self.image) # to PIL format
        self.image = ImageTk.PhotoImage(self.image) # to ImageTk format

        # Update image
        self.canvas.create_image(0, 0, anchor=tk.NW, image=self.image)

        # Repeat every 'interval' ms
        self.window.after(self.interval, self.update_image)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry('600x800')
    # root.resizable(width=0, height=0)
    MainWindow(root, cv2.VideoCapture(0))

    root.mainloop()

Thanks.

Answer

All you have to do is resize the image to fit the canvas size. You can either use cv2.resize() or Image.resize() to resize the image.

To get the current canvas size use canvas.winfo_height() and canvas.wifo_width(). Besides that, you should also consider updating the existing image using canvas.itemconfig(tag_id), instead of creating a new one each time.

sample code (I’ll be using a label instead of canvas to display the image):

import tkinter as tk
from PIL import Image, ImageTk
import cv2

class MainWindow():
    def __init__(self, window, cap):
        self.window = window
        self.cap = cap

        self.interval = 20 # Interval in ms to get the latest frame
        # Create canvas for image
        self.vid_lbl = tk.Label(self.window)
        self.vid_lbl.pack(fill="both", expand=True)

        # Update image on canvas
        self.update_image()

    def update_image(self):
        # Get the latest frame and convert image format

        width, height = self.vid_lbl.winfo_width(), self.vid_lbl.winfo_height()

        self.image = cv2.cvtColor(self.cap.read()[1], cv2.COLOR_BGR2RGB) # to RGB
        self.image = cv2.resize(self.image, (width, height), cv2.INTER_AREA)
        self.image = Image.fromarray(self.image) # to PIL format
        #self.image = self.image.resize((width, height))

        self.image = ImageTk.PhotoImage(self.image) # to ImageTk format
        self.vid_lbl.config(image=self.image)

        # Repeat every 'interval' ms
        self.window.after(self.interval, self.update_image)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry('600x800')
    # root.resizable(width=0, height=0)
    MainWindow(root, cv2.VideoCapture(0))

    root.mainloop()