Home > front end >  Using tkinter and grid to position buttons in an absolute manner
Using tkinter and grid to position buttons in an absolute manner

Time:04-01

I am new to using tkinter and am struggling to get my buttons to render at the very bottom of the screen, evenly spaced out, filling the entire bottom row.

I have been using grid() to try to do this but no luck. I want these three buttons to render without impacting other components of the page(such as the text at the top). I am trying to accomplish a window that has three buttons, each button rendering a different page that you can interact with.

Here is my full code below, I appreciate any insight at all more than you know.

from cgitb import text
import tkinter as tk
from tkinter import ttk
  
LARGEFONT =("Verdana", 35)
  
class tkinterApp(tk.Tk):
     
    # __init__ function for class tkinterApp
    def __init__(self, *args, **kwargs):
         
        # __init__ function for class Tk
        tk.Tk.__init__(self, *args, **kwargs)
         
        # creating a container
        container = tk.Frame(self) 
        container.grid(row=0, column=0)
  
        container.grid_rowconfigure(0)
        container.grid_columnconfigure(0)
  
        # initializing frames to an empty array
        self.frames = {} 
  
        # iterating through a tuple consisting
        # of the different page layouts
        for F in (StartPage, Page1, Page2):
  
            frame = F(container, self)
  
            # initializing frame of that object from
            # startpage, page1, page2 respectively with
            # for loop
            self.frames[F] = frame
  
            frame.grid(row = 0, column = 0, sticky ="nsew")
  
        self.show_frame(StartPage)
  
    # to display the current frame passed as
    # parameter
    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
  
class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
         
        # label of frame Layout 2
        welcome = ttk.Label(self, text ="Welcome!", font = LARGEFONT)
        welcome.grid(row = 0, column = 2, )
  
        button1 = ttk.Button(self, text ="Page 1", command = lambda : controller.show_frame(Page1))
        button1.grid(row = 9, column = 1,  padx = 10, pady = 10)

        button2 = ttk.Button(self, text ="Page 2", command = lambda : controller.show_frame(Page2))
        button2.grid(row = 9, column = 2, padx = 10, pady = 10)

# second window frame page1
class Page1(tk.Frame):
     
    def __init__(self, parent, controller):
         
        tk.Frame.__init__(self, parent)
        label = ttk.Label(self, text ="Page 1", font = LARGEFONT)
        label.grid(row = 0, column = 2, )
        button1 = ttk.Button(self, text ="StartPage", command = lambda : controller.show_frame(StartPage))
        button1.grid(row = 9, column = 1,  padx = 10, pady = 10)
  
        button2 = ttk.Button(self, text ="Page 2", command = lambda : controller.show_frame(Page2))
        button2.grid(row = 9, column = 2, padx = 10, pady = 10)
  
# third window frame page2
class Page2(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = ttk.Label(self, text ="Page 3", font = LARGEFONT)
        label.grid(row = 0, column = 2, )
  
        button1 = ttk.Button(self, text ="Page 1", command = lambda : controller.show_frame(Page1))
        button1.grid(row = 9, column = 1,  padx = 10, pady = 10)
  
        button2 = ttk.Button(self, text ="Startpage", command = lambda : controller.show_frame(StartPage))
        button2.grid(row = 9, column = 2, padx = 10, pady = 10)
  
# Driver Code
app = tkinterApp()
app.geometry("800x425")
app.mainloop()


CodePudding user response:

I made the following changes to your code to get it working:

Gridded the root window so that container would occupy the entire space in the window.

#Gridding the tkinter window
self.grid_rowconfigure(0, weight = 1)
self.grid_columnconfigure(0, weight = 1)

Made container expand in all directions using sticky = 'nsew'.

container.grid(row=0, column=0, sticky = 'nsew')

Modified __init__ and added tkraise to all the Page classes.

I will use the below block of code for explanation purposes.

class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        
        # label of frame Layout 2
        welcome = ttk.Label(self, text ="Welcome!", font = LARGEFONT)
        welcome.grid(row = 0, column = 2)
        
        #Creating a frame exclusively for the buttons
        self.frame_buttons = tk.Frame(parent)
        self.frame_buttons.grid(row = 1, column = 0, columnspan = 3)
        self.frame_buttons.grid_remove()
        
        #Gridding self.frame_buttons
        self.frame_buttons.grid_columnconfigure((0,1), weight = 1)
        self.frame_buttons.grid_rowconfigure(0, weight = 1)
        
        button1 = ttk.Button(self.frame_buttons, text ="Page 1", command = lambda : controller.show_frame(Page1))
        button1.grid(row = 0, column = 0,  padx = 10, pady = 10)

        button2 = ttk.Button(self.frame_buttons, text ="Page 2", command = lambda : controller.show_frame(Page2))
        button2.grid(row = 0, column = 1, padx = 10, pady = 10)
        
    def tkraise(self):
        self.frame_buttons.grid()
        tk.Frame.tkraise(self)

In the __init__ method, self.frame_buttons is created and all the buttons are added to it. It is displayed using .grid and then immediately removed with .grid_remove(). The advantage of using .grid_remove() is that the grid options are remembered and we can directly use .grid() to display the frame in future.

In the tkraise method, the frame is displayed and tk.Frame.tkraise(self) is used to call the in-built method.

enter image description here

Working Code:

from cgitb import text
import tkinter as tk
from tkinter import ttk
  
LARGEFONT =("Verdana", 35)
  
class tkinterApp(tk.Tk):
     
    # __init__ function for class tkinterApp
    def __init__(self, *args, **kwargs):
         
        # __init__ function for class Tk
        tk.Tk.__init__(self, *args, **kwargs)
        
        #Gridding the tkinter window
        self.grid_rowconfigure(0, weight = 1)
        self.grid_columnconfigure(0, weight = 1)
         
        # creating a container
        container = tk.Frame(self) 
        container.grid(row=0, column=0, sticky = 'nsew')
  
        container.grid_rowconfigure(0, weight = 1)
        container.grid_columnconfigure(0, weight = 1)
  
        # initializing frames to an empty array
        self.frames = {} 
  
        # iterating through a tuple consisting
        # of the different page layouts
        for F in (StartPage, Page1, Page2):
  
            frame = F(container, self)
  
            # initializing frame of that object from
            # startpage, page1, page2 respectively with
            # for loop
            self.frames[F] = frame
  
            frame.grid(row = 0, column = 0, sticky ="nsew")
  
        self.show_frame(StartPage)
  
    # to display the current frame passed as
    # parameter
    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()
  
class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        
        # label of frame Layout 2
        welcome = ttk.Label(self, text ="Welcome!", font = LARGEFONT)
        welcome.grid(row = 0, column = 2)
        
        #Creating a frame exclusively for the buttons
        self.frame_buttons = tk.Frame(parent)
        self.frame_buttons.grid(row = 1, column = 0, columnspan = 3)
        self.frame_buttons.grid_remove()
        
        #Gridding self.frame_buttons
        self.frame_buttons.grid_columnconfigure((0,1), weight = 1)
        self.frame_buttons.grid_rowconfigure(0, weight = 1)
        
        button1 = ttk.Button(self.frame_buttons, text ="Page 1", command = lambda : controller.show_frame(Page1))
        button1.grid(row = 0, column = 0,  padx = 10, pady = 10)

        button2 = ttk.Button(self.frame_buttons, text ="Page 2", command = lambda : controller.show_frame(Page2))
        button2.grid(row = 0, column = 1, padx = 10, pady = 10)
        
    def tkraise(self):
        self.frame_buttons.grid()
        tk.Frame.tkraise(self)

# second window frame page1
class Page1(tk.Frame):
     
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = ttk.Label(self, text ="Page 1", font = LARGEFONT)
        label.grid(row = 0, column = 2)
        
        #Creating a frame exclusively for the buttons
        self.frame_buttons = tk.Frame(parent)
        self.frame_buttons.grid(row = 1, column = 0, columnspan = 3)
        self.frame_buttons.grid_remove()
        
        #Gridding self.frame_buttons
        self.frame_buttons.grid_columnconfigure((0,1), weight = 1)
        self.frame_buttons.grid_rowconfigure(0, weight = 1)
        
        button1 = ttk.Button(self.frame_buttons, text ="StartPage", command = lambda : controller.show_frame(StartPage))
        button1.grid(row = 0, column = 0,  padx = 10, pady = 10)

        button2 = ttk.Button(self.frame_buttons, text ="Page 2", command = lambda : controller.show_frame(Page2))
        button2.grid(row = 0, column = 1, padx = 10, pady = 10)
        
    def tkraise(self):
        self.frame_buttons.grid()
        tk.Frame.tkraise(self)
  
# third window frame page2
class Page2(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = ttk.Label(self, text ="Page 2", font = LARGEFONT)
        label.grid(row = 0, column = 2)
        
        #Creating a frame exclusively for the buttons
        self.frame_buttons = tk.Frame(parent)
        self.frame_buttons.grid(row = 1, column = 0, columnspan = 3)
        self.frame_buttons.grid_remove()
        
        #Gridding self.frame_buttons
        self.frame_buttons.grid_columnconfigure((0,1), weight = 1)
        self.frame_buttons.grid_rowconfigure(0, weight = 1)
        
        button1 = ttk.Button(self.frame_buttons, text ="Page 1", command = lambda : controller.show_frame(Page1))
        button1.grid(row = 0, column = 0,  padx = 10, pady = 10)

        button2 = ttk.Button(self.frame_buttons, text ="Startpage", command = lambda : controller.show_frame(StartPage))
        button2.grid(row = 0, column = 1, padx = 10, pady = 10)
        
    def tkraise(self):
        self.frame_buttons.grid()
        tk.Frame.tkraise(self)
        
# Driver Code
app = tkinterApp()
app.geometry("800x425")
app.mainloop()

enter image description here

CodePudding user response:

You are using grid along with pack. You should never mix these two layout managers as it results in unknown buggy behiviour. Maybe your code will work after culling that pack call.

  • Related