Search code examples
pythonpython-3.xgraphicstkintergrid-layout

How can I evenly divide space between two frames using the grid geometry manager in tkinter?


I have a tkinter application I'm trying to create. I've deiced to use the grid geometry manger instead of pack. It has been working well so far, but I've come across a strange problem.

The basic layout I want in my application is to have a toolbar frame on the top side of the window, which will expand to fill the window horizontally, and take up 1/5 of the screen space in terms of height. Another frame will fill the reaming 4/5 screen space(again in terms of height), and will also fill the screen horizontally. Another third frame will be under the second, and I will use frame.tkraise() to switch between the two.

Here is a minimal example of what I'm trying to accomplish:

import tkinter as tk


class Toolbar(tk.Frame):
    def __init__(self, parent, root, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.root = root

        self.lbl = tk.Label(self, text='Toolbar')
        self.lbl.pack()


class PageOne(tk.Frame):
    def __init__(self, parent, root, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.root = root

        self.lbl = tk.Label(self, text='PageOne')
        self.lbl.pack()

        self.btn = tk.Button(self, text='MainPage', command=lambda: self.root.show_frame(MainPage))
        self.btn.pack()


class MainPage(tk.Frame):
    def __init__(self, parent, root, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.root = root

        self.lbl = tk.Label(self, text='MainPage')
        self.lbl.pack()

        self.btn = tk.Button(self, text='PageOne', command=lambda: self.root.show_frame(PageOne))
        self.btn.pack()


class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._setup_window()
        self.frames = {}

        container = tk.Frame(self, bg='yellow')
        container.pack(side='top', fill='both', expand=True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        container.grid_rowconfigure(1, weight=3)

        self.toolbar = Toolbar(container, self, bg='red')
        self.toolbar.grid(row=0, column=0, sticky='new')

        for page in (MainPage, PageOne):
            frame = page(container, self, bg='blue')
            self.frames[page] = frame
            frame.grid(row=1, column=0, sticky='nsew')

        self.show_frame(MainPage)

    def _setup_window(self):
        self.geometry('420x280')
        self.title('backup')

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()


if __name__ == '__main__':
    app = App()
    app.mainloop()

The code above displays the following frame:

enter image description here

The yellow frame is the window which holds the tool bar and two other frames. The red frame is the tool bar. And the blue frames are the two main windows, which can be toggled between using the "MainPage" and "PageOne" buttons respectively.

As you can probably tell, my problem is that the tool bar frame(the red part)is not fully expanding to fill the reaming yellow space. No matter how much space I allocate for the tool bar frame, it does not expand.

My understanding was that if I put the tool bar frame in the zeroth column, and the two main windows in the first, and I did container.grid_rowconfigure(0, weight=1) and container.grid_rowconfigure(1, weight=4) that would meet my needs. I made this assumption based on this excerpt from here:

To make a column or row stretchable, use this option and supply a value that gives the relative weight of this column or row when distributing the extra space. For example, if a widget w contains a grid layout, these lines will distribute three-fourths of the extra space to the first column and one-fourth to the second column:

   w.columnconfigure(0, weight=3)
   w.columnconfigure(1, weight=1)

If this option is not used, the column or row will not stretch.

Was I wrong to assume this? Is there something I'm missing? How can I make my tool bar frame expand to fill the extra space?


Solution

  • You just need to tell your toolbar to stick to the south side of the available space as well:

    self.toolbar.grid(row=0, column=0, sticky='nesw')