Reflow a child grid to fit enclosing window

In the attached program, reflow works when I stretch the width of the main window, but not when I try to unstretch the window. I gather that the sizing algorithm checks first for the width required by the child grid and then sets the width of the parent window accordingly. What I want is for the sizing algorithm to allocate the window first and then tell the child grid what width it needs to be. I expect that I am missing something basic, but every idea I have pursued involves a feature that is deprecated or removed (e.g., overriding size-request).

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

WIDTH = 12

class ReflowTester(Gtk.Window):
    def __init__(self):
        super().__init__()
        self.set_default_size(250, 150)
        self.connect('destroy', Gtk.main_quit)
        self.connect('size-allocate', self.on_size_allocate)

        self.grid = grid = Gtk.Grid()
        self.add(grid)

        n_cols = 250 // WIDTH
        row, col = (0, 0)
        for i in range(58):
            character = chr(ord('A') + i)
            label = Gtk.Label.new(character)
            label.set_size_request(WIDTH, -1)
            self.grid.attach(label, col, row, 1, 1)
            col += 1
            if col == n_cols:
                row, col = (row + 1, 0)

        self.show_all()

    def on_size_allocate(self, widget, allocation):
        labels = [label for label in self.grid.get_children()]
        for label in labels:
            self.grid.remove(label)

        n_cols = allocation.width // WIDTH
        row, col = (0, 0)
        for label in reversed(labels):
            self.grid.attach(label, col, row, 1, 1)
            col += 1
            if col == n_cols:
                row, col = (row + 1, 0)

reflow_tester = ReflowTester()
reflow_tester.show()
Gtk.main()

Your code is broken on many layers, but, more fundamentally: removing a widget from, and adding a widget to, a container will queue a relayout, and you should never do that from within a size_allocate implementation. You’re creating a relayout loop, and that usually makes everything slow, or breaks stuff.

Layout flows from the top-most widget (the window) to each leaf node. Each parent, recursively, will ask the preferred sizes of its children, and then assign a size and position to each child.

If you want a reflowing layout, you should use Gtk.FlowBox, instead of Gtk.Grid.

FlowBox is perfect. I wish I had known about it before wasting time on my broken solution. One comment for posterity: I wanted the height of the container for the FlowBox to adjust according to the number of rows required. I found that I could realize that behavior by putting the FlowBox in a vertical Box. It seems to be necessary to put a FlowBox in either a Box or a ScrolledWindow or it will do strange things with the layout.

Thanks!

The C API has a widget gallery, just in case.

Apart from the link given by Ebassi, you can try this : https://python-gtk-3-tutorial.readthedocs.io/en/latest/gallery.html and commands gtk3-demo and gtk3-widget-factory.

1 Like