PathField#

A text field with a browse button that opens a native file or directory picker dialog. When the user confirms a selection, the field value is updated and a change event fires.

PathField — light theme PathField — dark theme

Usage#

field.value is the selected path as a string — the user types it or picks it from a native dialog, and mode chooses which dialog opens (open a file, open many, save, or pick a directory). on_change fires after a pick or a manual edit; in 'open_multiple' mode the raw tuple of paths is on field.dialog_result.

Basic usage#

bs.PathField(placeholder="Select a file…")

Dialog modes#

Use mode= to control which native dialog opens when the browse button is clicked. The default is 'open'.

bs.PathField(mode="open")           # single-file open
bs.PathField(mode="open_multiple")  # multi-file open (result is a tuple)
bs.PathField(mode="save")           # save-file dialog
bs.PathField(mode="directory")      # directory picker

mode and the other dialog options (dialog_title, start_dir, file_filters, default_extension, default_filename) are also live properties — assign to them to reconfigure the picker after construction:

pf = bs.PathField(mode="open")
pf.mode = "save"                 # switch the dialog the button opens
pf.start_dir = last_used_folder  # open where the user last browsed

File filters#

Pass file_filters= as a list of (description, pattern) pairs to restrict which file types appear in the dialog. Has no effect in 'directory' mode.

bs.PathField(
    file_filters=[
        ("Images", "*.png *.jpg *.jpeg *.gif"),
        ("All Files", "*.*"),
    ],
)

Label and message#

Use label= for a field title and message= for helper text below. Set required=True to mark the field visually.

bs.PathField(
    label="Source file",
    placeholder="Select a file…",
    message="Accepted formats: .py, .txt, .csv",
)
bs.PathField(
    label="Output directory",
    placeholder="Choose output folder…",
    mode="directory",
    required=True,
)
PathField label and message — light theme PathField label and message — dark theme

States#

bs.PathField(value="/home/user/docs/report.pdf", label="Normal")
bs.PathField(value="/home/user/docs/report.pdf", label="Read only", read_only=True)
bs.PathField(value="/home/user/docs/report.pdf", label="Disabled",  disabled=True)
PathField states — light theme PathField states — dark theme

Reactive binding#

Bind a Signal[str] with textsignal=. The field and signal stay in sync automatically.

path = bs.Signal("")
bs.PathField(label="Pick a file", textsignal=path)
bs.Label(textsignal=path, accent="secondary")   # updates on each selection

Handling changes#

Use on_change() to respond when the user picks a path. The event fires after the dialog closes and the value is committed, or when the user edits the text portion directly and leaves the field.

pf = bs.PathField(label="Source file")

def handle_change(e):
    print("Selected:", pf.value)

pf.on_change(handle_change)

# As a subscription (cancellable)
sub = pf.on_change(handle_change)
sub.cancel()

# As a Stream (composable)
pf.on_change().listen(handle_change)

For 'open_multiple' mode, the raw tuple of paths is available on pf.dialog_result after the dialog closes.

Validation#

A path field validates like any text field — rules run against the path string. Attach them with add_validation_rule() (for example a custom rule that checks the extension, or required for a mandatory path):

field = bs.PathField(label="Data file")
field.add_validation_rule(
    "custom",
    func=lambda path: bool(path) and path.endswith(".csv"),
    message="Choose a .csv file.",
    trigger="blur",
)

is_valid = field.validate()   # run every rule on demand
PathField validation — light theme PathField validation — dark theme

Validity is reactive state. field.valid is a Signal[bool] and field.error a Signal[str] (the current message, "" when valid) — bind the error straight to a label and it keeps itself in sync:

bs.Label(textsignal=field.error, accent="danger")   # shows and clears itself

Note

The full rule taxonomy and aggregating a whole form’s validity live in the Validation guide.

Widget sizing#

All widgets accept self-placement kwargs via **kwargs. The parent container determines which options apply — Column / Row parents use the layout kwargs below, grid-based parents use grid kwargs.

Column (vertical layout)

Used inside a Column, App, or any other container with a column layout. Children are arranged top-to-bottom, so horizontal aligns each child across the width and grow shares the vertical space. (vertical does not apply — the order of the children sets their top-to-bottom position.)

horizontal

Cross-axis placement of the widget: 'left', 'center', 'right', or 'stretch' to fill the available width.

grow

Claim and fill a share of the leftover vertical space (the layout direction). True or False.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Row (horizontal layout)

Used inside a Row or any other container with a row layout. Children are arranged left-to-right, so vertical aligns each child across the height and grow shares the horizontal space. (horizontal does not apply — the order of the children sets their left-to-right position.)

vertical

Cross-axis placement of the widget: 'top', 'center', 'bottom', or 'stretch' to fill the available height.

grow

Claim and fill a share of the leftover horizontal space (the layout direction). True or False.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Grid

Used inside a Grid container.

row / column

Zero-based row and column indices.

rowspan / columnspan

Number of rows or columns to span.

horizontal

Horizontal placement within the grid cell: 'left', 'center', 'right', or 'stretch' to fill the cell width.

vertical

Vertical placement within the grid cell: 'top', 'center', 'bottom', or 'stretch' to fill the cell height.

margin

External spacing in pixels. Accepts an integer, a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing. Accepts an integer or (left, right).

margin_y

Vertical external spacing. Accepts an integer or (top, bottom).

See also#

API#

The complete reference for PathField lives on the Widgets API page. At a glance:

PathField

A text field with a browse button that opens a native file/directory dialog.

Full Example#

 1
 2with bs.App(title="PathField Demo", padding=20, gap=16) as app:
 3
 4    # Basic usage
 5    bs.Label("Basic", font="heading-sm")
 6    bs.PathField(placeholder="Select a file…")
 7
 8    # Dialog modes
 9    bs.Label("Dialog Modes", font="heading-sm")
10    bs.PathField(label="Open file",        mode="open",          placeholder="Select a file…")
11    bs.PathField(label="Open multiple",    mode="open_multiple", placeholder="Select files…")
12    bs.PathField(label="Save file",        mode="save",          placeholder="Choose save location…")
13    bs.PathField(label="Select directory", mode="directory",     placeholder="Select a folder…")
14
15    # File filters
16    bs.Label("File Filters", font="heading-sm")
17    bs.PathField(
18        label="Image file",
19        file_filters=[("Images", "*.png *.jpg *.jpeg *.gif"), ("All Files", "*.*")],
20        placeholder="Select an image…",
21    )
22
23    # Label and message
24    bs.Label("Label and Message", font="heading-sm")
25    bs.PathField(
26        label="Project folder",
27        message="Must contain a pyproject.toml or setup.py.",
28        placeholder="Select a folder…",
29        mode="directory",
30        required=True,
31    )
32
33    # Reactive binding
34    bs.Label("Reactive Binding", font="heading-sm")
35    path_sig = bs.Signal("")
36    bs.PathField(label="Pick a file", textsignal=path_sig)
37    bs.Label(textsignal=path_sig, accent="secondary")
38
39    # States
40    bs.Label("States", font="heading-sm")
41    bs.PathField(value="/home/user/docs/report.pdf", label="Normal")
42    bs.PathField(value="/home/user/docs/report.pdf", label="Read only", read_only=True)
43    bs.PathField(value="/home/user/docs/report.pdf", label="Disabled",  disabled=True)
44
45    # Handling changes
46    bs.Label("Handling Changes", font="heading-sm")
47    last = bs.Signal("(none)")
48    pf = bs.PathField(label="Choose a file")
49    pf.on_change(lambda e: last.set(pf.value))
50    bs.Label(textsignal=last, accent="secondary")
51
52app.run()