Ernie Peters Ernie Peters - 1 year ago 49
Python Question

Frames not stacking on top of each other in tkinter

I have a problem stacking 'Pages' on top of each other in tkinter.

I have a main

Frame
that contains two sub frames which both contain different information. The first sub frame contains a
Listbox
and a couple buttons and is packed to the left in the main Frame. The 2nd frame is supposed to conain different 'Pages' (two for now) and have them fill up the entire frame.
My issue is that both 'Pages' are displayed side by side instead of on top of each other.

import tkinter as tk


class Settings(tk.Tk):

def __init__(self, master=None):
tk.Tk.__init__(self, master)
self.focus_force()
self.grab_set()
# set focus to settings window
# Main window title
self.title("Settings")


# set up grid containers
container_main = tk.Frame(self, width=500, height=700)
container_main.pack(side='top', fill='both', expand=True)
container_main.grid_rowconfigure(0, weight=1)
container_main.grid_columnconfigure(0, weight=1)

container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700)
container_listbox.pack(side='left', fill='both', expand=True)
container_listbox.grid_rowconfigure(0, weight=1)
container_listbox.grid_columnconfigure(0, weight=1)

container_settings = tk.Frame(container_main, bg='red', width=300, height=700)
container_settings.pack(side='right', fill='both', expand=True)
container_settings.grid_rowconfigure(0, weight=1)
container_settings.grid_columnconfigure(0, weight=1)

# build settings pages
self.frames = {}

self.frames["Options"] = Options(parent=container_listbox, controller=self)
self.frames["General"] = General(parent=container_settings, controller=self)
self.frames["Future"] = Future(parent=container_settings, controller=self)


if I uncoment these two lines. I get an error saying I cannot use geometry manager grid inside.

# self.frames["General"].grid(row=0, column=0, sticky='nsew')
# self.frames["Future"].grid(row=0, column=0, sticky='nsew')


.

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


class Options(tk.Frame):

def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(parent, text='List Box')
label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1)
button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General'))
button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future'))
button1.grid(row=1, column=0, sticky='ew')
button2.grid(row=2, column=0, sticky='ew')


class General(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(parent, text='General')
label.pack(side='left', fill='both', expand=True, )
print("Hi I'm General")

class Future(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(parent, text='Future')
label.pack(side='left', fill='both', expand=True)
print("Hi I'm Future")

app = Settings()
app.mainloop()


Both 'Pages' are initialized and displayed at the same time which makes sense. I just don't know how to make one rise over the other since
frame.tkraise()
supposed to be doing this but is not. I would also like to be able to do
grid_forget()
on the page or pages that are not on top to avoid potentially accidentally enter values into a hidden entrybox in the future.

EDIT: If I comment out the 'Future' page then the 'General' page will take up the whole frame space so with
grid_forget()
I would yield the same result. I just don't know where I would but
grid_forget()
and then also where would I re-configure or do a
grid()
call?

Answer Source

My issue is that both 'Pages' are displayed side by side instead of on top of each other.

If you use pack() to place a frame on your root window and then use grid() inside of that frame then it will work but if you try to use pack() inside of a frame and then try to use grid() inside of that same frame it will fail.

The same goes for the root window and frames. If pack() a frame in the root window then you cannot use grid() to place anything into that same root window.

The problem with the grid() vs pack() issue was because the location where the class General and class Future was configuring the label widgets was the parent frame where pack() was being used. This prevented the use of grid() in that same parent frame to place the General and Future frames.

To fix this we change:

label = tk.Label(parent, text='General')

and

label = tk.Label(parent, text='Future')

to:

label = tk.Label(self, text='General')

and

label = tk.Label(self, text='Future')

the above was the only fix needed for this to work properly.

import tkinter as tk


class Settings(tk.Tk):

    def __init__(self, master=None):
        tk.Tk.__init__(self, master)
        self.focus_force()
        self.grab_set()
        # set focus to settings window
        # Main window title
        self.title("Settings")

        container_main = tk.Frame(self, width=500, height=700)
        container_main.pack(side='top', fill='both', expand=True)
        container_main.grid_rowconfigure(0, weight=1)
        container_main.grid_columnconfigure(0, weight=1)

        container_listbox = tk.Frame(container_main, bg='blue', width=200, height=700)
        container_listbox.pack(side='left', fill='both', expand=True)
        container_listbox.grid_rowconfigure(0, weight=1)
        container_listbox.grid_columnconfigure(0, weight=1)

        container_settings = tk.Frame(container_main, bg='red', width=300, height=700)
        container_settings.pack(side='right', fill='both', expand=True)
        container_settings.grid_rowconfigure(0, weight=1)
        container_settings.grid_columnconfigure(0, weight=1)

        self.frames = {}

        self.frames["Options"] = Options(parent=container_listbox, controller=self)
        self.frames["General"] = General(parent=container_settings, controller=self)
        self.frames["Future"] = Future(parent=container_settings, controller=self)   


        self.frames["General"].grid(row=0, column=0, sticky='nsew')
        self.frames["Future"].grid(row=0, column=0, sticky='nsew')

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

class Options(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(parent, text='List Box')
        label.grid(row=0, column=0, sticky='nsew', padx=1, pady=1)
        button1 = tk.Button(parent, text='General', command=lambda: controller.show_frame('General'))
        button2 = tk.Button(parent, text='Future', command=lambda: controller.show_frame('Future'))
        button1.grid(row=1, column=0, sticky='ew')
        button2.grid(row=2, column=0, sticky='ew')


class General(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text='General')
        label.pack(side='left', fill='both', expand=True)
        print("Hi I'm General")

class Future(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text='Future')
        label.pack(side='left', fill='both', expand=True)
        print("Hi I'm Future")

app = Settings()
app.mainloop()
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download