Minule jsme už měli horké objekty uvnitř horkých objektů – např. HotList v HotObject, ale když přišlo na to, pověsit si event listenery, tak jsme se dostali do slepé uličky. Připomeňme si, co vlastně děláme. Náš model vypadá logicky takto:
class ProductModel(object): def __init__(self, server): self.server = server self.article = "" self.sn = 0 self.process = [] self.operations = []
ale chceme od něj, aby každá změna hodnot způsobila vyvolání nějaké události.
model.article = "PCB647" # vyvolá událost (event) model.sn = 736 # vyvolá událost (event)To dělám pomocí jakési instrumentace, kvůli které ten objekt vypadá, jak se někdo vyjádřil v komentářích, jako nádor. V minulém díle nám ty objekty sice generovaly události, jenže jaksi každý za sebe. Takže když jsem měl např. HotList v HotObjectu, a změnil se HotList, tak vyšla událost z HotListu a ne z toho HotObjectu. Dnes si to celé upravíme tak, aby objekt znal svého rodiče, a abychom nastavili jeden listener, k kořenu té hierarchie, a tento listener aby používaly i všechny jeho „podobjekty“. Tak jdeme na to: github/modellerkit, step05.
Kromě toho, že objekty budou v hierarchii (což je celkem banální věc), si taky konečně musíme říct, jak bude vypadat to vystřelení události. Musí obsahovat jméno události (jako update, insert, reset), a nějaká data k události. Např. index prvku v poli. Taky budeme potřebovat cestu k objektu v té hierarchii, a vlastně nakonec proč ne i ten objekt. Takže hlavička funkce obsluhující událost by mohla vypadat třeba takto:
def event_handler(model, path, event_name, key) """ model je ten objekt, který způsobil událost path je cesta k modelu v rámci naší hierarchie event_name je jméno eventu key jsou data k události """
Zjednodušeně pak např.:
def event_handler(model, path, event_name, key): if "/process" == path and "reset" == event_name: process_view.Clear() for i in model: process_view.Append(str(i))
Když stavíme UI v desktopových frameworcích (wx, GTK, …), tak si nějak navěsíme na ty události obslužnou funkci, a ta událost pak nějak prochází hierarchií widgetů/ovládacích prvků, a jakmile cestou narazí na patřičnou obslužnou funkci, tak se ta funkce zavolá. V našem modelu to ale máme jinak. My nenavěsíme obslužnou funkci přímo na tu část modelu, která nás zajímá, ale pro celý model máme centrální bod, přes který jdou všechny události, a potom se, pomocí nějakého směrování či mapování, rozhodne, kdo (a jestli vůbec někdo) si událost obslouží. Znamená to samozřejmně, že při generování události si musím nejprve posbírat tu cestu k objektu, který ji vyvolal (path), a potom znovu provést to směrování – tuto cestu (spolu s typem události) si převést na volání konkrétní obslužné funkce.
Vybral jsem si, že cesta bude řetězec znaků. Úplne klidně jsem si ale mohl říct, že to bude pole řetězců, nebo dokonce pole objektů s nějakým významem. Chci, aby se s tím snadno pracovalo, a zatím to směruji k mapování událostí na funkce pomocí jednoduchého porovnávání řetězců tak. Takže mám pomocnou třídu Mapper, která jednoduše namapuje cestu v rámci modelu (spolu se jménem události) na obslužnou funkci. Např. takto:
MAPPER = hotmodel.Mapper() MAPPER.add_route("/process", "reset", handle_process_reset,) MAPPER.add_route("/operations", "update", handle_operations_update,) MAPPER.add_route("/process", "", update_process_counter,) MAPPER.add_route("", "", handle_everything,) MODEL.add_listener(MAPPER.listener)
A vlastní handler pak může vypadat takto:
def update_process_counter(model, fqname, event_name, key): process_counter.SetLabel(str(len(model))
V příštím díle konečně uvidíte ukázku s nějakým UI.
Což o to, tak to samozřejmně jde. Ale pak mám vlastně model a view v jednom, a řekl bych, že tím ztratím dost přehlednosti. Kromě toho, model a view je logicky rozdíl. Zatímco model může být třeba 2 seznamy něčeho, ve view je můžu nějak spojit a zobrazit jako jeden, což se mi s odděleným modelem líbí víc.
Díky za komentář.
Jmenuju se Petr Blahoš. Programuju něco přes 20 let. Tady se snažím psát hlavně o Pythonu, webovém frameworku Pyramid, a občas i o něčem úplně jiném.
Přečteno 19 056×
Přečteno 11 778×
Přečteno 9 177×
Přečteno 8 679×
Přečteno 8 476×