I'm using tkinter to write a card game, and I'm having trouble with he grid layout manager 'sticky' configuration. I would like help fixing my code to make the frames display in the desired location. In my code and illustration below, there is a frame (b2) that contains two other (one green, b2a; and one red; b2b) frames. I would like to display frame b2 at the bottom of the parent frame (frame b). I've tried various combinations of N+S+E+W as arguments for 'sticky', for both frame b2 and the child frames b2a and b2b. However, I've been unable to make frame b2 (and more importantly b2a and b2b) appear in the desired location (the bottom image below with the correct placement was made in Illustrator).
In particular, it seems that sticky arguments in lines 27, 36 and 37 have no effect on the placement of frame b2, b2a and b2b inside of frame b.
from tkinter import *
from PIL import Image, ImageTk
def main(root):
cons = Frame(root)
cons.grid()
frameDict = setup_frames(cons)
populate_frames(frameDict)
def setup_frames(cons):
frame = {}
# Parental Frames
frame['a'] = Frame(cons, borderwidth=2, relief='groove')
frame['b'] = Frame(cons, borderwidth=2, relief='groove')
frame['c'] = Frame(cons, borderwidth=2, relief='groove')
frame['a'].grid(row=0, column=0, sticky=N+S+E+W)
frame['b'].grid(row=0, column=1, sticky=N+S+E+W)
frame['c'].grid(row=1, column=0, columnspan=2)
# Progeny 0 Frames:
frame['b1'] = Frame(frame['b'], borderwidth=2, relief='groove')
frame['b2'] = Frame(frame['b'], borderwidth=2, relief='groove')
frame['b1'].grid(row=0, column=0, sticky=N+S+E+W)
frame['b2'].grid(row=1, column=0, sticky=N+S+E+W)
# Progeny 1 Frames:
frame['b2a'] = Frame(frame['b2'], borderwidth=2, relief='groove',
background='green')
frame['b2b'] = Frame(frame['b2'], borderwidth=2, relief='groove',
background='red')
frame['b2a'].grid(row=0, column=0, sticky=S)
frame['b2b'].grid(row=0, column=1, sticky=SW)
return frame
def populate_frames(fr):
# Populating 'a' frame
aLab = Label(fr['a'], image=img[0])
aLab.grid()
# Populating b2a & b2b frames
bLab = Label(fr['b2a'], image=img[1])
bLab.grid(row=0, column=0)
bLab = Label(fr['b2b'], image=img[2])
bLab.grid(row=0, column=1)
# Populating c1 frame
cLab = Label(fr['c'], image=img[3])
cLab.grid()
if __name__ == '__main__':
root = Tk()
img = []
w = [40, 160, 80, 480]
h = [180, 60, 60, 60]
for i in range(4):
a = Image.new('RGBA', (w[i], h[i]))
b = ImageTk.PhotoImage(a)
img.append(b)
main(root)
The images below illustrate where the offending frames (green and red) are displaying (top) and where I would like them displayed (bottom).
Could someone please help me display frame b2 (and ultimately b2a and b2b) in the correct position (Edit: at the bottom of frame b, and spanning from the right side of frame a to the right side of frame c)?
Update: I've solved both problems (vertical placement and horizontal justification of frame b2) using grid weights, as Bryan suggested. The solution to the vertical placement problem is straightforward, but I would not have predicted the solution to the horizontal justification issue.
I solved the vertical placement problem by giving weight=1 to row 0 in frame b (resulting in the upper panel of the figure below).
I solved the horizontal justification problem (wherein frames b1 and b2 were not stretching to fill frame b) by assigning weight=1 to column 0 in frame b. The frame outlines in the figure below show that frame b is already stretched from the right side of frame a to the right side of frame c. It's strange to me that giving weight to the only column in a frame would be required to allow child frames to fill horizontally. In any case, I've pasted my working code below. Lines 40 and 41 solved the issue I was having.
from tkinter import *
from PIL import Image, ImageTk
def main(root):
cons = Frame(root)
cons.grid()
frameDict = setup_frames(cons)
populate_frames(frameDict)
def setup_frames(cons):
frame = {}
# Parental Frames
frame['a'] = Frame(cons, borderwidth=2, relief='groove')
frame['b'] = Frame(cons, borderwidth=2, relief='groove')
frame['c'] = Frame(cons, borderwidth=2, relief='groove')
frame['a'].grid(row=0, column=0, sticky=N+S+E+W)
frame['b'].grid(row=0, column=1, sticky=N+S+E+W)
frame['c'].grid(row=1, column=0, columnspan=2)
# Progeny 0 Frames:
frame['b1'] = Frame(frame['b'], borderwidth=2, relief='groove')
frame['b2'] = Frame(frame['b'], borderwidth=2, relief='groove')
frame['b1'].grid(row=0, column=0, sticky=N+S+E+W)
frame['b2'].grid(row=1, column=0, sticky=N+S+E+W)
# Progeny 1 Frames:
frame['b2a'] = Frame(frame['b2'], borderwidth=2, relief='groove',
background='green')
frame['b2b'] = Frame(frame['b2'], borderwidth=2, relief='groove',
background='red')
frame['b2a'].grid(row=0, column=0, sticky=S)
frame['b2b'].grid(row=0, column=1, sticky=SW)
# Weighting
frame['b'].grid_rowconfigure(0, weight=1)
frame['b'].grid_columnconfigure(0, weight=1)
return frame
def populate_frames(fr):
# Populating 'a' frame
aLab = Label(fr['a'], image=img[0])
aLab.grid()
# Populating b2a & b2b frames
bLab = Label(fr['b2a'], image=img[1])
bLab.grid(row=0, column=0)
bLab = Label(fr['b2b'], image=img[2])
bLab.grid(row=0, column=1)
# Populating c1 frame
cLab = Label(fr['c'], image=img[3])
cLab.grid()
if __name__ == '__main__':
root = Tk()
img = []
w = [40, 160, 80, 480]
h = [180, 60, 60, 60]
for i in range(4):
a = Image.new('RGBA', (w[i], h[i]))
b = ImageTk.PhotoImage(a)
img.append(b)
main(root)
Consistent with Bryan's advice, it does seem to be a good rule of thumb to assign a weight to at least one column and one row in every container.
Here's before and after I fixed the horizontal justification problem:
Using Python 3.4, Yosemite

