pythontkintertkinter-layout

Tkinter nested frames + grid manager overlapping and uneven spacing


When trying to create nested frames with grid layouts I get overlapping and uneven spacing.

The expected results is the TitleFrame is above the Boardframe and the squares on the BoardFrame are evenly spaced. (quick mockup done here in paint)

Expected result

The actual result is the board covers the title frame and the first column is spaced the same as the title.

Actual result

from tkinter import *

class Square(Button):
    def __init__(self, rank, file, bg="white", startingPiece = None) -> None:
        Button.__init__(self, master=None, text="T", bg=bg, borderwidth=2)
        self.Piece = startingPiece or None
        self.IsSelected = False
        self.rank = rank
        self.file = file

class Window(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.master = master

        self.CreateGrid()

    def CreateGrid(self):
    
        self.TitleFrame = Frame()
        self.BoardFrame = Frame()
        self.TitleFrame.grid(row=0,column=0, sticky='news')
        self.BoardFrame.grid(row=1,column=0, sticky='news')

        self.TitleFrame.Title = Label(text="Chess Bot Viewer")
        self.TitleFrame.Title.grid(row=0,column=0,sticky='news')

    
        self.BoardFrame.Squares = [[0 for x in range(8)] for y in range(8)] 
        doFillBG = True
        for x in range(8):
            doFillBG = not doFillBG
            for y in range(8):
                if doFillBG: bg = 'gray' 
                else: bg = 'white'
                self.BoardFrame.Squares[x][y] = Square(rank=x,file=y,bg=bg)
                self.BoardFrame.Squares[x][y].grid(row=x,column=y,sticky='news')
                print(self.BoardFrame.Squares[x][y].rank, self.BoardFrame.Squares[x][y].file)
                doFillBG = not doFillBG
root = Tk()
app = Window(root)
root.wm_title("Chess")
root.geometry("400x400")
root.mainloop()

The CreateGrid method is part of a subclass of Frame, and there are two member frames created in the method.


Solution

  • Since you didn't specify the parent/master for the title label and instances of Square, they will be created as children of root window. So the title label is put at row 0 and column 0 in the root window which causes the wider width of column 0 of the table of squares.

    You need to specify the parent for those widgets as below:

    from tkinter import *
    
    class Square(Button):
        # added master argument
        def __init__(self, master, rank, file, bg="white", startingPiece=None) -> None:
            Button.__init__(self, master=master, text="T", bg=bg, borderwidth=2)
            self.Piece = startingPiece
            self.IsSelected = False
            self.rank = rank
            self.file = file
    
    class Window(Frame):
        def __init__(self, master=None):
            Frame.__init__(self, master)
            #self.master = master  # it is implicitly set
    
            self.CreateGrid()
    
        def CreateGrid(self):
    
            self.TitleFrame = Frame(self) # passed self as the parent
            self.BoardFrame = Frame(self) # passed self as the parent
            self.TitleFrame.grid(row=0, column=0, sticky='news')
            self.BoardFrame.grid(row=1, column=0, sticky='news')
    
            # passed self.TitleFrame as the parent
            self.TitleFrame.Title = Label(self.TitleFrame, text="Chess Bot Viewer")
            self.TitleFrame.Title.grid(row=0, column=0,sticky='news')
    
            self.BoardFrame.Squares = [[0 for x in range(8)] for y in range(8)]
            doFillBG = True
            for x in range(8):
                doFillBG = not doFillBG
                for y in range(8):
                    if doFillBG: bg = 'gray'
                    else: bg = 'white'
                    # passed self.BoardFrame as the parent
                    self.BoardFrame.Squares[x][y] = Square(self.BoardFrame, rank=x, file=y, bg=bg)
                    self.BoardFrame.Squares[x][y].grid(row=x, column=y, sticky='news')
                    print(self.BoardFrame.Squares[x][y].rank, self.BoardFrame.Squares[x][y].file)
                    doFillBG = not doFillBG
    
    root = Tk()
    app = Window(root)
    app.pack() # show the frame using pack()
    root.wm_title("Chess")
    root.geometry("400x400")
    root.mainloop()
    

    Result:

    enter image description here