[PEAK] Trellis on_commit and Performers

Sergey Schetinin maluke at gmail.com
Sat Oct 4 09:14:41 EDT 2008


>> 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.

This I seem to be unable to get right. I approached it by having
Component for each instance that stores the written value in
.pending_write and schedules the actual write in a performer. The rule
used to read the value checks if there's a value pending and returns
that if there is. Then, if there is a relevant event it should discard
the written value and return the actual value, which may be different.
Also, if the rule wasn't subscribed to, when it's next read it should
also discard the written value and reread it from wx. Whatever I try
it's always broken in some of the cases. Any advice is much
appreciated.

Here's a bit of code that I currently have:

# WxActiveAttr is a read-write attribute implementation for fields for
which wx can notify about changes

class WxActiveAttr(WxAttrBase):
    # this gets called by __get__
    def get(self):
        return self.value

    # this gets called by __set__
    @trellis.modifier
    def set(self, value):
        self.pending_write = self.attr.encode(value)


    @trellis.compute
    def value(self):
        # this creates a chain of dependency to a sensor that tracks
EVT_SIZE events (with resetting_to=None)
        # this part works fine
        evt = getattr(self.ob, self.attr.event_name)
        if evt is not None:
            self.reset_pending()
            if self.attr.decode_event is not None:
                return self.attr.decode_event(evt)

        if self.pending_write is not NO_WRITE:
            if self.listening:
                # here I try not to return stale data if this rule
wasn't subscribed to
                # (which wouldn't give the event handler a chance to
reset .pending_write)
                return self.pending_write

        self.reset_pending()
        return self._get() # _get() read the actual wx value

    @property
    def listening(self):
        return self.__cells__['value'].listening is not trellis.NOT_GIVEN

    # ?? what should be here?
    def reset_pending(self):
        pass
        #wxevents.schedule(setattr, self, 'pending_write', NO_WRITE)
        #trellis.on_commit(setattr, self, 'pending_write', NO_WRITE)

    pending_write = trellis.attr(NO_WRITE)


    @trellis.perform
    def perform_set(self):
        if self.pending_write is not NO_WRITE:
            # this is effectively wx.CallAfter(..) but batches
multiple schedule() calls into one CallAfter
            wxchanges.schedule(self.do_write, self.pending_write)

    def do_write(self, value):
        manage_freeze(self.ob)
        # this writes the value to wx
        self.wxwrite(value)


If you want to see full code for this part of library, I can try to
clean it from dependencies, but I still think it will clock around
15K.



More information about the PEAK mailing list