Tkinter frame sizes when using class and raise() Tkinter frame sizes when using class and raise() tkinter tkinter

Tkinter frame sizes when using class and raise()


If you want to use the grid geometry manager only, you'll face the problem that the standard behavior for a frame with widgets inside of it is to resize itself to the smaller size possible, yet enough to contain its children (labels, buttons, entries, etc).

In such case you will not know in advance the dimensions of the controlled frames and the use of tkraise() will not work, since one controlled frame might be bigger than the other controlled frame and will still be visible when the smaller frame is raised.

I could find 2 solutions to this:

One solution is to force all controlled frames to have a fixed size and not resize themselves when you add widgets to them with the grid manager.tkraise() can then be used to selectively show a controlled frame (since all controlled frames have the same fixed dimensions, bringing one to the front will hide the others)..grid_propagate()is needed here

w.grid_propagate()Normally, all widgets propagate their dimensions, meaning that they adjust to fit the contents.However, sometimes you want to force a widget to be a certain size, regardless of the size of itscontents. To do this, call w.grid_propagate(0) where w is the widget whose size you want toforce.

The down side is that it will be harder to organize the children widgets in a fixed size frame in order to get the appearance you deisre. You might have to carefully play with padx and pady(By the way, you can use padx=(left, right) to specify different padding for the lef hand side and the right hand side of a widget. The same applies to pady, as pady=(top, bottom)

The code below uses this solution of fixed size frames (.grid_propagate(0)) and tkraise()Note: I'm usig Python 3.9.1

import tkinter as tkfrom tkinter import *from tkinter import font as tkfont  # python 3from tkinter.ttk import *class InterimMain(tk.Tk):  def __init__(self, *args, **kwargs):    tk.Tk.__init__(self, *args, **kwargs)    # Requested dimension of main window    windowWidth = 800    windowHeight = 600    # Info for centering the window in the screen    offsetLeft = int( (self.winfo_screenwidth() - windowWidth) / 2 )    offsetTop  = int( (self.winfo_screenheight() - windowHeight) / 2 )    # Positions the window in the center of the screen    self.geometry('{}x{}+{}+{}'.format(windowWidth, windowHeight, offsetLeft, offsetTop))    # set the window title    self.title('Using the grid geometry manager, propagate(0) and tkraise() to show/hide frames')     # set a minimun window size, in order to preserve the container frame    self.minsize(600, 400)    # create a Frame widget in the main window, requested dimensions 600 x 400    container = tk.Frame(self, width = 600, height = 400)    # the frame should have a red background    container.config(background='red')    # the frame will go in row 0, column 0 of the grid in the windows    container.grid(row=0, column=0)    # this frame will not auto-size to fit its contents    # in other words, its dimensions will always honor the ones requested for its width and size    container.grid_propagate(0)    # the grid in the root window (self here) is stretchable    self.rowconfigure(0, weight=1)    self.columnconfigure(0, weight=1)                        self.frames = {}        # create the needed frame (WelcomeScreen frame and Login frame)    for F in (WelcomeScreen, Login):      # this is the frame "name"      page_name = F.__name__      # create the frame in the red container frame. This current window is the controller (responsible for showing hidding frames)      frame = F(container, self)      # add the created frame to our list of controlled frames      self.frames[page_name] = frame      # the created frame will gon in row 0, colum 0, of its parent (the red container frame)      frame.grid(row=0, column=0)    # the grid in red container frame should stretch    container.rowconfigure(0, weight=1)    container.columnconfigure(0, weight=1)    # ask the controller (this window) to show the WelcomeScreen frame (and hide all the others in the list of controlled frames)    self.show_frame('WelcomeScreen')      def show_frame(self, page_name):    # since the controlled frames were made to be of the same size,    # raising a frame will make it apeear 'in front' of the others, hiding them    frame = self.frames[page_name]    frame.tkraise()    class WelcomeScreen(tk.Frame):  # this WelcomeScreen frame has a parent and a controller  def __init__(self, parent, controller):        # we initialize the base Frame class with some standard arguments. we request specific width and height    tk.Frame.__init__(self, parent, width = 360, height = 360)        # this frame will not auto-size to fit its contents    # in other words, its dimensions will always honor the ones requested for its width and size    self.grid_propagate(0)    # add a label to this frame (in row 0, column 0)    Label(self,text='WELCOME TO THE INTRIM REPORT',font=('Arial Bold',16)).grid(row=0, pady=20)    # get the image to be used in the next label to be created    render = PhotoImage(file='logo2.png')    # add label with an image to this frame  (in row 1, column 0)    img = Label(self, image=render)    img.image = render    img.grid(row=1, column=0)        # add another label to this frame (in row 2, column 0)    Label(self,text='Press the button to proceed',font=('Arial Bold',10)).grid(row=2, column=0, padx=5, pady=10)    # add an 'proceed to login screen'  button to this frame (in row 3, column 0)    # when this button is pressed, the Login frame should be shown and the WelcomeScreen frame should be hidden    Button = tk.Button(self,text='Sign In',font=('Arial Bold',16),command=lambda: controller.show_frame('Login'))    Button.grid(row=3, column=0, padx=20,pady=20)    # the grid in this frame should stretch    self.rowconfigure(0, weight=1)    self.columnconfigure(0, weight=1)class Login(tk.Frame):  # this Login frame has a parent and a controller  def __init__(self, parent, controller):    # we initialize the base Frame class with some standard arguments. we request specific width and height    tk.Frame.__init__(self, parent, width = 360, height = 360)        # this frame will have a blue background    self.config(background='blue')    # this frame will not auto-size to fit its contents    # in other words, its dimensions will always honor the ones requested for its width and size    self.grid_propagate(0)    # add a label to this frame (in row 0, column 0)    Label(self,text='LOGIN SCREEN',font=('Arial Bold',16)).grid(row=0, column=0, padx=5, pady=5, columnspan=3)    # add a label to this frame (in row 1, column 0)    Label(self,text='Username',font=('Arial',12)).grid(row=1,column=0, padx=5, sticky='w')    # add a label to this frame (in row 2, column 0)    Label(self,text='Password',font=('Arial',12)).grid(row=2,column=0, padx=5, sticky='w')    # add an Entry widget to this frame    user = Entry(self)    # the Entry widget will go in row 1, column 1, will span for 2 columns and will stretch left and right    user.grid(row=1,column=1,padx=5, pady=5,columnspan=2,sticky='we')    # add another Entry widget to this frame    pwd = Entry(self)    # the Entry widget go in row 2, column 1, will span for 2 columns and will stretch left and right    pwd.grid(row=2,column=1,padx=5, pady=5,columnspan=2,sticky='we')        # add a button to this frame    # when this button is pressed, the checkLogin() function should be called with the stated parameters    Button = tk.Button(self,text='LOGIN',command=lambda:checkLogin(str(user.get()),str(pwd.get())))    # the button widget will go in row 3, column 2 and will stick to the rigth side of the grid cell    Button.grid(row=3,column=2,columnspan=3,padx=5, pady=5, sticky='e')        # add a button to this frame    # when this button is pressed, the WelcomeScreen frame should be shown and the Login frame should be hidden    Button = tk.Button(self,text='CANCEL',command=lambda: controller.show_frame('WelcomeScreen'))    # the button widget will go in row 3, column 0 and will stick to the left of the grid cell    Button.grid(row=3,column=0,columnspan=3,padx=5, pady=5,sticky='w')    # the grid in this frame should stretch    self.rowconfigure(0, weight=1)    self.columnconfigure(0, weight=1)if __name__ == '__main__':    app = InterimMain()    app.mainloop()

The second solution is to let the controlled frames resize themselves at will, to fit their contents, and use another way to hide/show the controlled frames:

.grid_remove()This method is like .grid_forget(), but its grid options are remembered, so if you .grid() itagain, it will use the same grid configuration options.

So, .grid_remove() is used on a widget inside of a grid to make the widget invisible. Later, .grid() is used on the widget to make it reappear in the grid.You just call .grid() with no parameters. It's enough to make the widget reappear as it was before .grid_remove() was used.

The code below uses this solution with .grid_remove() and .grid() to hide/show frames.Note: I'm usig Python 3.9.1

import tkinter as tkfrom tkinter import *from tkinter import font as tkfont  # python 3from tkinter.ttk import *class InterimMain(tk.Tk):  def __init__(self, *args, **kwargs):    tk.Tk.__init__(self, *args, **kwargs)    # Requested dimension of main window    windowWidth = 800    windowHeight = 600    # Info for centering the window in the screen    offsetLeft = int( (self.winfo_screenwidth() - windowWidth) / 2 )    offsetTop  = int( (self.winfo_screenheight() - windowHeight) / 2 )    # Positions the window in the center of the screen    self.geometry('{}x{}+{}+{}'.format(windowWidth, windowHeight, offsetLeft, offsetTop))    # set the window title    self.title('Using the grid geometry manager, .grid_remove() and .grid() to hide/show frames')     # create a Frame widget in the main window, requested dimensions 600 x 400    container = tk.Frame(self,width=600, height= 400)    # the frame should have a red background    container.config(background="red")    # the frame will go in row 0, column 0 of the grid in the windows    container.grid(row=0, column=0)    # set a minimun window size, in order to preserve the container frame    self.minsize(windowWidth, windowHeight)        # the grid in the root window (self here) is stretchable    self.rowconfigure(0, weight=1)    self.columnconfigure(0, weight=1)                        #container.place(relx = 0.5,rely=0.5,anchor='c')    #w = WelcomeScreen(controller)    #l =Login(controller)        self.frames = {}        # create the needed frame (WelcomeScreen frame and Login frame)    for F in (WelcomeScreen, Login):      # this is the frame "name"      page_name = F.__name__      # create the frame in the main window      frame = F(self)      # add the created frame to our list of controlled frames      self.frames[page_name] = frame      # the created frame will gon in row 0, colum 0, of its parent (the red container frame)      frame.grid(row=0, column=0)          # ask the controller (this window) to show the WelcomeScreen frame (and hide all the others in the list of controlled frames)    self.show_frame("WelcomeScreen")      def show_frame(self, page_name):    # hide all frames    for aFrame in self.frames:      # this will make the widget invisible, but its grid options will be remembered      self.frames[aFrame].grid_remove()        # show the requested frame    # calling .grid() on a widget that was grid_remove()'d will restore its grid options and make it visible again    self.frames[page_name].grid()class WelcomeScreen(tk.Frame):  # this WelcomeScreen frame has same widget as parent and controller  def __init__(self, controller):    # we initialize the base Frame class with no special arguments    # we do not request width and height for the frame. They would not be honored,    # since the frame will resize itself to fit    # the widgets indside of it, due its grid geometry manager    tk.Frame.__init__(self)      # add a label to this frame (in row 0, column 0)    Label(self,text="WELCOME TO THE INTRIM REPORT",font=("Arial Bold",16)).grid(row=0, column=0,pady=20)    # get the image to be used in the next label to be created    render = PhotoImage(file='logo2.png')        # add label with an image to this frame  (in row 1, column 0)    img = Label(self, image=render)    img.image = render    img.grid(row=1, column=0)        # add another label to this frame (in row 2, column 0)    Label(self,text="Press the button to proceed",font=("Arial Bold",10)).grid(padx=5,row=2,column=0,pady=10)    # add an 'proceed to login screen'  button to this frame (in row 3, column 0)    # when this button is pressed, the Login frame should be shown and the WelcomeScreen frame should be hidden    Button = tk.Button(self,text="Sign In",font=("Arial Bold",16),command=lambda: controller.show_frame("Login"))    Button.grid(row=3,column=0,padx=20,pady=20)class Login(tk.Frame):  # this WelcomeScreen frame has same widget as parent and controller  def __init__(self,controller):    # we initialize the base Frame class with no special arguments    # we do not request width and height for the frame. They would not be honored,    # since the frame will resize itself to fit    # the widgets indside of it, due its grid geometry manager    tk.Frame.__init__(self)        # this frame will have a blue background    self.config(background="blue")    # add a label to this frame (in row 0, column 0)    Label(self,text="LOGIN SCREEN",font=("Arial Bold",16)).grid(row=0,column=0, padx=5, pady=10,columnspan=3)    # add a label to this frame (in row 1, column 0)    Label(self,text="Username",font=("Arial",12)).grid(row=1,column=0, padx=5, sticky='w')    # add a label to this frame (in row 2, column 0)    Label(self,text="Password",font=("Arial",12)).grid(row=2,column=0, padx=5, sticky='w')    # add an Entry widget to this frame    user = Entry(self)    # the Entry widget will go in row 1, column 1, will span for 2 columns and will stretch left and right    user.grid(row=1,column=1, padx=5, pady=5,columnspan=2,sticky='we')    # add another Entry widget to this frame    pwd = Entry(self)    # the Entry widget go in row 2, column 1, will span for 2 columns and will stretch left and right    pwd.grid(row=2,column=1, padx=5, pady=5,columnspan=2,sticky='we')        # add a button to this frame    # when this button is pressed, the checkLogin() function should be called with the stated parameters    Button = tk.Button(self,text="LOGIN",command=lambda:checkLogin(str(user.get()),str(pwd.get())))    # the button widget will go in row 3, column 2 and will stick to the rigth side of the grid cell    Button.grid(row=3,column=2,columnspan=3, padx=5, pady=5, sticky='e')    # add a button to this frame    # when this button is pressed, the WelcomeScreen frame should be shown and the Login frame should be hidden    Button = tk.Button(self,text="CANCEL",command=lambda: controller.show_frame('WelcomeScreen'))    # the button widget will go in row 3, column 0 and will stick to the left of the grid cell    Button.grid(row=3,column=0, padx=5, pady=5, sticky='w') if __name__ == "__main__":    app = InterimMain()    app.mainloop()