# Cambios sin guardar

> “¿Guardar los cambios antes de cerrar?”. 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/unsaved-changes/

:::tip[En pocas palabras]
La pregunta “¿Guardar los cambios antes de cerrar?”.
:::

“¿Guardar los cambios de *nombre* antes de cerrar?” con No guardar, Cancelar y Guardar.

`ui.unsaved_changes` · Categoría: [Diálogos](https://albakore.github.io/pyzed/es/library/#diálogos) · [Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#unsaved_changes)

**Código:**

```python
ui.dialog_layer(main_view(), ui.unsaved_changes("notes.py", on_save, on_discard, on_cancel))
```

:::note[Nuevo]
Este componente se añadió hace poco. Cuéntanos qué tal te funciona en el [gestor de incidencias](https://github.com/albakore/pyzed/issues).
:::

## Cuándo usarlo

- Al cerrar una pestaña o una ventana con cambios sin guardar.

## Uso

```python
from pyzed import ui

ui.dialog_layer(main_view(), ui.unsaved_changes("notes.py", on_save, on_discard, on_cancel))
```

## 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.unsaved_changes("notes.py", on_save, on_discard, on_cancel)
```

```python
ui.unsaved_changes("report.pzform", on_save, on_discard, on_cancel, width=360)
```

## Eventos

Cambios sin guardar tiene **3 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_save` | `()` | Guardar. |
| `on_discard` | `()` | No guardar. |
| `on_cancel` | `()` | Cancelar. |

**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 Notes(pz.App):
    def __init__(self):
        self.closing = True

    def save(self):
        self.closing = False             # write the file, then close

    def view(self):
        dialog = ui.unsaved_changes("notes.py", self.save,
                                    lambda: setattr(self, "closing", False),   # on_discard
                                    lambda: setattr(self, "closing", False))   # on_cancel
        return ui.dialog_layer(main_view(), dialog if self.closing else None)
```

**Declarativo:**

```python
class Notes(pz.App):
    closing = True

    def update(self, message):
        match message:
            case "save" | "discard":
                self.closing = False
            case "cancel":
                self.closing = False

    def view(self):
        dialog = ui.unsaved_changes("notes.py", "save", "discard", "cancel")
        return ui.dialog_layer(main_view(), dialog if self.closing else None)
```

**Funcional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    closing: bool = True
    saved: bool = False

def view(state, set_state):
    dialog = ui.unsaved_changes("notes.py", lambda: set_state(saved=True, closing=False),
                                lambda: set_state(closing=False), lambda: set_state(closing=False))
    return ui.dialog_layer(main_view(), dialog if state.closing else None)
```

## Propiedades y API

### `ui.unsaved_changes`

```python
unsaved_changes(name, on_save, on_discard, on_cancel, *, width=440, theme=None)
```

«¿Guardar los cambios de *nombre* antes de cerrar?» con No guardar, Cancelar y Guardar.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `name` | *obligatorio* | Nombre del archivo o documento. |
| `width` | `440` | Ancho: píxeles, `"fill"`, `"shrink"` o `"fill:N"` (proporción). |
| `theme` | `None` | Tema a usar en lugar del actual (`pyzed.theme.current()`). |

Eventos: `on_save`, `on_discard`, `on_cancel` ([eventos](#eventos)).

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

## Relacionados

- [Confirmación](https://albakore.github.io/pyzed/es/library/confirm/): Una pregunta de sí o no, en rojo para acciones destructivas.
- [Elección](https://albakore.github.io/pyzed/es/library/choice/): Un mensaje con tus propios botones.
- [Barra de pestañas](https://albakore.github.io/pyzed/es/library/tab-bar/): Pestañas de editor con iconos, puntos de cambios y botón de cerrar.
