Ir al contenido

Servicios

Los servicios son las partes de una app que lanzan eventos pero no dibujan nada: pz.Timer, pz.Shortcut, pz.FileWatcher, pz.Process, pz.Worker, pz.HttpRequest, pz.Clipboard, pz.Settings, pz.FileDialog, pz.Notification y pz.TrayIcon. Son los mismos objetos que los componentes no visuales de Pyzed Design, y funcionan en un pz.App, en un formulario o en un script sin ninguna ventana.

from pathlib import Path
import pyzed as pz
from pyzed import ui
class Inbox(pz.App):
def __init__(self):
self.files = sorted(p.name for p in Path("inbox").glob("*"))
self.watcher = pz.FileWatcher("inbox", on_change=self.refresh)
self.watcher.start()
def refresh(self, changed): # corre en el hilo de la interfaz
self.files = sorted(p.name for p in Path("inbox").glob("*"))
def view(self):
return pz.column([ui.list_item(name, icon="file-text") for name in self.files], padding=16)
pz.run(Inbox)

Un Vigilante de archivos listando los archivos que se crean y se borran en una carpeta

Pasa los handlers como on_<evento>=, o más tarde con service.on("evento", handler) (None lo quita). Un handler con un parámetro recibe el valor del evento; uno sin parámetros simplemente se llama. Cada clase lista sus eventos en Service.events.

Los vigilantes, los procesos, las tareas y las peticiones web trabajan en hilos en segundo plano, pero sus eventos llegan a los handlers en el hilo de la app (con pz.post), así que un handler puede cambiar el estado de la app y devolver comandos como cualquier callback. Sin una app en marcha, en un script o en un test, el handler se llama en el momento, en el hilo que lanzó el evento.

service.enabled = False silencia todos sus eventos; stop() detiene lo que esté haciendo.

Clase Lanza Qué saber
pz.Timer(interval=1000, repeat=True) tick (la cuenta) Milisegundos, como en Visual Basic y Qt. Devuelve timer.subscriptions() desde App.subscriptions. start(), stop(), running.
pz.Shortcut("mod-s") activate (la combinación) Llama a shortcut.handle(combo) desde on_key. Valen "Ctrl+S", "ctrl-s, cmd-s" y mod- (Cmd en macOS, Ctrl en el resto).
pz.FileWatcher(path, pattern="*", recursive=False, interval=500) created, modified, deleted (una ruta), change (la lista) Compara fechas y tamaños cada interval ms, así que funciona igual en todas partes, también en unidades de red.
pz.Process(command, directory=None, env=None, shell=False) started, output, error_output (una línea), finished (código de salida), failed start(), write(text), kill(), wait(), running, exit_code. Un comando en texto se separa como lo haría una shell.
pz.Worker(work) progress, done (el resultado), failed (la excepción) run(value) llama a work(value) en un hilo; puede usar report(progress) y mirar cancelled.
pz.HttpRequest(url, method="GET", headers=None, body=None, timeout=30) response (un HttpResponse), failed send(); send(json={...}) envía JSON con POST. Los estados de error también son respuestas: mira response.ok.
pz.Clipboard(watch=False, interval=500) change, paste (el texto) copy(text) y paste() devuelven comandos. Con watch, devuelve subscriptions() desde tu app.
pz.Settings(name, folder=None, defaults=None) change ((clave, valor)) Un diccionario guardado como JSON al momento (autosave), por usuario, en la carpeta de ajustes de Pyzed.
pz.FileDialog(mode="open", filters=..., directory=None, file_name="") chosen (ruta o lista), cancelled show() devuelve el comando que lo abre; modos open, open_many, save, folder.
pz.Notification(title, text) shown, failed show(text=None) devuelve si se pudo mostrar.
pz.TrayIcon(icon=None, tooltip="", menu=()) click, double_click (Windows), menu (el elemento), failed show() devuelve si pudo; hide(). Los cambios de icon, tooltip y menu se ven al momento.
pz.SystemInfo(interval=0, disk_path=None) update (un diccionario) os, os_version, hostname, user, cpu_count, language, memory_total, memory_available, disk_free; folder("downloads"), env(name).
pz.Launcher(target="") opened, failed (el destino) open(target) con el programa del sistema (archivos, carpetas, enlaces, mailto:), reveal(path), email(to, subject, body).
pz.NetworkMonitor(host="1.1.1.1", port=53, interval=5000) change (bool), online, offline Prueba una conexión cada interval ms en segundo plano; online guarda la última respuesta.
pz.PowerMonitor(interval=30000, low_level=20) change ((porcentaje, enchufada)), low, plugged, unplugged percent es None en ordenadores sin batería (has_battery).
import time
import pyzed as pz
class Clock(pz.App):
def __init__(self):
self.now = ""
self.clock = pz.Timer(1000, on_tick=self.tick)
self.quit = pz.Shortcut("mod-q", on_activate=lambda: pz.Command.exit())
def tick(self):
self.now = time.strftime("%H:%M:%S")
def subscriptions(self):
return self.clock.subscriptions()
def on_key(self, combo):
return self.quit.handle(combo)
def view(self):
return pz.text(self.now, size=48)
self.log = []
self.build = pz.Process("cargo build --release", directory="engine",
on_output=self.log.append, on_error_output=self.log.append,
on_finished=lambda code: self.done(code == 0))
self.build.start()
def resize_all(paths):
for i, path in enumerate(paths):
if worker.cancelled:
return None
shrink(path)
worker.report((i + 1) * 100 // len(paths))
return len(paths)
worker = pz.Worker(resize_all, on_progress=self.set_progress, on_done=self.finished)
worker.run(photos)
api = pz.HttpRequest("https://api.github.com/repos/albakore/pyzed", on_response=self.show_stars)
api.send()
def show_stars(self, response):
if response.ok:
self.stars = response.json()["stargazers_count"]
prefs = pz.Settings("my-app", defaults={"theme": "One Dark"})
prefs["theme"] = "One Light" # se guarda al momento
prefs.get("theme") # "One Light", también la próxima vez
dialog = pz.FileDialog("open", filters="Imágenes: png, jpg", on_chosen=self.load_image)
def open_clicked(self):
return dialog.show() # un comando: devuélvelo

Los servicios de pyzed.system (también en el nivel superior: pz.SystemInfo, pz.Launcher…) leen el ordenador y abren cosas con los programas que el usuario ya tiene:

info = pz.SystemInfo()
f"{info.os} {info.os_version}, {info.cpu_count} núcleos" # "Windows 11, 8 núcleos"
info.folder("downloads") # la carpeta Descargas del usuario
info.disk_free // 2**30 # GB libres
launcher = pz.Launcher(on_failed=lambda destino: print("No se pudo abrir", destino))
launcher.open("https://example.com") # el navegador por defecto
launcher.open("informe.pdf") # el visor de PDF
launcher.reveal("informe.pdf") # su carpeta, con el archivo seleccionado
network = pz.NetworkMonitor(on_offline=lambda: print("Sin conexión"), on_online=lambda: print("De vuelta"))
network.start()

Para archivos, ui.file_input es un campo de ruta con un botón que abre el diálogo del sistema, y ui.DropZone recibe archivos arrastrados desde el escritorio. Una ventana también recibe cada archivo soltado en on_file_drop(path) (on_file_hover y on_file_leave mientras se arrastran por encima). Mira las páginas de Sistema de la librería.

pz.TrayIcon pone un icono en el área de notificación de Windows, en la barra de menús de macOS y en el panel de Linux, con un texto al pasar el ratón y un menú. El menú es una lista, o un texto con un elemento por línea: - es un separador, [x] y [ ] crean casillas que cambian al hacer clic, y las líneas con sangría forman un submenú.

self.tray = pz.TrayIcon("icono.png", "Copias", menu="""
Abrir
[x] Pausa
Velocidad
Lenta
Rápida
-
Salir
""", on_click=self.open, on_menu=self.menu)
self.tray.show()
def menu(self, item):
if item == "Pausa":
self.paused = self.tray.checked("Pausa")
elif item == "Salir":
return pz.Command.exit()

pz.TrayItem(text, id=None, enabled=True, checked=None, items=[]) le da a un elemento un id (lo que recibe on_menu) o lo desactiva. Sin icon muestra un punto azul.

En Linux el icono es un StatusNotifierItem, sin GTK: KDE, Cinnamon, XFCE, MATE, LXQt y la mayoría de paneles lo muestran, y GNOME también con la extensión AppIndicator. pz.TrayIcon.supported() dice si el escritorio muestra iconos de bandeja; donde no, show() devuelve False y failed dice por qué.

Nada de esto necesita una app en marcha. Los scripts y los tests pueden esperar el resultado:

import pyzed as pz
process = pz.Process(["git", "status", "--short"], on_output=print)
process.start()
process.wait() # espera a que termine; devuelve el código de salida
request = pz.HttpRequest("https://example.com")
request.send()
print(request.wait(10).status) # 200

Process.wait(), Worker.wait() y HttpRequest.wait() esperan a que termine el trabajo (con un timeout opcional) y devuelven el código de salida, el resultado o la respuesta.