# Campo numérico

> Un número con botones −/+, límites, paso y sufijo. Un componente nativo de Pyzed para GUIs de escritorio en Python, con vista previa, ejemplos y su API.

Source: https://albakore.github.io/pyzed/es/library/number-field/

:::tip[En pocas palabras]
Una caja con un número y botones − / + para hacerlo más pequeño o más grande.
:::

Un campo numérico con botones − y +. También se puede escribir el valor: `on_change` recibe el número ya ajustado a `min`/`max` y el texto que no es un número se ignora.

`ui.number_field` · Categoría: [Formularios](https://albakore.github.io/pyzed/es/library/#formularios) · [Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#number_field)

**Código:**

```python
pz.row([
    ui.number_field(14, on_change, min=8, max=32, suffix="px"),
    ui.number_field(4, on_change, min=1, max=8),
    ui.number_field(0.5, on_change, step=0.1, suffix="s", width=140),
], spacing=12)
```

## Cuándo usarlo

- Para valores exactos con límites razonables: tamaño de fuente, ancho de tabulación, puerto, reintentos.
- Usa `suffix` para las unidades (`"px"`, `"ms"`, `"%"`).

## Uso

```python
import pyzed as pz
from pyzed import ui

class Settings(pz.App):
    def __init__(self):
        self.font_size = 14

    def view(self):
        return ui.number_field(self.font_size, lambda v: setattr(self, "font_size", v),
                               min=8, max=32, suffix="px")
```

## Variantes

:::note[Sobre estas capturas]
Cada captura la graba pyzed mismo, fuera de pantalla a 2x, en One Dark o One Light según el tema de este sitio, y recortada al componente para que se vea a su tamaño real. El puntero pasa por encima, pulsa o escribe en él; un componente que no reacciona al puntero recorre algunos temas. Con movimiento reducido ves el primer fotograma. Abre **Código** bajo una captura para ver el fragmento que la dibuja; los nombres libres como `on_save` son tus propios callbacks.
:::

### Opciones

```python
ui.number_field(4, on_change, min=1, max=8)
```

```python
ui.number_field(14, on_change, min=8, max=32, suffix="px")
```

```python
ui.number_field(0.5, on_change, step=0.1, suffix="s", width=140)
```

```python
ui.number_field(8080, on_change, min=1, max=65535, align="start", width=140)
```

### Límites

En un límite su botón se atenúa y el valor no puede ir más allá.

```python
ui.number_field(1, on_change, min=1, max=8)
```

```python
ui.number_field(8, on_change, min=1, max=8)
```

### Estados y eventos

Tras pulsarlo, ↑/↓ lo mueven un paso (con Shift, diez) e Inicio/Fin saltan a `min`/`max`.

```python
ui.number_field(9.5, on_change, step=0.5, prefix="$", decimals=2, width=150)
```

```python
ui.number_field(70000, on_change, invalid=True, width=150)
```

```python
ui.number_field(4, on_change, min=1, max=8, disabled=True)
```

## Eventos

Campo numérico tiene **2 eventos propios**, en la tabla de abajo. Como todo widget, también tiene los **18 eventos comunes** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), que se conectan con `.on(...)`: mira [Eventos](https://albakore.github.io/pyzed/es/guides/events/) y [Eventos de cada componente](https://albakore.github.io/pyzed/es/library/events/).

| Evento | El handler recibe | Cuándo |
| --- | --- | --- |
| `on_change` | `(value)` | Callable que recibe el nuevo valor. |
| `on_submit` | `()` | Se envía con Enter. |

**El handler recibe** es lo que Pyzed le pasa a tu función: `()` es nada. Cualquier handler también puede ser un mensaje (un valor que no se puede llamar), que va a `update()`.

### Manejar los eventos

Los mismos eventos escritos de tres formas. **Imperativa**: métodos de la app que cambian `self`. **Declarativa**: los handlers producen mensajes y un único `update()` decide. **Funcional**: una función pura dibuja un estado inmutable y pide cambios con `set_state(...)`. Mira [Imperativo, declarativo y funcional](https://albakore.github.io/pyzed/es/guides/code-styles/).

**Imperativo:**

```python
class Settings(pz.App):
    def __init__(self):
        self.font_size = 14
        self.applied = 14

    def view(self):
        return pz.row([
            ui.number_field(self.font_size, lambda v: setattr(self, "font_size", v),   # on_change
                            min=8, max=32, suffix="px",
                            on_submit=lambda: setattr(self, "applied", self.font_size)),  # Enter
            pz.text("Preview", size=self.applied),
        ], spacing=12, align="center")
```

**Declarativo:**

```python
from dataclasses import dataclass

@dataclass
class FontSize:
    value: float

class Settings(pz.App):
    font_size = 14

    def update(self, message):
        match message:
            case FontSize(value):
                self.font_size = value

    def view(self):
        return ui.number_field(self.font_size, FontSize, min=8, max=32, suffix="px")
```

**Funcional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    font_size: float = 14

def view(state, set_state):
    return pz.row([ui.number_field(state.font_size, lambda v: set_state(font_size=v), min=8, max=32),
                   pz.text("Preview", size=state.font_size)], spacing=12, align="center")
```

## Propiedades y API

### `ui.number_field`

```python
number_field(
    value,
    on_change=None,
    *,
    min=None,
    max=None,
    step=1,
    width=120,
    suffix=None,
    prefix=None,
    align='center',
    decimals=None,
    disabled=False,
    invalid=False,
    id=None,
    on_submit=None,
    steppers=True,
    drag=True,
    drag_step=None,
    theme=None,
)
```

Un campo numérico con botones −/+ que también se puede arrastrar, como en Figma. `on_change(valor)` recibe el número ya limitado a `min`/`max`; lo que no se puede leer como número se ignora. Mantén pulsado y mueve el ratón de lado a lado sobre el número para cambiarlo (sigue aunque salgas de la ventana); haz doble clic para escribir un valor, y Enter, Escape o un clic fuera para terminar. Tras pulsarlo, ↑/↓ lo mueven un paso (con Shift, diez) e Inicio/Fin saltan a `min`/`max`.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `value` | *obligatorio* | Valor actual. |
| `min` | `None` | Mínimo. |
| `max` | `None` | Máximo. |
| `step` | `1` | Paso de los botones. |
| `width` | `120` | Ancho: píxeles, `"fill"`, `"shrink"` o `"fill:N"` (proporción). |
| `suffix` | `None` | Unidad que se muestra al final (`"px"`, `"%"`…). |
| `prefix` | `None` | Texto antes del número (`"$"`…). |
| `align` | `"center"` | Dónde va el número entre los botones: `"center"` (por defecto), `"start"` o `"end"`. |
| `decimals` | `None` | Decimales que muestra (por defecto, los de `step`). |
| `disabled` | `False` | Lo atenúa. |
| `invalid` | `False` | Pinta el borde con el color de error. |
| `id` | `None` | Identificador estable para enfocarlo o desplazarlo con un `Command`. |
| `steppers` | `True` | Muestra los botones −/+ (`False`: un campo compacto, como en una rejilla de propiedades). |
| `drag` | `True` | Deja cambiarlo arrastrando el ratón (`False`: un campo de texto normal). |
| `drag_step` | `None` | Cuánto suma cada píxel arrastrado (por defecto `step`, hasta 1; los pasos menores de 1 piden tres píxeles cada uno). |
| `theme` | `None` | Tema a usar en lugar del actual (`pyzed.theme.current()`). |

Eventos: `on_change`, `on_submit` ([eventos](#eventos)).

[Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#number_field)

## Relacionados

- [Deslizador](https://albakore.github.io/pyzed/es/library/slider/): Elige un número de un rango arrastrando.
- [Campo de texto](https://albakore.github.io/pyzed/es/library/input/): Un campo de texto controlado con icono, placeholder y modo contraseña.
- [Campo de formulario](https://albakore.github.io/pyzed/es/library/field/): Una fila de ajustes: título y descripción a la izquierda, control a la derecha.
