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()
        self.frame_buttons.tkraise()
        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, lifted to the top and then tk.Frame.tkraise(self) is used to call the in-built method.

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()
        self.frame_buttons.tkraise()
        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()
        self.frame_buttons.tkraise()
        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()
        self.frame_buttons.tkraise()
        tk.Frame.tkraise(self)
        
# Driver Code
app = tkinterApp()
app.geometry("800x425")
app.mainloop()

Answer from Sriram Srinivasan on Stack Overflow
๐ŸŒ
ActiveState
activestate.com โ€บ home โ€บ resources โ€บ quick read โ€บ how to position buttons in tkinter
How to Position Buttons in Tkinter - with Grid, Place or Pack - ActiveState
January 24, 2024 - The grid manager locates widgets in a two dimensional grid using row and column relative coordinates. The place manager places widgets in a two dimensional grid using x and y absolute coordinates.
๐ŸŒ
Python.org
discuss.python.org โ€บ python help
Button Placement Using '.grid' via tkinter - Python Help - Discussions on Python.org
October 23, 2023 - Hello, I am getting familiar with the tkinter package/library. In my test program, I would like to test the three different placement options using either pack, place, or grid. Using either the place or the pack optโ€ฆ
๐ŸŒ
ActiveState
activestate.com โ€บ home โ€บ resources โ€บ quick read โ€บ how to position buttons in tkinter with grid
How To Position Buttons In Tkinter With Grid (Demo and Codes) - ActiveState
January 24, 2024 - In this example, grid() is used to position buttons based on row and column coordinates on a grid in a frame: This code will draw four buttons, and the placement of each button is specified by left, top, right, and bottom sections of a frame ...
Top answer
1 of 2
1

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()
        self.frame_buttons.tkraise()
        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, lifted to the top and then tk.Frame.tkraise(self) is used to call the in-built method.

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()
        self.frame_buttons.tkraise()
        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()
        self.frame_buttons.tkraise()
        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()
        self.frame_buttons.tkraise()
        tk.Frame.tkraise(self)
        
# Driver Code
app = tkinterApp()
app.geometry("800x425")
app.mainloop()

2 of 2
-1

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.

๐ŸŒ
YouTube
youtube.com โ€บ activestate
How to position buttons in Tkinter with Grid - YouTube
How to Position Buttons in Tkinter With GridPack() has two options you can use: row and columnThe row option aligns buttons horizontally.The column option al...
Published ย  September 10, 2020
Views ย  18K
๐ŸŒ
Python Tutorial
pythontutorial.net โ€บ home โ€บ tkinter tutorial โ€บ tkinter grid
Tkinter Grid Geometry Manager - Python Tutorial
April 4, 2025 - By default, when a cell is larger than the widget it contains, the grid geometry manager places the widget at the center of the cell horizontally and vertically. To change this default behavior, you can use the sticky option.
Top answer
1 of 3
51

Causing a widget to appear requires that you position it using with what Tkinter calls "geometry managers". The three managers are grid, pack and place. Each has strengths and weaknesses. These three managers are implemented as methods on all widgets.

grid, as its name implies, is perfect for laying widgets in a grid. You can specify rows and columns, row and column spans, padding, etc.

Example:

b = Button(...)
b.grid(row=2, column=3, columnspan=2)

pack uses a box metaphor, letting you "pack" widgets along one of the sides of a container. pack is extremely good at all-vertical or all-horizontal layouts. Toolbars, for example, where widgets are aligned in a horizontal line, are a good place to use pack.

Example:

b = Button(...)
b.pack(side="top", fill='both', expand=True, padx=4, pady=4)`

place is the least used geometry manager. With place you specify the exact x/y location and exact width/height for a widget. It has some nice features such as being able to use either absolute or relative coordinates (for example: you can place a widget at 10,10, or at 50% of the widgets width or height).

Unlike grid and pack, using place does not cause the parent widget to expand or collapse to fit all of the widgets that have been placed inside.

Example:

b = Button(...)
b.place(relx=.5, rely=.5, anchor="c")

With those three geometry managers you can do just about any type of layout you can imagine.

2 of 3
16

astynax is right. To follow the example you gave:

MyButton1 = Button(master, text="BUTTON1", width=10, command=callback)
MyButton1.grid(row=0, column=0)

MyButton2 = Button(master, text="BUTTON2", width=10, command=callback)
MyButton2.grid(row=1, column=0)

MyButton3 = Button(master, text="BUTTON3", width=10, command=callback)
MyButton3.grid(row=2, column=0)

Should create 3 row of buttons. Using grid is a lot better than using pack. However, if you use grid on one button and pack on another it will not work and you will get an error.

Find elsewhere
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 44587651 โ€บ button-locationgrid-in-tkinter-python
Button location(grid) in tkinter Python - Stack Overflow
You also need to add a columnspan of the other frames so you can have your buttons placed centered when side by side. A columspan is used to tell the program how many columns that widget is going to be taking up. the same can be done with rows ...
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ python โ€บ how-to-move-a-tkinter-button
How to move a Tkinter button? - GeeksforGeeks
July 23, 2025 - # Importing tkinter module from ... btn.pack(side=RIGHT, padx=15, pady=20) root.mainloop() ... This method is used to grid a button at a relative position....
๐ŸŒ
Python GUIs
pythonguis.com โ€บ tutorials โ€บ getting started with tkinter โ€บ using the grid geometry manager in tkinter
Using the Grid Geometry Manager in Tkinter
October 9, 2022 - We use the grid geometry manager to position each widget in the correct cell. Finally, we add a Submit button, which we place in the sixth row, third column, aligning it to the right with sticky=tk.E. In this tutorial, we learned how to use ...
๐ŸŒ
Stack Overflow
stackoverflow.com โ€บ questions โ€บ 69688124 โ€บ tkinter-grid-system-weird-placement-of-buttons
python - Tkinter grid system weird placement of buttons - Stack Overflow
October 23, 2021 - In tkinter in grid() you can use column=0, columnspan=26 to use all columns for image. The same you can use for word and button and they will be centered.
๐ŸŒ
ActiveState
activestate.com โ€บ home โ€บ resources โ€บ quick read โ€บ how to position buttons in tkinter with place
How To Position Buttons In Tkinter With Place (Demo and Codes) - ActiveState
January 24, 2024 - Tkinterโ€™s place function lets you very easily manipulate the placement of Tkinter button positions and other widgets in a two dimensional grid using x and y absolute coordinates.
๐ŸŒ
TutorialsPoint
tutorialspoint.com โ€บ setting-the-position-on-a-button-in-tkinter-python
Setting the position on a button in Tkinter Python?
April 22, 2021 - Try the following example by moving cursor on different buttons โˆ’ ยท from tkinter import * top = Tk() L1 = Label(top, text="Physics") L1.place(x=10,y=10) E1 = Entry(top, bd =5) E1.place(x=60,y=10) L2=Label(top,text="Maths") L2.place(x=10,y=50) E2=Entry(top,bd=5) E2.place(x=60,y=50) L3=Label(top,text="Total") L3.place(x=10,y=150) E3=Entry(top,bd=5) E3.place(x=60,y=150) B = Button(top, text ="Add") B.place(x=100, y=100) top.geometry("250x250+10+10") top.mainloop()
๐ŸŒ
Python Forum
python-forum.io โ€บ thread-32241.html
tkinter python button position problem
Hello.I 'am beginner with tkinter and I build a simple calculator app and I have a problem with position of 3 buttons (+ , - , =). I have 2 photos to see my problem & understand. 1st photo 2nd photo 2nd photo is the original size of window. So, I ...
๐ŸŒ
Python GUIs
pythonguis.com โ€บ faq โ€บ when to use pack, place or grid in tkinter
Tkinter Pack vs Grid vs Place โ€” Which Layout Manager Should You Use?
September 18, 2019 - Use place when you need precise absolute or relative positioning. Remember: never mix different layout managers within the same parent container -- Tkinter cannot resolve conflicts between pack, grid, and place when used together in a single frame or window.
๐ŸŒ
CustomTkinter
customtkinter.tomschimansky.com โ€บ beginner โ€บ 1. grid system
1. Grid System | CustomTkinter
The grid splits a window or frame into columns and rows, which collapse when they are empty, but adapt to the size of the widgets placed inside them. If you want to center the button in the last example, you would have to give the first column a weight other than zero, so that it does not collapse to the size of the button anymore (use grid_rowconfigure() for rows):
Top answer
1 of 3
33

To center horizontally this should be enough

button.pack(side=TOP)

But to center horizontally and vertically you could use nested frames. Check the following script:

import tkinter as tk

#%% Frames
frameA = tk.Frame(background="#c8c8c8")
frameB = tk.Frame(width=200, height = 200, background="#646464")
# Nested Frame. framebb is created within frameB without width or height
framebb = tk.Frame(frameB, background="#646464")
frameC = tk.Frame(width=100, height = 100, background="bisque")

frameA.pack(side='top', fill=None)
frameB.pack(side='top')
# expand is the key parameter to center the framebb within frameB
framebb.pack(expand=True)
frameC.pack(side='bottom')

#frameA.pack_propagate(False)
frameB.pack_propagate(False)
frameC.pack_propagate(False)

#%% Buttons and Labels
tk.Label(frameA, text = "Text within the frame A").pack()

a = tk.Button(framebb, text = "A").pack()
b = tk.Button(framebb, text = "B").pack()
c = tk.Button(framebb, text = "C").pack()
d = tk.Button(frameC, text = "D").pack()
e = tk.Button(frameC, text = "E").pack()

tk.mainloop()

Another approach could be using the .grid() method

button.grid(row=1,col=0)

the values of row=1,col=0 depend of the position of the other widget in your window

or you can use .place(relx=0.5, rely=0.5, anchor=CENTER)

button.place(relx=0.5, rely=0.5, anchor=CENTER)

Notice that the parameter anchor is referencing the a relative position to the object (in this case button). anchor is not referencing to a position in the window. You could think that the button is a ship that has several anchors so you should choose a coordinate and which anchor you want to fix in that coordinate.

Example using .place():

from tkinter import *  # Use this if use python 3.xx
#from Tkinter import *   # Use this if use python 2.xx
a = Button(text="Center Button")
b = Button(text="Top Left Button")
c = Button(text="Bottom Right Button")

# You can use the strings the referencing the relative position on the button
# strings = n, ne, e, se, s, sw, w, nw, c or center
# Or you can use the constants of tkinter
# N, NE, E, SE, S, SW, W, NW, CENTER
a.place(relx=0.5, rely=0.5, anchor=CENTER)
b.place(relx=0.0, rely=0.0, anchor=NW)
c.place(relx=1.0, rely=1.0, anchor=SE)
mainloop()

2 of 3
1

this is slightly old now, you have to do this:

import tkinter as tk #you can do 'from tkinter import *', any is fine
btn = tk.Button(text = "Centered Button")
btn.place(relx=0.5, rely=0.5, anchor='center')