Skip to content

Layout basics

Every screen, from a login box to a full IDE, is built from four ideas: stack things vertically, put them side by side, decide who takes the free space, and leave room between them. Learn them once and you can read any Pyzed layout.

Goal Time Level You need
A profile page that resizes nicely 20 minutes Basic · Python Pyzed installed (Installation)
  • pz.column and pz.row: vertical and horizontal stacks.
  • spacing (the gap between children) and padding (the room inside the edge).
  • width="fill" / height="fill" and pz.hspace(): taking the free space.
  • align: lining children up across the stack.
  • pz.grid: equal cells.
  • The same ideas in the designer: Flex row, Flex column, Grid, Spacer and anchors.
A profile page: avatar and name, a Follow button at the right, three stat cards and an About box
The finished layout. Resize the window: the About box grows, the button stays at the right.
pz.column([...], spacing=16) pz.row([...], spacing=12, align="center")
┌──────────────┐ ┌───────────────────────────────────────┐
│ header │ │ (AL) Ada Lovelace ⟷fill⟷ [Follow]│
│ ── 16 px ── │ └───────────────────────────────────────┘
│ stats │ pz.hspace() pushes the button right
│ ── 16 px ── │
│ about (fill) │ ← height="fill" takes what is left
└──────────────┘
Size value Means
a number (width=200) Exactly that many pixels.
"shrink" (the default for most widgets) As small as the content.
"fill" All the free space; several fill children share it equally.
"fill:2" Twice the share of a plain fill sibling.
  1. The page is a column. view() returns one widget; here a pz.container with padding=24 around a column of three parts, spacing=16 apart:

    def view(self):
    t = self.resolved_theme()
    return pz.container(
    pz.column([self.header(), self.stats(), self.about()], spacing=16, height="fill"),
    padding=24, width="fill", height="fill", bg=t.background,
    )
  2. The header is a row. An avatar, a column with the name and a muted line, then pz.hspace(), an empty piece with width="fill", then the button. align="center" centers them vertically:

    pz.row([
    ui.avatar("AL", size=48),
    pz.column([ui.heading("Ada Lovelace", level=2), ui.muted("Analyst · London")], spacing=2),
    pz.hspace(),
    ui.button("Follow", self.toggle_follow, variant="solid", icon="user-plus"),
    ], spacing=12, align="center")
  3. Three equal cards: a grid. columns=3 makes three cells of the same width; height is the height of each row:

    pz.grid([ui.stat("Projects", "12", width="fill"), ...], columns=3, spacing=12, height=84)
  4. The last part fills. height="fill" on the About box makes it take every pixel the other parts leave, so it grows with the window.

  5. Run it and resize the window. The header keeps the button at the right edge, the cards stay equal and the About box stretches.

The designer has the same building blocks in the Layout category of the toolbox:

In code In the designer
pz.row([...], spacing=8) Flex row, with Gap 8
pz.column([...]) Flex column
pz.hspace() Spacer (a child with grow 1)
width="fill" on a child The child’s Grow field (Position section) set to 1
pz.grid([...], columns=3) Grid with Columns 3
padding=24 The container’s Padding
A sidebar form made of a flex column and its children
A flex column in Pyzed Design: children are placed one after another, in the order of the outline.

Controls placed freely on the form (not in a flex container) use anchors instead: anchored left and right, a control stretches; anchored only right, it follows the right edge. Select controls and use Group in a row / column / grid (right-click menu) to turn a free arrangement into a flex one. Details in Layouts and containers.

examples/tutorial/layout_basics.py
"""Layout basics: rows, columns, fill, spacing, padding, alignment and grids.
python examples/tutorial/layout_basics.py
A small profile page laid out with the four ideas behind every Pyzed layout:
- ``pz.column`` stacks children top to bottom, ``pz.row`` puts them side by side.
- ``spacing`` is the gap between children, ``padding`` the room inside the edge.
- ``width="fill"`` / ``height="fill"`` take the space that is left; ``pz.hspace()`` is an empty
piece that fills, so it pushes what follows to the end of the row.
- ``pz.grid`` places children in cells of equal width.
Walkthrough: https://albakore.github.io/pyzed/tutorials/basic/layout-basics/
"""
import pyzed as pz
from pyzed import ui
class Profile(pz.App):
title = "Layout basics"
size = (720, 500)
def __init__(self):
self.follows = False
def header(self):
# A row: avatar, a column with two lines, a spacer that fills, then a button at the far right.
return pz.row([
ui.avatar("AL", size=48),
pz.column([ui.heading("Ada Lovelace", level=2), ui.muted("Analyst · London")], spacing=2),
pz.hspace(),
ui.button("Following" if self.follows else "Follow", self.toggle_follow,
variant="outline" if self.follows else "solid", icon="user-plus"),
], spacing=12, align="center")
def stats(self):
# A grid: three cells of the same width, whatever the window size.
return pz.grid([
ui.stat("Projects", "12", width="fill"),
ui.stat("Followers", "1,205" if self.follows else "1,204", width="fill"),
ui.stat("Stars", "86", width="fill"),
], columns=3, spacing=12, height=84) # height: the height of each row
def about(self):
# A container with height="fill" grows to take the rest of the window.
t = self.resolved_theme()
return pz.container(
pz.column([
ui.label("About", weight="semibold"),
ui.label("Writes the first programs for the Analytical Engine. "
"Resize the window: this box grows, the button stays at the right."),
], spacing=6),
padding=16, width="fill", height="fill",
bg=t.surface, border_color=t.border, border_width=1, radius=t.radius + 2,
)
def toggle_follow(self):
self.follows = not self.follows
def view(self):
t = self.resolved_theme()
return pz.container(
pz.column([self.header(), self.stats(), self.about()], spacing=16, height="fill"),
padding=24, width="fill", height="fill", bg=t.background,
)
if __name__ == "__main__":
pz.run(Profile)
Windows, macOS and Linux
uv run --with pyzed-ui python examples/tutorial/layout_basics.py

The page has one event: the Follow button’s on_press, which calls toggle_follow (no argument). Layout widgets have no events of their own, but any widget takes the common events with .on().

def __init__(self):
self.follows = False
self.narrow = False # new: the window is narrow
self.opened = None # new: the stat that was clicked
def stats(self):
return pz.grid([
ui.stat(name, value, width="fill", on_press=lambda name=name: setattr(self, "opened", name))
for name, value in (("Projects", "12"), ("Followers", "1,204"), ("Stars", "86"))
], columns=1 if self.narrow else 3, spacing=12, height=84)
def view(self):
page = pz.column([self.header(), self.stats(), self.about()], spacing=20, padding=24)
# resize: one column when the window is narrower than 560 px
return page.on(resize=lambda e: setattr(self, "narrow", e.width < 560),
double_click=lambda: setattr(self, "opened", None))