How to place the buttons side to side in tkinter using place method?

Question:

I am new to tkinter and learning to create simple widgets. I have encountered on issue, when I was creating many buttons to click, I found that the spacing between the buttons is not uniform and it becomes more congested as it goes left to right.

MWE

How to make spacing between buttons uniform?

%%writefile a.py
import sys
import tkinter as tk
from tkinter import ttk,messagebox

win = tk.Tk()

def countdown(win):
    child = tk.Toplevel(win)
    child.geometry('400x300')
    child.resizable(0, 0)
    
    # label: current time title
    tk.Label(child, font='arial 15 bold', text='current time :').place(x=40, y=70)
    tk.Label(child, font='arial 15 bold', text='set the time').place(x=40, y=150)
    tk.Label(child,text='',fg='gray25').place(x=190, y=70)

    frame_top = tk.Frame(child)
    frame_top.pack(expand=False, fill=tk.X)
    
    frame_bottom = tk.Frame(child)
    frame_bottom.pack(expand=False, fill=tk.X)

    mins = [1,2,5,10,15,20,25,30,35,40]
    for i,minn in enumerate(mins):
        tk.Button(frame_top,text=str(minn)+'m',bd='5',).pack(expand=True, side=tk.LEFT)

    for i,minn in enumerate([45,50,55,60,90,120,150,180]):
        tk.Button(frame_bottom,text=str(minn)+'m',bd='5',).pack(expand=True, side=tk.LEFT)

menubar = tk.Menu(win)
menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Scripts", menu=menu)

menu.add_command(label='Countdown',command=lambda : countdown(win))
menu.add_command(label='Exit',command=sys.exit)

win.config(menu=menubar)
win.mainloop()

Suggestion

enter image description here

Asked By: dallascow

||

Answers:

You’ll have a much easier time using a different geometry manager like pack() or, better yet, grid()

Using pack:

import tkinter as tk

child = tk.Tk()
child.geometry('400x300')

x,w = 0,40
mins = [1,2,5,10,15,20,25,30,35,40]
mins2 = [45,50,55,60,90,120,150,180]
# create some frames to contain each row of buttons
frame_top = tk.Frame(child)
frame_top.pack(expand=False, fill=tk.X)
frame_bottom = tk.Frame(child)
frame_bottom.pack(expand=False, fill=tk.X)

for minn in (mins):
    button = tk.Button(frame_top, text=str(minn)+'m', bd='5')
    button.pack(expand=True, side=tk.LEFT)

for minn in (mins2):
    button = tk.Button(frame_bottom, text=str(minn)+'m', bd='5')
    button.pack(expand=True, side=tk.LEFT)

child.mainloop()

Using grid:

import tkinter as tk

child = tk.Tk()
child.geometry('400x300')

x,w = 0,40
mins = [1,2,5,10,15,20,25,30,35,40]
mins2 = [45,50,55,60,90,120,150,180]

for i, min in enumerate(mins):
    button = tk.Button(child, text=str(minn)+'m', bd='5')
    button.grid(row=0, column=i)

for i, minn in enumerate(mins2):
    button = Button(child, text=str(minn)+'m', bd='5')
    button.grid(row=1, column=i)

child.mainloop()

Admittedly, I am the most familiar with pack() – if anyone sees an issue with my grid() implementation, by all means let me know!


Addendum

It’s usually prudent to instantiate your widgets separately from adding them to a geometry manager like pack, place, or grid

# DON'T:
button = ttk.Button(parent).pack()
# button = None
# DO:
button = ttk.Button(parent)  # instantiate widget
button.pack()  # pack separately
# button = .!button

The reason is explained here, but the gist is that the geometry manager methods return None and that can cause problems if you’re not paying attention.

Answered By: JRiggles
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.