Home > Blockchain >  tkinter frame inside of canvas won't expand when using scrollbar
tkinter frame inside of canvas won't expand when using scrollbar

Time:01-24

When trying to use the ttk.Scrollbar feature, my frame won't expand to fill the canvas even with expand=True. I have found similar issues on other posts however the solutions they used aren't fixing my problem.

Here is a simplified version of my code to show the issue. I essentially want the pink area to fill the entire canvas.

import tkinter as tk
from tkinter import ttk


class MyView(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Any help is appreciated :)")
        self.geometry("600x300")

        self.container = OuterFrame(self)
        self.container.pack(expand=1, fill="both")



class OuterFrame(tk.Frame):
    def __init__(self, container):
        super().__init__(container)

        #CONTAINERS
        self.canvas = tk.Canvas(self)
        self.canvas.pack(side="left", expand=1, fill="both")

        self.scrollbar = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
        self.scrollbar.pack(side="right", fill="y")

        self.canvas.config(yscrollcommand=self.scrollbar.set)

        self.canvas_id = tk.Frame(self.canvas)
        self.canvas_id.bind("<Configure>", lambda e: self.canvas.config(scrollregion=self.canvas.bbox("all")))
        self.canvas.create_window((0,0), window=self.canvas_id, anchor="nw")
        
        self.container = InnerFrame(self.canvas_id)
        self.container.pack(expand=1, fill="both")


        
class InnerFrame(tk.Frame):
    def __init__(self, container):
        super().__init__(container)
        self.config(bg="pink")

        self.titles = ["test1", "test2", "test3", "test4", "test5", "test6"]

        self.label = tk.Label(self, text="Objectives")
        self.label.pack(pady=5)

        self.checkvars = {} #for the ckeckbuttons
        self.frames = {}

        for title in self.titles:
            self.checkvars[title] = tk.IntVar()
            self.frames[title] = InnerFrameObjective(self, title, self.checkvars[title])
            self.frames[title].pack(expand=1, fill="x", pady=(0,15))
            
        
class InnerFrameObjective(tk.Frame):
    def __init__(self, container, string, checkvar):
        super().__init__(container)
        self.configure(pady=4, padx=30, bg="pink")
        self.title = string

        self.Framelabels = InnerFrameObjectiveLabels(self)
        self.Framelabels.pack(side="left", expand=1, fill="both")

        self.checkbutton = tk.Checkbutton(self, variable=checkvar, onvalue=1, offvalue=0, bg="blue")
        self.checkbutton.pack(side="right", expand=1, fill="both")


class InnerFrameObjectiveLabels(tk.Frame):
    def __init__(self, container):
        super().__init__(container)
        self.parent = container
        self.config(bg="blue")

        self.title = self.parent.title
        self.due_date = None

        self.label = tk.Label(self, text=self.title, bg="blue")
        self.label.pack()

        self.label2 = tk.Label(self, text="Due Date:", bg="blue")
        self.label2.pack(anchor="w")


a = MyView()

CodePudding user response:

There are a few things to note here:

  1. The self.canvas_id will only grow as much as the space needed by its children, unless told not to (with pack_propagate(0) and need to give in the dimensions)

  2. You are manually setting dimension on the main window, which in turn will make the self.canvas grow to fill that dimension since you are using pack(side="left", expand=1, fill="both") on self.canvas.

  3. my frame won't expand to fill the canvas even with expand=True

    Your frame self.container will not expand the entire size of self.canvas as it can only grow as much as its parent (self.canvas_id), which also does not grow as much as the canvas unless told to do so. Also since self.canvas_id is a window inside the canvas, you cannot make it grow with pack like you expect, you will have to fire up an event each time the canvas changes size and change the frame size to match the size of the canvas.

  4. Now another problem arises when you use pack_propagate(0), which makes it a problem to get the height of self.container to set for the frame to outgrow the current size of canvas (only then the scrolling will work properly). To tackle this, rather than using pack_propagate(0) and changing the size of self.canvas_id directly, we will configure its size on self.canvas instead.

self.canvas.create_window((0,0), window=self.canvas_id, anchor="nw", tags='window') # Add a tag
self.canvas.bind('<Configure>', lambda e: self.canvas.itemconfig('window', width=e.width)) # Use that tag to identify that window and change the size

The explanation kind of assumes you know nothing, so it goes in some concepts that may seem hard to understand.

  • Related