[PEAK] Trellis on_commit and Performers

Phillip J. Eby pje at telecommunity.com
Sun Oct 5 10:36:03 EDT 2008

At 11:21 AM 10/5/2008 +0300, Sergey Schetinin wrote:
>There needs to be a way to delay layout recalculation for nested
>windows. If the .size if written, we don't know the .client_size yet,
>but we know it will change. Then, when writing to wx we handle the
>immediate event and set the .client_size which allows the nested rules
>to run. But this doesn't match the model of transactions where
>side-effects rules are write-only. I think I'll try to write to wx in
>@maintain rules with on_undo.

That rather sounds like the correct way to do it, as long as there's 
no way for the re-entry events to set conflicting values.  From the 
trellis' POV, the rule that writes to wx and thereby triggers a 
callback that sets some other cell value, might as well be directly 
updating that other value.  And that should work out correctly for 
putting everything into a single transaction.

What's not entirely clear to me is how the freeze/thaw works with 
that...  I think you'd have to set up a non-trellis flag somewhere 
for whether freeze has been called, and then have a performer that 
does the thaw and clears the flag.

>The other order (when we first set it to new value, then it's
>recalculated due to an event) could happen if we write the size to wx
>in the same transaction.

This is actually the bit I don't understand about what you're doing. 
If you set it to a new value *and* it's recalculated due to an event 
in the same transaction, it seems like something is broken in the design.

That is, if the event is firing due to user activity, then how is it 
that you're also programmatically setting the size?  Conversely, if 
the event is firing due to you setting the size, then why not just 
ignore the nested event?  After all, either the nested event will set 
the same size, or if it's a different size, you could just read back 
the value you sent to wx, and then update the cell with that new value.

That is, in the property.fset, write to wx (with undo) and with the 
event effectively disabled.  Then, read the new value, and write 
*that* value to the cell.  ISTM that you could even get that to work 
lazily, using receive().

One simple way to disable all re-entrant event callbacks would be to 
have them check whether a transaction is currently in effect, thereby 
ensuring that only non-programmatic events will ever update the 
trellis.  I'm not sure right off the top of my head whether that's 
something you'd want globally, but I'm wondering where in wx any 
programmatic events are non-local; i.e., where the event tells you 
something you don't already know.

But certainly a size event that only tells you that you just changed 
the size is not necessary, as trellis propagation from the cell in 
question is sufficient to notify the rest of the system.  So, in the 
general case, it's probably best to:

1. Prevent re-entrant events from setting trellis values
2. Handle sets by writing to wx first, reading back the value, and 
then storing it to a @maintain cell

You could also make this "lazy" by using @.connector/@.disconnector 
methods on the @maintain rule.  These methods run in a second recalc 
phase, after a connection or disconnection has occurred, and can 
manipulate trellis values.  Thus you could do something like:

     watching = trellis.attr(False)

     def _value(self):
         if self.watching:
             # code that depends on event
         # code that doesn't

     def _watch(self, sensor):
         self.watching = True

     def _unwatch(self, sensor, key):
         self.watching = False

     def set_value(self, value):
         # write to wx, with undo
         self._value = # value read from wx

     value = property(lambda self: self._value, set_value)

Of course, this approach assumes that the watched event is ignored 
when it occurs inside a transaction (i.e., programmatically).  Also, 
the "write to wx" needs to handle the freeze/thaw stuff already 
mentioned above.

More information about the PEAK mailing list