user-interfaceenaml

How to set fixed width/height in Enaml grid container


I have just come across Enaml for python GUI programming. I only used PySide2 once before for a simple calculator GUI mockup, so I'm new to both Qt and Enaml. Forgive my ignorance ;)

Essentially, I want to have a regular grid of Field or other elements, with fixed, square sizes. After going over some of the examples, I came up with:

from enaml.layout.api import grid
from enaml.widgets.api import Window, Container, Field


enamldef DigitField(Field):
    # The fields will contain a single digit for testing.
    mask = 'D'
    max_length = 1
    # These don't have any effect?
    hug_width = 'required'
    hug_height = 'required'

enamldef Main(Window):
    Container:
        constraints = [
            grid(
                [f11, f12],
                [f21, f22],
            ),
        ]

        DigitField: f11:
            text = '1'
        DigitField: f12:
            text = '1'

        DigitField: f21:
            text = '1'
        DigitField: f22:
            text = '1'

But the hug_width and hug_height don't seem to work. I then tried manually setting f11.width == 50, for example, inside the constraints, but the kiwisolver shouts at me about unresolvable constraints. I tried everything I could find from the examples about setting width values, but stuff that works for vbox doesn't seem to play with grid.

Any ideas? Also, if someone has a full app made with Enaml, that is open source, I would love to take a look. The docs are OK but some more advanced examples would be awesome.


Solution

  • Well, I think I have found a way to make it work. hug_width restricts width to the field content plus some default padding (from the Qt toolkit). Instead, using resist_width = 'ignore' I was able to completely remove the padding. The grid can be generated using a manual or an automatic method.

    The manual method:

    from enaml.layout.api import grid
    from enaml.widgets.api import Window, Container, Field
    
    
    enamldef DigitField(Field):
        # The fields will contain a single digit for testing.
        mask = 'D'
        max_length = 1
        resist_width = 'ignore'
        resist_height = 'ignore'
    
    enamldef Main(Window):
        Container:
            constraints = [
                grid(
                    [f11, f12],
                    [f21, f22],
                ),
                f11.width == f11.height,
                f12.width == f12.height,
            ]
    
            DigitField: f11:
                text = '1'
            DigitField: f12:
                text = '1'
    
            DigitField: f21:
                text = '1'
            DigitField: f22:
                text = '1'
    

    This is too WET and scales horribly, so instead we have...

    The factory method:

    from itertools import zip_longest
    
    from enaml.core.api import Include
    from enaml.layout.api import align, grid, factory
    from enaml.widgets.api import Window, Container, Field
    
    
    enamldef DigitField(Field):
        mask = 'D'
        max_length = 1
        resist_width = 'ignore'
        resist_height = 'ignore'
    
    def generate_grid(container, num_cols):
        rows = []
        widgets = container.visible_widgets()
        row_iters = (iter(widgets),) * num_cols
        rows = list(zip_longest(*row_iters))
        return [grid(*rows), align('width', *widgets)]
    
    enamldef Main(Window):
        Container:
            Container:
                constraints << [factory(generate_grid, 3)]
                Include:
                    objects << [DigitField(text=str(1)) for i in range(9)]
    

    I have nested the Container because there will probably be other things in the main window as well, and Enaml windows require a single master Container.