2025, Nov 17 15:00

Build a Tkinter Chessboard: Alternate Colors with Modulo and Update Any Grid Cell via a 2D Button List

Learn to build a Tkinter chessboard UI in Python: alternate colors with modulo, keep a 2D list of button references, and update grid cell text or background.

Designing a chessboard UI in Tkinter looks straightforward until you need to update individual squares. If each cell is created inline and immediately gridded, addressing or recoloring buttons later becomes awkward. Below is a practical approach that alternates colors cleanly and lets you access any square by its row and column to change text or background on demand.

Initial approach and where it gets in the way

The typical first pass creates buttons directly in a grid without keeping references to them. It renders fine but doesn’t leave convenient handles for later updates.

import tkinter as tk
ui = tk.Tk()
ui.geometry("800x500")
ui.minsize(800, 500)
ui.maxsize(800, 500)
boardPane = tk.Frame(ui, width=300, height=300, padx=5, pady=5)
boardPane.pack(side=tk.LEFT)
for r in range(8):
    for c in range(8):
        tk.Button(boardPane, text=f"{r},{c}", width=5, height=3).grid(row=r, column=c)
ui.mainloop()

What’s really happening and why updates fail

When a widget is constructed and immediately placed with grid in the same statement, the reference to that widget is not stored anywhere. Without a reference, you can’t look it up later to change properties like background or text. On top of that, keeping references helps ensure the widgets aren’t garbage collected prematurely. For alternating colors, you also need a consistent rule that maps each coordinate to a color; that’s where modulo arithmetic fits perfectly.

Solution: alternate with modulo and keep a 2D list of buttons

Alternating the checkerboard pattern is a simple parity check. If the sum of row and column is even, use one color; if odd, the other. Storing every Button in a nested list indexed by [row][column] makes updates trivial.

import tkinter as tk
TILE_A = "white"
TILE_B = "green"
app = tk.Tk()
app.geometry("800x500")
app.minsize(800, 500)
app.maxsize(800, 500)
board = tk.Frame(app, width=300, height=300, padx=5, pady=5)
board.pack(side=tk.LEFT)
cells = []  # 2D list of button references
for r in range(8):
    cells.append([])
    for c in range(8):
        shade = TILE_A if (r + c) % 2 else TILE_B
        btn = tk.Button(
            board,
            text=f"{r},{c}",
            width=5,
            height=3,
            bg=shade,
        )
        btn.grid(row=r, column=c)
        cells[r].append(btn)
# Example of updating a specific square by row and column
cells[3][2]["text"] = "It works!"
app.mainloop()

Why modulo 2 gives the checkerboard

Consider a 2x2 grid. If you sum row and column indices, the parity flips as you move horizontally or vertically. Applying % 2 to that sum alternates between 0 and 1 across neighboring squares, which maps directly to two colors.

+---+---+
| 0 | 1 |
+---+---+
| 1 | 0 |
+---+---+

This same rule scales to any board size, producing the classic alternating pattern without conditional cascades or hardcoded coordinates.

Accessing and modifying any square

With the nested list in place, you can reach any button via its indices and update attributes the same way you would in a dict-like interface. For example, cells[3][2]["text"] = "It works!" changes the label at row 3, column 2. The stored references also keep the widgets alive, preventing them from being garbage collected while the UI is running.

About mainloop

The mainloop runs an event loop. It prevents the program from exiting after the last line of code and checks for events such as button presses.

Why this matters

GUI code is much easier to maintain when widgets are addressable. A small structural decision—keeping references in a 2D list—unlocks straightforward updates to color, text, and behavior. The modulo-based coloring is compact, readable, and reliable. Together these practices make iterative UI tweaks predictable and scalable.

Takeaways

Build the board once, but keep access to every tile. Compute alternating colors using (row + column) % 2, and store widgets in a nested list keyed by their grid coordinates. With that foundation, you can change any square’s appearance or label on demand while the event loop keeps the interface responsive.