Skip to content

A form that does something

A contact form: the user types a name, an email and a message, picks a topic and presses Send. The form checks what was typed, shows a friendly message, counts characters as you type, and prints what it “sent” in the console. It is the shape of most business apps: inputs, a button, and a few lines of Python that react.

Goal Time Level You need
A contact form with checks and feedback 25 minutes Basic · a little Python Your first window
  • Use several kinds of input: Text box, Drop-down and Text area.
  • Read what the user typed in code: form.email.text, form.topic.selected.
  • React as the user types with a change event, and to a click with click.
  • Give feedback: change a label’s text and color, move the cursor with focus().
  • Run code when the window opens (the form’s load event) and read print output in the console.
The contact form after sending: a green thank-you message
The finished form, after pressing Send.
  1. Create the form. Ctrl+N → Form, name it contact, title Contact us. Select an empty spot of the canvas: the Properties panel now shows the form. Set Width to 480 and Height to 500.

  2. Add the fields. For each field drag a Label (its title) and the input under it:

    Label text Control Name Properties
    Name Text box your_name Placeholder Ada Lovelace
    Email Text box email Placeholder ada@example.com, Icon mail
    Topic Drop-down topic Options Question, Bug report, Idea; Selected Question
    (none) Text area message Placeholder How can we help?

    The Options of a drop-down are a list: each item is its own field, and + adds one.

  3. Add the button and two labels for feedback. A Button named send (text Send, variant solid, icon send) in the bottom-right corner, a small Label named counter (text 0 / 500, size xs) under the message, and an empty Label named status next to the button.

  4. Anchor everything. Text boxes and labels: stretch left and right. The text area: stretch on all four sides, so it takes the extra room when the window grows. The button: bottom · right. counter and status: bottom, so they stay under the text area.

  5. Focus the first box when the window opens. Click an empty spot (the form is selected), open Events and double-click load. Write:

    def form_load(form, event):
    return form.your_name.focus()

    Handlers can return a command: focus() puts the cursor in that box.

  6. Count characters as the user types. Select the text area, double-click its change event. event.value is the new text:

    LIMIT = 500
    def message_change(form, event):
    count = len(event.value)
    form.counter.text = f"{count} / {LIMIT}"
    form.counter.color = "error" if count > LIMIT else "text_muted"

    "error" and "text_muted" are theme colors: they look right in a dark and in a light theme.

  7. Check and send. Double-click the Send button and write the checks. Each one shows a message, moves the cursor to the field and stops with return:

    def send_click(form, event):
    if not form.your_name.text.strip():
    form.status.color = "error"
    form.status.text = "Please tell us your name."
    return form.your_name.focus()
    if "@" not in form.email.text:
    form.status.color = "error"
    form.status.text = "That email does not look right."
    return form.email.focus()
    print(f"New {form.topic.selected.lower()} from {form.email.text}")
    form.status.color = "success"
    form.status.text = "Thanks! We got your message."
  8. Run it with F5. Press Send with empty fields, then fill them in. The print goes to the Console at the bottom of the editor, together with the events the form handled and any error.

    The console of Pyzed Design showing prints and events
    The console shows what your form prints, the events it handled and its errors.
examples/tutorial/designer/contact.py
"""Event handlers for the contact form (tutorial: A form with inputs and a button).
pyzed run examples/tutorial/designer/contact.pzform
Each function receives the running ``form`` and an ``event`` (``event.value`` holds the new
value of a ``change`` event).
"""
LIMIT = 500
def form_load(form, event):
"""Runs once when contact opens: put the cursor in the first box."""
return form.your_name.focus()
def message_change(form, event):
"""Runs on every keystroke in message: update the character counter."""
count = len(event.value)
form.counter.text = f"{count} / {LIMIT}"
form.counter.color = "error" if count > LIMIT else "text_muted"
def send_click(form, event):
"""Runs when send is clicked: check the fields, then 'send' the message."""
if not form.your_name.text.strip():
form.status.color = "error"
form.status.text = "Please tell us your name."
return form.your_name.focus()
if "@" not in form.email.text:
form.status.color = "error"
form.status.text = "That email does not look right."
return form.email.focus()
if len(form.message.text) > LIMIT:
form.status.color = "error"
form.status.text = f"Keep the message under {LIMIT} characters."
return None
# print() shows up in the designer's console (and in the terminal)
print(f"New {form.topic.selected.lower()} from {form.email.text}: {form.message.text!r}")
form.status.color = "success"
form.status.text = f"Thanks, {form.your_name.text.split()[0]}! We got your message."
form.message.text = ""
form.counter.text = f"0 / {LIMIT}"
return None
Windows, macOS and Linux
uv run --with pyzed-ui pyzed run examples/tutorial/designer/contact.pzform
Control Event Handler event.value
The form load form_load None: it puts the cursor in the first box.
message (Text area) change message_change The whole text, on every keystroke.
send (Button) click send_click None.
def email_submit(form, event): # Enter in the email box sends too
return send_click(form, event)
def topic_change(form, event): # a drop-down: event.value is the option picked
form.status.color = "text_muted"
form.status.text = f"Topic: {event.value}"
def send_mouse_enter(form, event): # every control has mouse_enter and mouse_leave
form.status.text = "Ready to send?"
def form_close(form, event): # return False to keep the window open
if form.message.text.strip():
form.status.text = "Send or clear the message before closing."
return False