7

I'm trying to do something quite standard in GUI design using Python and Tkinter, and I can't figure it out. I'm trying to make something like the following:

+------+----------------------+
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
|      |                      |
+------+----------------------+

Very simple setup with a sidebar and a main content area. I can manage to lay out the frames no problem, but the problem comes when I go to resize the window. I can get the sidebar to resize just fine, vertically but not horizontally and stuck to the left side, but the main content area refuses to fill the rest of the space horizontally. It's like the sidebar still takes up the same horizontal space even though it's set to resize only in the y direction.

Here's some very simple code to demonstrate what I mean:

from Tkinter import *

root = Tk()

# sidebar
sidebar = Frame(root, width=200, bg='white', height=500, relief='sunken', borderwidth=2)
sidebar.pack(expand=True, fill='y', side='left', anchor='nw')

# main content area
mainarea = Frame(root, bg='#CCC', width=500, height=500)
mainarea.pack(expand=True, fill='both', side='right')

root.mainloop()

And here's what happens when I try to resize it:

improperly-resizing layout

Any ideas? I mean, this has to be doable, right? It's practically a standard.

nbro
  • 15,395
  • 32
  • 113
  • 196
Ken Bellows
  • 6,711
  • 13
  • 50
  • 78

1 Answers1

8

TL;DR: use fill="both" for the sidebar.

Overview of the problem

The extra space you are seeing belongs to the sidebar. Because you use expand=true for both widgets, they each are given part of the extra space when you resize the window. Since you didn't specify fill In the "x" direction for the sidebar, it's being given some of the extra space but it is not filling it. It is filling it in the "y" direction, just as you asked, and the main area is completely filling its space since it has fill="both".

You need to either have the sidebar fill in both the "x" and "y" dimension, or turn expand off to have it be a fixed size. Note: when I say "fixed size", it will still fill the area it is given if you use fill=both, but when you resize the window, all extra space will go to the main window.

Using a fixed size sidebar

To have a fixed size sidebar, turn expand off but set fill to be both. You want it to fill the area that was allocated to it, but you don't want that area to grow if the window gets bigger:

import Tkinter as tk

root = tk.Tk()

# sidebar
sidebar = tk.Frame(root, width=200, bg='white', height=500, relief='sunken', borderwidth=2)
sidebar.pack(expand=False, fill='both', side='left', anchor='nw')

# main content area
mainarea = tk.Frame(root, bg='#CCC', width=500, height=500)
mainarea.pack(expand=True, fill='both', side='right')

root.mainloop()

Using an expanding sidebar

To have a flexible-sized sidebar, turn expand on so that it will be given part of the extra space when you resize the window. You want fill to be both so that it fills the space that it is given.

import Tkinter as tk

root = tk.Tk()

# sidebar
sidebar = tk.Frame(root, width=200, bg='white', height=500, relief='sunken', borderwidth=2)
sidebar.pack(expand=True, fill='both', side='left', anchor='nw')

# main content area
mainarea = tk.Frame(root, bg='#CCC', width=500, height=500)
mainarea.pack(expand=True, fill='both', side='right')

root.mainloop()

Summary

The only difference between the two examples is the use of expand=True or expand=False. expand controls what happens to any extra space when the window resizes. When true, extra space is allocated to the widget, along with any other widgets that have expand set to true. In both cases, however, you want the sidebar to completely fill the area it has been given. So in both cases you want fill="both". fill only determines what the widget does with the area it has been given (fill it or not), but has no bearing on whether the widget gets more or less space that it asks for.

  • expand: whether the widget is given more space as the window resizes
  • fill: whether the widget fills the space it is given, or whether part of the space is left empty
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • But I did specify `fill` for the sidebar: `sidebar.pack(... fill='y' ...)` – Ken Bellows Mar 07 '14 at 23:40
  • 1
    Right. Notice how it _is_ filling in the "y" axis but it is _not_ filling in the "x" axis. The extra space you are trying to remove is to the right (in the "x" direction) of the sidebar. – Bryan Oakley Mar 08 '14 at 00:20
  • Yeah, but how do I stop it from expanding in `` but still let it expand in `y`? This is my whole problem. – Ken Bellows Mar 08 '14 at 02:08
  • Are you saying that `fill="x"` and `fill="y"` are useless and don't actually work? – Ken Bellows Mar 08 '14 at 02:10
  • No, I'm not saying that. They are very useful, and do what they advertise. They cause the widget to fill their space in the x and y directions. As you can see, when you specify `fill="y"`, it stretches in the y direction but not the x direction. Note that "expanding" and "filling" are two different things. "expand" means "reserve the extra space", and fill means "fill the extra space". If you expand but don't fill, you steal the space from other widgets without using it. – Bryan Oakley Mar 08 '14 at 02:17
  • If you want a fixed-width sidebar, don't use expand. The normal use case is to only have one widget expand. – Bryan Oakley Mar 08 '14 at 02:18
  • So how would I cause the sidebar to expand vertically if not through `expand`? – Ken Bellows Mar 08 '14 at 02:30
  • it does still need to resize with the window – Ken Bellows Mar 08 '14 at 02:30
  • If you want it to expand to fill extra space, leave `expand` in, and make sure it fills its area in both directions. It's as simple as that. Seriously. Try it. Setting expand means that tkinter will give the widget extra space. Fill tells tkinter what the widget does with that extra space, it can fill it or leave it unused. – Bryan Oakley Mar 08 '14 at 02:33
  • Thing is, I *don't want* the sidebar to fill in both directions. That's the problem. – Ken Bellows Mar 08 '14 at 05:05
  • @KenB: turn off expand in the sidebar if you want the main area to take up all the extra space. It's that simple. – Bryan Oakley Mar 08 '14 at 14:02
  • OMG. I'm really sorry, I *just now* understood what it is you were saying. I was somewhat blinded by preconceptions here. Thank you. I had it in my head that `expand` controlled whether it changed size *at all* when the window resized, and `fill` only controlled the *direction in which it filled*. But that was clearly wrong. The added code was very helpful. – Ken Bellows Mar 09 '14 at 14:31