The key to doing layout is to be methodical, and to use the right tool
for the job. It also means that sometimes you need to be creative.
That means using grid
when laying things out in a
grid
, and using pack
when laying things out top-to-bottom or
left-to-right.
The other key is to group your layout code together. It's much, much
easier to visualize and modify the layout when it's all in one block
of code.
In your case you seem to have three or four distinct areas, depending
on how you count. If you want to use grid
, it will be easiest to
combine "menu upper" and "menu lower" into a frame, and treat that
whole frame as a table cell. It looks like you're already doing that,
which is good.
So, let's start with those main areas:
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
# you don't have a status bar in the example code, but if you
# did, this is where you would put it
# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
Any time you use grid
, you need to give at least one row and one
column a positive weight so that tkinter knows where to use any
unallocated space. Usually there is one widget that is the "main"
widget. In this case it's the canvas. You want to make sure that the
row and column for the canvas has a weight of 1 (one):
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
note: using pack
instead of grid
would save you two lines of code, since pack
doesn't require you to set weights the way grid
does.
Next, we need to solve the problem of the menu areas. By default,
frames shrink to fit their contents, which is why adding the label
broke your layout. You weren't telling tkinter what to do with extra space, so the frames shrunk to fit, and extra space went unused.
Since you want "menu_upper" and "menu_lower" to
each share 50% of that area, pack
is the simplest solution. You can
use grid
, but it requires more lines of code to add the row and column weights.
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
Here is a functioning version, with statusbar. Notice how it behaves exactly as it should when you resize the window:
import Tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.title("some application")
# menu left
self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")
self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")
self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")
self.test = tk.Label(self.menu_left_upper, text="test")
self.test.pack()
self.menu_left_upper.pack(side="top", fill="both", expand=True)
self.menu_left_lower.pack(side="top", fill="both", expand=True)
# right area
self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")
self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")
self.some_title.pack()
self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")
self.canvas_area.grid(row=1, column=1)
# status bar
self.status_frame = tk.Frame(self.root)
self.status = tk.Label(self.status_frame, text="this is the status bar")
self.status.pack(fill="both", expand=True)
self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")
self.some_title_frame.grid(row=0, column=1, sticky="ew")
self.canvas_area.grid(row=1, column=1, sticky="nsew")
self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.root.mainloop()
Example()
On an unrelated note: I would strongly encourage you to not remove the ability for the user to resize the window. They know better than you what their requirements are. If you use grid
and pack
properly, the GUI will resize perfectly.