How to Center Layout Elements in Eto StackLayout?

Hi Python Gurus,

For the life of me, I have spent half a day trying to figure out what is causing my layout to be off center. Please see attached script and diagrams.

Based on my calculations, my combined cell widths + the text label column width should give me a 684 pixel menu. Instead, my menu is 855 pixels wide, I’m pulling my head out trying to figure out where the additional 171 pixels are coming from.

I have tried to set the middle_row to True and it shifted the whole block to the right.

Any advice? Thank you for checking my post. I appreciate it :slight_smile:

import Eto.Forms as forms
import Eto.Drawing as drawing
import Rhino

class VerticalLine(forms.Drawable):
    def __init__(self):
        super(VerticalLine, self).__init__()
        self.Width = 2
        self.Height = 180

    def OnPaint(self, e):
        super(VerticalLine, self).OnPaint(e)
        g = e.Graphics
        g.AntiAlias = True
        bounds = self.Bounds
        g.FillRectangle(drawing.Brushes.White, bounds)
        center_x = bounds.Width // 2
        g.DrawLine(drawing.Pens.Gray, center_x, 0, center_x, bounds.Height)

_string_columns = {}

def make_column(id):
    column = {}

    Color_field = forms.TextBox(Width=30)
    input_column = forms.StackLayout(Orientation=forms.Orientation.Vertical, Spacing=10)
    input_column.Items.Add(forms.StackLayoutItem(Color_field))

    layout_item = forms.StackLayoutItem(input_column)
    layout_item.HorizontalAlignment = forms.HorizontalAlignment.Center
    column["layout"] = layout_item
    column["divider"] = VerticalLine()
    return column

def create_blank_form():

    def on_document_closed(sender, e):
        form.Close()
    Rhino.RhinoDoc.CloseDocument += on_document_closed

    def make_label(text):
        lbl = forms.Label(Text=text)
        lbl.VerticalAlignment = forms.VerticalAlignment.Center
        stack = forms.StackLayout(Orientation=forms.Orientation.Vertical, Spacing=0)
        stack.Items.Add(forms.Label(Text="", Height=4))
        stack.Items.Add(lbl)
        return forms.StackLayoutItem(stack)

    label_column = forms.StackLayout(Orientation=forms.Orientation.Vertical, Spacing=10)
    for label in ["Color"]:
        label_column.Items.Add(make_label(label))

    middle_row = forms.StackLayout(Orientation=forms.Orientation.Horizontal, Spacing=5)
    middle_row.Items.Add(label_column)
    middle_row.Items.Add(VerticalLine())

    num_Cells = 12
    form_width = 60 + (num_Cells * 52)
    form = forms.Form(Title="Blank Form", ClientSize=drawing.Size(form_width, 200), MinimumSize=drawing.Size(form_width, 200), Padding=drawing.Padding(10), Resizable=False, Topmost=True)

    for i in range(num_Cells):
        col = make_column(i + 1)
        _string_columns[i + 1] = col
        middle_row.Items.Add(col["layout"])
        if i < num_Cells - 1:
            middle_row.Items.Add(col["divider"])

    centered_table = forms.TableLayout(Spacing=drawing.Size(0,0), Padding=drawing.Padding(0))
    centered_table.Rows.Add(forms.TableRow(
        forms.TableCell(None, True),
        forms.TableCell(middle_row, False),
        forms.TableCell(None, True)
    ))

    stack_layout = forms.StackLayout(Orientation=forms.Orientation.Vertical, Spacing=10)
    stack_layout.Items.Add(centered_table)
    form.Content = stack_layout
    form.Show()

create_blank_form()

I’m never a fan of replying to a post of “How do I do X?” with “Don’t do X, do Y”, but in this case I believe Y might be better. If you’re dead-set on doing X though I can help with that.

If your goal is to create an Eto UI with evenly spaced items, it’s best to keep it simple.
Setting pixels yourself won’t work cross platform, or possibly between OS versions.

My example below focuses on the layout and ignores some of your other code for brevity.
I also just prefer DynamicLayouts to StackLayouts for more particular spacing setups (such as this).

import Eto.Forms as ef
import Eto.Drawing as ed
import Rhino

class VerticalLine(ef.Drawable):
    def __init__(self):
        super(VerticalLine, self).__init__()
        self.Width = 2
        self.Height = 180

    def OnPaint(self, e):
        super(VerticalLine, self).OnPaint(e)
        g = e.Graphics
        g.AntiAlias = True
        bounds = self.Bounds
        g.FillRectangle(ed.Brushes.White, bounds)
        center_x = bounds.Width / 2
        g.DrawLine(ed.Pens.Gray, center_x, 0, center_x, bounds.Height)

dialog = ef.Dialog()
dialog.Width = 600
dialog.Height = 80

layout = ef.DynamicLayout()
layout.Padding = ed.Padding(4)
layout.Spacing = ed.Size(4, 4)
layout.BeginHorizontal()

label = ef.Label()
label.Text = "Color"
layout.Add(label, True, True)

for i in range(0,12):
    layout.Add(VerticalLine(), False, True)
    text_box = ef.TextBox()
    text_box.Width = 30
    layout.Add(text_box, True, True)

layout.EndHorizontal()

dialog.Content = layout

dialog.ShowModal()
2 Likes

Thanks Callum for the reply. I used your method and got the UI layout to look exactly how I wanted.

1 Like