Layout
A widget isn't visible until you place it with a geometry manager.
ScriptWeaver gives you the three Tk managers — pack, grid, place — plus two
higher-level helpers, Flex and Grid.
Don't mix managers in one parent. Use
packorgridfor the children of a given container, not both — mixing them inside the same parent can hang the layout. Different containers may use different managers.
Each widget exposes its manager as a proxy: w.pack, w.grid, w.place. You
configure placement with .configure({…}) and undo it with .forget().
pack — stacking
pack stacks widgets against a side of their container. Great for toolbars,
button rows, and simple top-to-bottom forms.
const bar = app.TFrame();
bar.pack.configure({ side: 'top', fill: 'x' });
bar.TButton({ text: 'Open' }).pack.configure({ side: 'left' });
bar.TButton({ text: 'Save' }).pack.configure({ side: 'left', padx: 4 });
Common options: side ('top' | 'bottom' | 'left' | 'right'), fill
('x' | 'y' | 'both'), expand (claim leftover space), padx / pady
(outer spacing), and ipadx / ipady (inner padding).
grid — rows and columns
grid places widgets in a table. It's the workhorse for forms and anything
aligned in columns.
const form = app.TFrame({ padding: 10 });
form.pack.configure({ fill: 'both', expand: true });
form.TLabel({ text: 'Name' }).grid.configure({ row: 0, column: 0, sticky: 'w' });
form.TEntry().grid.configure({ row: 0, column: 1, sticky: 'ew' });
form.TLabel({ text: 'Email' }).grid.configure({ row: 1, column: 0, sticky: 'w' });
form.TEntry().grid.configure({ row: 1, column: 1, sticky: 'ew' });
// let column 1 (the inputs) absorb extra width
form.grid.columnConfigure(1, { weight: 1 });
Common cell options: row, column, sticky (any of n / s / e / w —
the edges the widget clings to), columnspan / rowspan, padx / pady. Make
a row or column stretch with parent.grid.rowConfigure(i, { weight: 1 }) or
columnConfigure(i, { weight: 1 }).
place — absolute / relative
place positions a widget by exact or proportional coordinates. Use it sparingly
— for overlays, or when you need pixel control.
badge.place.configure({ relx: 1, rely: 0, anchor: 'ne', x: -8, y: 8 });
Options include x / y (pixels), relx / rely (0–1, a fraction of the
parent), relwidth / relheight, and anchor.
Flex and Grid — higher-level helpers
For responsive layouts, two helpers wrap grid with a friendlier, flexbox-like
API. Create them like any container, passing a children array.
Flex arranges children in a row or column:
const row = app.Flex({
direction: 'row', // or 'column'
gap: 8,
align: 'center',
children: [
app.TButton({ text: 'Cancel' }),
app.TButton({ text: 'OK', style: 'Accent.TButton' }),
],
});
row.pack.configure({ fill: 'x' });
Give a child grow: 1 to make it expand into spare space; set align per child
to override the container default.
Grid lays out a track-based grid — pass columns as a count (equal widths) or
a list of weights, and children as a flat list or as rows of arrays:
const pad = app.Grid({
columns: 3,
gap: 6,
children: [
[app.TButton({ text: '7' }), app.TButton({ text: '8' }), app.TButton({ text: '9' })],
[app.TButton({ text: '4' }), app.TButton({ text: '5' }), app.TButton({ text: '6' })],
],
});
pad.pack.configure({ expand: true, fill: 'both' });
Editing a Flex or Grid at runtime
Both helpers are editable after construction — useful for lists that grow and shrink, or for any UI that reorders itself:
row.add(widget); // append
row.insertAt(0, widget); // insert at an index (an array inserts in order)
row.remove(widget); // unmap and drop it (the widget is not destroyed)
row.move(widget, 2); // reorder to a new index
row.clear(); // remove every child
row.childList(); // ordered snapshot (a copy) of the laid-out children
row.indexOf(widget); // its position, or -1
The layout hints (grow, align, row, col, colSpan, rowSpan,
sticky) are plain properties on each child, so changing one does not reflow on
its own. Set it, then call relayout():
okButton.grow = 1;
row.relayout(); // OK now expands into the spare space
A removed child is only unmapped, not destroyed, so you can re-insertAt() it
later. Call widget.destroy() yourself if you want it gone for good. Every edit
method returns the frame, so calls chain: row.remove(a).insertAt(0, b).