Assume a simple 3row-1col grid, where 2nd widget is a label, while 1st and 3rd widgets are Text. sticky
and weight
settings are most certainly correct. Grid dimensions are defined and shouldn't be dictated by its content.
The problem is that Texts in 1st and 3rd rows share the space as if the Label in the 2nd row didn't exist. Both Texts occupy half the grid height each.
Weirder still, the Label is certainly there. You can see it if the grid gets stretched enough to exceed default Text height, which is around 24 lines.
I will appreciate any clarification on this weird behavior.
I am open to alternatives (pack?) that would allow me to combine Text-Label-Text in one column so that each one takes all the width available, Label takes minimal necessary height, and Texts share the remaining grid height equally.
What I've tried
The docs (1,2) regarding the grid manager show, that parent.rowconfigure(n, weight=1)
for n
th row ensures correct resizing, while child.grid(row=r, column=c, sticky="news")
stretches the widget in the r,c
cell of the grid.
Sadly, all the other SO questions dance around these concepts, which fail to help in this situation.
I've made a test application. If you run it with with_text=False
, you can see that grid height is distributed as expected between three Labels. If you then run it with with_text=True)
, you can see that 1st and 3rd rows with Text widgets occupy half the grid height both. If you stretch the app enough vertically, the 2nd row with the Label does appear.
from tkinter import *
import tkinter.font as tkFont
def application(master, with_text):
for r in range(3):
master.rowconfigure(r, weight=1)
for c in range(1):
master.columnconfigure(c, weight=1)
if r == 1 and c == 0:
lbl = Label(master, text="Hello")
lbl.grid(row=r, column=c, sticky="news")
continue
if with_text:
lbl = Text(master, bd=3, relief=SUNKEN)
lbl.grid(row=r, column=c, sticky="news")
lbl.insert(END, 1000 + r + c)
else:
lbl = Label(master, text="Hi")
lbl.grid(row=r, column=c, sticky="news")
root = Tk()
root.geometry("200x100")
root.title('Text grid overflow')
mono_font = tkFont.nametofont("TkFixedFont")
mono_font.configure(size=8)
with_text=False
application(root, with_text)
root.mainloop()
with_text = False
with_text = True
with_text=True, stretched vertically
The factors that contribute to the problem:
grid
will attempt to fit everything into the window, and when it can't it will start reducing the size of the widgets in each row and column proportional to its weight.This is what happens:
grid
tries to fit two 80 character tall widgets and one one-character tall widget into the window. They won't fit because you forced the window to be 200x100 pixels.
So, it has to start shrinking each widget down from its preferred size to make them fit. To do this, it takes one pixel from each widget since they all share the same weight.
If it still doesn't fit, it takes one more from each widget. After a dozen or so attempts, the label will become zero in height so it no longer is visible. It then continues to shrink the text widgets one pixel at a time until they fit in the window.
Note: this behavior is documented in the grid man page, where it states "For containers whose size is smaller than the requested layout, space is taken away from columns and rows according to their weights."
To keep the label at the minimum size and have the text widgets expand to take up all of the extra space, give the row with the label a weight of zero. That will force the grid
algorithm to skip over the label when removing pixels in order to make everything fit into the window.
master.rowconfigure(1, weight=0)