Skip to content Skip to sidebar Skip to footer

Tkinter Window In Canvas Doesn't Fill Its Parent In Height

I would like to have my scrollbar in the bottom of the frame and my text widgets filling the whole frame above the scrollbar. I found some solution about the width configuration he

Solution 1:

Step 1: Remove space above and below the scrollbar

The expand option determines how tkinter handles unallocated space. Extra space will be evenly allocated to all widgets where the value is 1 or True. Because it's set to 1 for the scrollbar, it is given some of the extra space, causing the padding above and below the widget.

What you want instead is for all of the space to be allocated only to the canvas. Do this by setting expand to zero on the scrollbar:

myscrollbar.pack(fill=X, expand=0)

Step 2: call a function when the canvas changes size

The next problem is that you want the inner frame to grow when the canvas changes size, so you need to bind to the <Configure> event of the canvas.

defOnCanvasConfigure(self, event):
    <code to set the size of the inner frame>
...
self.canvas.bind("<Configure>", self.OnCanvasConfigure)

Step 3: let the canvas control the size of the inner frame

You can't just change the size of the inner frame in OnCanvasConfigure, because the default behavior of a frame is to shrink to fit its contents. In this case you want the contents to expand to fit the frame rather than the frame shrink to fit the contents.

There are a couple ways you can fix this. You can turn geometry propagation off for the inner frame, which will prevent the inner widgets from changing the size of the frame. Or, you can let the canvas force the size of the frame.

The second solution is the easiest. All we have to do is use the height of the canvas for the frame height, and the sum of the widths of the inner text widgets for the frame width.

defOnCanvasConfigure(self, event):
    width = 0for child inself.sensorsStatsFrame.grid_slaves():
        width += child.winfo_reqwidth()

    self.canvas.itemconfigure(self.canvas_frame, width=width, height=event.height)

Step 4: fix the scrollbar

There's still one more problem to solve. If you resize the window you'll notice that tkinter will chop off the scrollbar if the window gets too small. You can solve this by removing the ability to resize the window but your users will hate that.

A better solution is to cause the text widgets to shrink before the scrollbar is chopped off. You control this by the order in which you call pack.

When there isn't enough room to fit all of the widgets, tkinter will start reducing the size of widgets, starting with the last widget added to the window. In your code the scrollbar is the last widget, but if instead you make it the canvas, the scrollbar will remain untouched and the canvas will shrink instead (which in turn causes the frame to shrink, which causes the text widgets to shrink).

myscrollbar.pack(side="bottom", fill=X, expand=0)
self.canvas.pack(fill=BOTH, expand=1)

Solution 2:

Changing pack layout to grid layout for self.canvas and myscrollbar makes it work.

from tkinter import *
from tkinter import ttk

classMainView(Frame):

    defFrameHeight(self, event):
        canvas_height = event.height
        self.canvas.itemconfig(self.canvas_frame, height = canvas_height)

    defOnFrameConfigure(self, event):
        self.canvas.config(scrollregion=self.canvas.bbox("all"))

    def__init__(self, *args, **kwargs):
        Frame.__init__(self, *args, **kwargs)
        self.grid_columnconfigure(0, weight=1)
        self.grid_rowconfigure(0, weight=1)

        sensorsFrame = Frame(self)
        sensorsFrame.grid(row=0, sticky="nsew")
        sensorsFrame.grid_rowconfigure(0, weight=1)
        sensorsFrame.grid_columnconfigure(0, weight=1)


        self.canvas = Canvas(sensorsFrame, bg="blue")
        self.sensorsStatsFrame = Frame(self.canvas)
        self.canvas.grid_rowconfigure(0, weight=1)
        self.sensorsStatsFrame.grid_rowconfigure(0, weight=1)

        myscrollbar = Scrollbar(sensorsFrame,orient=HORIZONTAL,command=self.canvas.xview)
        self.canvas.configure(xscrollcommand=myscrollbar.set)
        self.canvas.grid(row=0, sticky="nsew")
        myscrollbar.grid(row=1, sticky="nsew")

        test0 = Text(self.sensorsStatsFrame, state=DISABLED, bg="red")
        test1 = Text(self.sensorsStatsFrame, width=150)
        test0.grid(column=0, row=0, sticky="nsew")
        test1.grid(column=1, row=0, sticky="nsew")

        self.canvas_frame = self.canvas.create_window((0,0),window=self.sensorsStatsFrame,anchor='nw')
        self.sensorsStatsFrame.bind("<Configure>", self.OnFrameConfigure)
        self.canvas.bind('<Configure>', self.FrameHeight)

if __name__ == "__main__":
    root = Tk()
    main = MainView(root)
    main.pack(fill="both", expand=1)
    root.wm_geometry("1100x500")
    root.wm_title("MongoDB Timed Sample Generator")
    root.mainloop()

Post a Comment for "Tkinter Window In Canvas Doesn't Fill Its Parent In Height"