[PEAK] Trellis on_commit and Performers
Phillip J. Eby
pje at telecommunity.com
Sat Oct 4 12:22:48 EDT 2008
At 04:14 PM 10/4/2008 +0300, Sergey Schetinin wrote:
> >> In my case there's a generic Frame class. The app issues a command to
> >> create a wx backend for it which also does the same for its children.
> >> After that happens the generic implementation is switched to read /
> >> write the backend properties (which are Trellis managed as well). So
> >> if something depends on Frame.size the chain of dependencies will make
> >> it subscribe to EVT_SIZE events. So, if we have a layout manager
> >> working on these generic instances, once the backend is created it
> >> will detect actual sizes of windows and work with that.
> >>
> >> The layout is implemented as a @maintain rule. It couldn't work in the
> >> same transaction as @perform's that create and initialize the backend.
> >
> > I still don't follow. However, if you're saying that you need this because
> > the wx changes you're making in the performer cause a recursive wx event to
> > fire, then the solution is to forward-schedule the wx API call(s) so they
> > don't happen in the same wx event. That is, push the wx stuff into a new
> > transaction, rather than the trellis changes.
> >
> > (I don't know if this is actually the issue you're having; if not, please
> > explain in more detail or show code.)
>
>The issue seems to be more complex, so let me start by explaining one
>of the smaller problems I have. Let's forget about the generic layer
>for now. On the lower level there's a Component that wraps wx.Window
>to make it more Trellis friendly. For example I want it to have an
>attribute .size that can be read, written and depended on. This by
>itself works fine, but I want the writes to trigger the dependencies
>immediately, before writing to underlying window. This is important so
>that layout rules for nested windows can all run in one transaction.
Hm. Well, if you didn't need the EVT_SIZE subscription to be lazy,
I'd have a solution. Just use a @maintain rule for ``value``, and
have your performer check the cell object's ``was_set`` attribute to
know if it was written to (as opposed to computed). You could then
drop all the pending_write and listening attribute stuff.
You might try that simple approach first, and see if you can get away
with it. The downside is that checking ``was_set`` creates a
dependency, and thus the ``value`` rule could not be lazy. So, it
would always be subscribed to EVT_SIZE, but everything else would be simpler.
When I was originally designing Sensor and Effector, I was thinking
that Effector needed some kind of "on write" call, but ended up
dropping it because it seemed that you could just use
``was_set``. Now I see that using ``was_set`` keeps the Effector
from being able to be lazy.
What's really needed here is a lazy Effector with a ``write()``
callback. However, since such a thing does not exist, I think the
simplest thing is to wrap the value cell in a property whose write
method schedules the wx-level update. Something like:
@trellis.compute
def _value(self)
evt = getattr(self.ob, self.attr.event_name)
if evt is not None:
if self.attr.decode_event is not None:
return self.attr.decode_event(evt)
return self._get()
def get_value(self):
return self._value
def set_value(self, value):
if not trellis.ctrl.active:
return trellis.atomically(self.set_value, value)
self.__cells__['_value'].receive(value) # hack!
trellis.on_commit(wxchanges.schedule, self.do_write, value)
value = property(get_value, set_value)
I think this does what you want. But it's a bit of a hack, though,
because the assumption underlying receive() is that it is being
called from the external side, not the trellis side of things.
This means that if you write ``value`` during the same transaction as
anything that causes ``_value`` to be recalculated, then the
recalculated value will take precedence over the written value... I
think. IOW, any rules that depend on the value will get re-run, and
the explicitly set value will be ignored in this transaction (but set
in wx post-transaction).
However, if there is no outside event and you set a value, then that
value will be seen by other rules in this transaction, and then
copied to the outside world.
I think that's what your original code was trying to do, though, so
maybe this is exactly what you want. However, if setting the value
in wx doesn't cause an event to come back, you need to make sure that
do_write() sets .value, too, in order to update things.
This whole scenario is making my head spin, though, so I'm starting
to wonder if anything I'm saying makes sense at all. :)
More information about the PEAK
mailing list