Ir al contenido

Varias ventanas

Una app puede tener tantas ventanas como quieras. La ventana principal es tu App; las demás son subclases de pz.Window, cada una con su view(), título, tamaño y tema.

Varias ventanas

import pyzed as pz
from pyzed import ui
class Settings(pz.Window):
title = "Ajustes"
size = (420, 300)
theme = "One Light" # opcional: por defecto usa el de la app
def view(self):
return pz.container(
pz.column([
ui.heading("Ajustes", level=2),
ui.field("Autoguardado", ui.switch(self.app.autosave, self.app.set_autosave)),
ui.button("Cerrar", self.close),
], spacing=12),
padding=20,
)
class Main(pz.App):
def __init__(self):
self.autosave = True
self.settings = Settings()
def set_autosave(self, value):
self.autosave = value
def open_settings(self):
if not self.settings.is_open:
return self.settings.open()
return pz.Command.focus_window(self.settings)
def view(self):
return pz.center(ui.button("Abrir ajustes", self.open_settings, icon="settings"))
pz.run(Main)
flowchart LR
    subgraph APP["App (estado compartido)"]
        ST[("self.*")]
    end
    M["Ventana principal"] -->|view| ST
    W1["Window A"] -->|"view / self.app"| ST
    W2["Window B"] -->|"view / self.app"| ST
    M -->|"return Window().open()"| W1
    W1 -->|"eventos"| U["update()"]
    W2 -->|"eventos"| U
    M -->|"eventos"| U
    U --> ST
  • ventana.open() (o Command.open_window(ventana)) devuelve el comando que la abre.
  • self.app apunta a la app dentro de cada ventana, así que comparten estado: los callbacks como self.app.guardar funcionan directamente.
  • Los mensajes que se producen en una ventana van a su update(), que por defecto los reenvía a app.update().
  • ventana.is_open indica si está abierta. Cerrar la ventana principal termina la app.

Atributos de clase o de instancia:

Atributo Descripción
title Texto o callable.
size, min_size, max_size Tamaños en píxeles lógicos.
position "center" o (x, y).
resizable, decorations decorations=False quita el marco del sistema por completo.
frame "custom" dibuja la barra de título, los botones de ventana y los menús en Pyzed (ver Marcos de ventana).
transparent, maximized, fullscreen Estado inicial.
scale Escala de la interfaz.
theme Un Theme, un nombre o themes.auto(...). None usa el de la app.
transition, exit_transition Cómo se anima al abrirse y al cerrarse: "fade", "zoom", "pop", "slide_up"… un preset o una pz.Animation. Ver Animación.

window_size contiene el tamaño actual y window_position dónde está la ventana en la pantalla (los actualiza el runtime; la posición es None en Wayland). window_maximized y window_focused dicen si está maximizada y si tiene el foco del teclado.

Método Cuándo se llama
on_open() Al abrirse.
on_close() Cuando el usuario pide cerrarla. Devuelve False para mantenerla abierta (por ejemplo, para preguntar si guardar).
on_resize(ancho, alto) Al cambiar de tamaño.
on_key(combo) Teclas pulsadas con esa ventana enfocada.

Todos aceptan la ventana como último argumento; sin él actúan sobre la ventana que produjo el mensaje.

Comando Efecto
Command.open_window(w) / w.open() Abre la ventana.
Command.close_window(w) / w.close() La cierra.
Command.drag_window(w) / w.drag() Empieza a moverla con el ratón (para barras de título propias).
Command.minimize_window(w) / w.minimize() La minimiza.
Command.toggle_maximize_window(w) / w.toggle_maximize() Maximiza o restaura.
Command.maximize_window(True, w) La maximiza (False la restaura).
Command.drag_resize_window("se", w) Empieza a cambiar su tamaño desde un borde o una esquina (para marcos propios).
Command.show_window_menu(w) Muestra el menú de ventana del sistema (solo Windows).
Command.set_fullscreen(True, w) Pantalla completa.
Command.resize_window(ancho, alto, w) Cambia el tamaño.
Command.move_window(x, y, w) La mueve.
Command.focus_window(w) La trae al frente.

Marcos de ventana: tu propia barra de título

Sección titulada «Marcos de ventana: tu propia barra de título»

frame = "custom" cambia el marco del sistema por uno dibujado por Pyzed: una barra de título que mueve la ventana (doble clic la maximiza), los botones de minimizar, maximizar y cerrar con el estilo del sistema, menús a la izquierda y bordes que cambian el tamaño de la ventana. frame_menus() devuelve los menús.

Marcos de ventana de cada plataforma

class Notas(pz.App):
title = "Notas"
frame = "custom" # o "macos", "windows", "gnome", "ubuntu", "kde"
def frame_menus(self):
return [
ui.Menu("Archivo", [ui.MenuItem("Abrir…", self.abrir, keys="ctrl-o"), ui.SEPARATOR,
ui.MenuItem("Salir", self.close)]),
ui.Menu("Editar", [ui.MenuItem("Deshacer", self.deshacer, keys="ctrl-z")]),
]
def view(self):
return self.editor()

Los menús quedan tras un botón ☰: un clic abre el primero y muestra el nombre de todos, y al pasar el puntero a otro nombre se abre ese. Para poner más cosas en la barra (un selector de proyecto, un botón Ejecutar), llama tú a ui.window_frame en view(); así la ventana no se enmarca dos veces:

def view(self):
return ui.window_frame(
self.editor(),
menus=self.frame_menus(),
start=[ui.title_button("pyzed", chevron=True)],
end=[ui.button("Ejecutar", self.ejecutar, icon="play", size="sm")],
)
Valor de frame Qué obtienes
None (por defecto) o "system" El marco del sistema.
"custom" o True La barra de título de Pyzed con los botones del sistema. En macOS el sistema sigue dibujando sus semáforos sobre la barra.
"macos", "windows", "gnome", "ubuntu", "kde" Los botones de esa plataforma en cualquier sistema.

window_maximized y window_focused dicen si la ventana está maximizada y tiene el foco (los actualiza el runtime), así que el botón de maximizar muestra el símbolo de restaurar y la barra se atenúa en segundo plano. Maximizada, la ventana no tiene borde para cambiar su tamaño.

Las piezas también funcionan por separado: ui.window_title_bar es la barra sola (sin callbacks solo se muestra, que es como Pyzed Design previsualiza los formularios), ui.window_controls los tres botones y pz.window_edges los bordes que cambian el tamaño de una ventana sin decoraciones. Para una barra hecha desde cero, usa los comandos Command.drag_window, Command.drag_resize_window, Command.toggle_maximize_window y Command.show_window_menu.

Notas por plataforma. En Windows la ventana conserva la sombra y las esquinas redondeadas del sistema. Wayland mueve y redimensiona la ventana igual que X11 (lo hace el compositor). En WSL Pyzed usa X11 y "custom" dibuja botones de Windows, el escritorio donde aparece la ventana. ui.system_platform() dice qué estilo elige "custom".

Pestañas arrastrables y ventanas desprendibles

Sección titulada «Pestañas arrastrables y ventanas desprendibles»

Las pestañas de ui.tab_bar se pueden arrastrar. on_drag(id) avisa de que empezó un arrastre; mientras dragging nombra la pestaña, on_drag_over(index) informa del punto de inserción bajo el puntero (o None fuera de la fila) y drop_index dibuja la marca ahí. on_drop(id, event) llega al soltarla, con el puntero en la ventana (event.window_x, event.window_y, fuera de ella si se soltó fuera) y en la pantalla (event.screen). El window_position y el window_size de cada ventana dicen dónde está, así que puedes abrir la pestaña en una ventana nueva o apilarla en la ventana que hay bajo el puntero:

class Detached(pz.Window):
size = (640, 420)
def __init__(self, name, position):
self.name = self.title = name
if position is not None:
self.position = position
def view(self):
return ui.tab_bar([ui.Tab.for_file(self.name)], self.name)
class Editor(pz.App):
def __init__(self):
self.files, self.active = ["main.py", "app.py", "README.md"], "main.py"
self.dragging, self.over = None, None
def start(self, name):
self.dragging = name
def hover(self, index):
self.over = index
def drop(self, name, event):
over, self.dragging, self.over = self.over, None, None
if over is not None: # soltada en la fila: reordenar
i = self.files.index(name)
self.files.insert(over - (over > i), self.files.pop(i))
return None
width, height = self.window_size or self.size
if 0 <= event.window_x < width and 0 <= event.window_y < height:
return None # soltada dentro de la ventana, fuera de la fila
self.files.remove(name)
self.active = self.files[0] if self.files else None
where = (event.screen_x - 80, event.screen_y - 12) if event.screen else None
return Detached(name, where).open()
def view(self):
return ui.tab_bar([ui.Tab.for_file(f) for f in self.files], self.active,
on_select=lambda f: setattr(self, "active", f),
on_drag=self.start, on_drop=self.drop, on_drag_over=self.hover,
dragging=self.dragging, drop_index=self.over)

Para apilar una pestaña en otra de tus ventanas, compara event.screen con el window_position y el window_size de esa ventana. Wayland no les dice a las apps dónde están sus ventanas: ahí event.screen y window_position son None, así que ofrece también un comando de menú (Pyzed Design tiene Mover la pestaña a una ventana nueva y Mover la pestaña a la ventana principal).