[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