Closed getzze closed 7 months ago
I managed to make it work with a custom signal_group_class
argument. I have to see how it behaves with sub-classing though.
from typing import ClassVar
from attrs import define, field
from psygnal import SignalGroupDescriptor, Signal, SignalGroup, EmissionInfo
ModelSignalGroup = type("ModelSignalGroup", (SignalGroup,), {"name": Signal(str, str)})
@define
class Model:
events: ClassVar[ModelSignalGroup] = SignalGroupDescriptor(
signal_group_class= ModelSignalGroup,
)
_controller: str = field()
_name: str = field(kw_only=True)
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str) -> None:
# Generate a unique name among siblings
siblings = [self.controller]
value = f"{value}_1" if value in siblings else value
self._name = value
@property
def controller(self) -> str:
return self._controller
m = Model("ctt", name="c")
@m.events.connect
def on_any_change(info: EmissionInfo):
print(f"field {info.signal.name!r} changed to {info.args}")
m.name = "ctt"; m.name
# >>> field 'name' changed to ('ctt_1', 'c')
It works with sub-classing, although I have to redefine the events
class attribute for the subclasses.
see #262 and https://github.com/pyapp-kit/psygnal/pull/260#issuecomment-1924217669 for another option.
This can be closed, with #299 merged, it can be done in two ways (that are not exactly equivalent).
With aliases only:
from typing import ClassVar
from attrs import define, field
from psygnal import SignalGroupDescriptor, EmissionInfo
aliases = {"_name": "name", "name": None, "_controller": None}
@define
class Model:
events: ClassVar[SignalGroupDescriptor] = SignalGroupDescriptor(signal_aliases=aliases)
_controller: str = field()
_name: str = field(kw_only=True)
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str) -> None:
# Generate a unique name among siblings
siblings = [self.controller]
value = f"{value}_1" if value in siblings else value
self._name = value
@property
def controller(self) -> str:
return self._controller
m = Model("ctt", name="c")
@m.events.connect
def on_any_change(info: EmissionInfo):
print(f"field {info.signal.name!r} changed to {info.args}")
m.name = "ctt"; m.name
# >> field 'name' changed to ('ctt_1', 'c')
With aliases and a SignalGroup
subclass (thanks to #291, it will work on new fields if subclassing Model
without the need to redefine events
in the subclass):
from typing import ClassVar
from attrs import define, field
from psygnal import SignalGroupDescriptor, Signal, SignalGroup, EmissionInfo
ModelSignalGroup = type("ModelSignalGroup", (SignalGroup,), {"name": Signal(str, str)})
@define
class Model:
events: ClassVar[ModelSignalGroup] = SignalGroupDescriptor(
signal_group_class= ModelSignalGroup,
signal_aliases={"_name": None, "_controller": None}
)
_controller: str = field()
_name: str = field(kw_only=True)
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str) -> None:
# Generate a unique name among siblings
siblings = [self.controller]
value = f"{value}_1" if value in siblings else value
self._name = value
@property
def controller(self) -> str:
return self._controller
m = Model("ctt", name="c")
@m.events.connect
def on_any_change(info: EmissionInfo):
print(f"field {info.signal.name!r} changed to {info.args}")
m.name = "ctt"; m.name
# >>> field 'name' changed to ('ctt_1', 'c')
My problem, I am using
attrs
to make a dataclass with aname
attribute. I want to modify thename
before setting it, but I cannot useattrs.converters
because they are not bound to the instance. So I create a private_name
field and use aname
property to validate the name before setting it. Now I would like to emit a signal whenname
is changed. This is my working example using PR #260.Now the signal is emitted at
self.events._name_changed
but I would like it to beself.events.name_changed
orself.events.name
, i.e. without the "_" prefix because then I will add other signals likeself.events.color
and I want it to be consistent, so all without the "_" prefix.Any idea how I can do that without another PR :) ?