1

I am currently trying to make a game in Tkinter which uses multiple different windows.
However, as I am trying to create the layout of a secondary window, I can't seem to get my Return to Menu button underneath the list box, and aligned to the left. I have tried it using .pack() and .grid() methods, but they don't make sense to me.

I've tried using .pack():

header = Frame(wn).pack()
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).pack(anchor='center')
footer = Frame(wn).pack(side=BOTTOM)

return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).pack(side=BOTTOM,padx=20,pady=250)

# body frame (left side)
bodyL = Frame(wn).pack(side=LEFT)

#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).pack(side=LEFT,padx=15)`

And I've tried using .grid():

header = Frame(wn).grid(sticky=N)
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).grid(sticky=N+E+W,row=0,column=0)
footer = Frame(wn).grid(sticky=S)

return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).grid(sticky=S,padx=20,row=0,column=0)

# body frame (left side)
bodyL = Frame(wn).grid(sticky=W)

#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).grid(sticky=W,padx=15, )`

However using .grid() doesn't align my title to the center of the screen anymore.
Is there a way to center it more efficiently - I didn't like using padx=450 to get it centered.
What happens with the button and the list box, is the button appears to the left of the list box, instead of on the bottom. I have attempted several other methods, such as incrementing row numbers, but my script doesn't appear to do what I anticipated.

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
Angreyboy
  • 13
  • 1
  • 7
  • You need to use weights when using grid(). columnconfigure and rowconfigure is what you are looking for. – Mike - SMT Dec 30 '19 at 19:46
  • Take a look at [this post](https://stackoverflow.com/a/50750928/7475225). I go into some detail on the `grid()` geometry manager. Should help clear up the use some. – Mike - SMT Dec 30 '19 at 19:53

1 Answers1

8

Here is an example of how you can set up the weight of specific columns and row to get widgets to stick to a specific location on the screen.

With the use of grid() we need to use columnconfigure() and rowconfigure() to make this work.

These 2 methods are used to define at what ratio the column or row will expand in relation to the columns or rows around it as the container grows or shrinks.

See below example and let me know if you have any questions:

import tkinter as tk


root = tk.Tk()

for i in range(3):
    root.columnconfigure(i, weight=1)

root.rowconfigure(1, weight=1)

tk.Label(root, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(root, text='Top center').grid(row=0, column=1)
tk.Label(root, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(root, text='center').grid(row=1, column=1)
tk.Label(root, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(root, text='Bottom center').grid(row=2, column=1)
tk.Label(root, text='Bottom right').grid(row=2, column=2, sticky='e')

root.mainloop()

Example:

enter image description here

Here is another example but this time I have a title label outside of a frame so that we can make it easier to manage the title being centered and then working with all the other content of the frame is separate from the title label.

import tkinter as tk

root = tk.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(1, weight=1)

frame = tk.Frame(root)
frame.grid(row=1, column=0, sticky='nsew')

for i in range(3):
    frame.columnconfigure(i, weight=1)

frame.rowconfigure(1, weight=1)

tk.Label(root, text='Title centered').grid(row=0, column=0)
tk.Label(frame, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(frame, text='Top center').grid(row=0, column=1)
tk.Label(frame, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(frame, text='Center').grid(row=1, column=1)
tk.Label(frame, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(frame, text='Bottom center').grid(row=2, column=1)
tk.Label(frame, text='Bottom right').grid(row=2, column=2, sticky='e')
tk.Label(root, text='Footer centered').grid(row=2, column=0)
root.mainloop()

Example:

enter image description here

To address your problem in the comments you cannot use grid() or any other geometry manager for that matter on the same line you create your container. This will always cause the variable for that frame to return None as the geometry managers return None when called.

See this image as to what happens when you use grid() on the same line you create your container.

enter image description here

Now if you delete the grid() part on the row that your container is created and then write it on the next line as the commented out section of the above images shows it will work as expected. See below image of proper use of grid() for containers.

enter image description here

To address your 2nd question in the comments you can add this line to provide a button at the bottom left.

tk.Button(root, text='Bottom left button').grid(row=3, column=0, sticky='w')

Example:

enter image description here

Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • Thanks, that was quite helpful. The only part that wasn't was Python. I've pretty much copied and pasted your example code for the Frames and I keep getting an attribute error saying 'rowconfiguration' and 'columnconfiguration' aren't attributes of a NoneType. How do I get around this? It works for the main frame and that the title is neatly centred, however the footer isn't happy with it. – Angreyboy Dec 30 '19 at 20:28
  • That error comes from using a geometry manager on the container directly instead of a new line. For example in my code you can see I define frame on one line and then apply `grid()` on a new line. If you do all of it on the same line then ` grid()` will return `None`. – Mike - SMT Dec 30 '19 at 20:30
  • @Angreyboy I have also updated my 2nd example to include a footer. – Mike - SMT Dec 30 '19 at 20:33
  • Thanks a lot, finally the button to return to the menu appears in the centre at the bottom and not left at the bottom, even when I use sticky=S+W. Is there one last thing I can do? :-) (Edit: Never mind, please ignore as my page was not refreshed to see your extra comment a minute ago) – Angreyboy Dec 30 '19 at 20:38
  • Depends on where you want your button. If you want it in the frame you need to specify the last row in the frame and column 0. If you want it at the bottom left outside of the frame like for example the footer is outside the frame. Then you need to specify `sticky='w'` as well as placing the button on the row you want in column 0. – Mike - SMT Dec 30 '19 at 20:41
  • Is there a way to underline the title, just so that it looks a bit more formal? :) – Angreyboy Dec 30 '19 at 20:43
  • @Angreyboy I have updated my post to include an example of a button on the bottom left. As for a label with underline that is possible as well. See [this post](https://stackoverflow.com/a/3658463/7475225) about how to use tkFont for that. – Mike - SMT Dec 30 '19 at 20:44
  • 1
    This is one of the best answers on Stack Overflow – thethiny Aug 29 '21 at 14:58