[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)
@trellis.maintain
def _value(self):
if self.watching:
# code that depends on event
# code that doesn't
@_value.connector
def _watch(self, sensor):
self.watching = True
@_value.discconnector
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