From: Thomas H. <th...@py...> - 2003-07-25 17:00:00
|
(This is a repost of a mail sent to the pycrust users list, Patrick told me about this dispatcher list. BTW: dispatcher is a great module, and I wish you well with the sf project. But expect further questions or usage reports from me, I have at least one.) I hope this forum is the correct one to ask questions about Patrick's dispatcher module ;-) I'm using an old and slightly patched version of the dispatcher module to 'connect' models to widgets in my program. The model instances in my program trigger certain events by calling dispatcher.send(signal=(self._name, "show"), visible=self._active) The frame creates zero, one or more widgets which should display the 'state' of the model, and the frame connects the widgets to the models: def on_create(self, widget): name = widget.label dispatcher.connect(signal=(name, "show"), receiver=widget.show)) The above code arranges that the widget is made visible when the model becomes active, and invisible when the model becomes inactive. The above code works very well, I don't need to disconnect the widgets from the model when they are destroyes, dispatcher does this automatically with it's weak references. The problem occurs when the event handling become more complicated, and cannot simply be done by a simple (bound) method call in a widget instance. What I want to achive is something like this: def on_create(self, widget): name = widget.label import registry model = registry.get(name) def handler(model, widget=widget): data = model.get_some_data() widget.show_value(data) dispatcher.connect(signal=(name, "changed"), receiver=handler) In other words, the handler for the event must do an operation on the model for some data, and then call a method on the widget. (The 'changed' event in the above code has the model as a parameter to make this possible). This code doesn't work, because when the the on_create method returns, the handler function is destroyed, and dispatcher drops the connection. One possibility would be to assign handler to widget as an attribute so that its lifetime is the same as the widget, but this is ugly. And it has a further consequence: because there is a reference cycle between the handler function and the widget, the widget is not destroyed any more (before the next gc.collect() run). Equally ugly would be to make the handler function a method of the widget class, because this would break the separation between the model and the widget. Any ideas, anyone? Is this a common problem? Thomas |
From: Mike C. F. <mcf...@ro...> - 2003-07-31 23:46:27
|
Thomas Heller wrote: ... >I hope this forum is the correct one to ask questions about Patrick's >dispatcher module ;-) > Yup, this is the place. Though I'm rather busy these days, so there may be considerable delays in response from me particularly. >The problem occurs when the event handling become more complicated, and >cannot simply be done by a simple (bound) method call in a widget >instance. > >What I want to achive is something like this: > > def on_create(self, widget): > name = widget.label > import registry > model = registry.get(name) > > def handler(model, widget=widget): > data = model.get_some_data() > widget.show_value(data) > > dispatcher.connect(signal=(name, "changed"), > receiver=handler) > >In other words, the handler for the event must do an operation on the >model for some data, and then call a method on the widget. (The >'changed' event in the above code has the model as a parameter to make >this possible). > If I understand correctly, what you want to do is to have the handler function live a life of its own (not be collected) until the widget goes out of scope, at which point you want to drop the handler function. class Holder( object ): def __init__( self, method, client ): self.method= method # strong reference, kept alive self.client = saferef.safeRef( client, onDelete = self.cleanup ) def __call__( self, *args, **named ): if self.client: client = self.client() if client is not None: return self.method( *args, **named ) def cleanup( self, oldWeakRef ): self.client = None dispatcher.disconnect( self ) I haven't tested that, but it should outline the basic approach. You use weak=False when registering the Holder instance, and the holder takes care of managing the lifetime of the method (keeps a strong ref until the client widget goes away). >Any ideas, anyone? Is this a common problem? > >Thomas > > Don't know how common it is, I've seen similar patterns in a few cases, but I do a lot of meta-programming. HTH, Mike _______________________________________ Mike C. Fletcher Designer, VR Plumber, Coder http://members.rogers.com/mcfletch/ |
From: Thomas H. <th...@py...> - 2003-08-13 08:52:33
|
"Mike C. Fletcher" <mcf...@ro...> writes: > Thomas Heller wrote: > ... > >>I hope this forum is the correct one to ask questions about Patrick's >>dispatcher module ;-) >> > Yup, this is the place. Though I'm rather busy these days, so there > may be considerable delays in response from me particularly. > >>The problem occurs when the event handling become more complicated, and >>cannot simply be done by a simple (bound) method call in a widget >>instance. >> >>What I want to achive is something like this: >> >> def on_create(self, widget): >> name = widget.label >> import registry >> model = registry.get(name) >> def handler(model, widget=widget): >> data = model.get_some_data() >> widget.show_value(data) >> >> dispatcher.connect(signal=(name, "changed"), >> receiver=handler) >> >>In other words, the handler for the event must do an operation on the >>model for some data, and then call a method on the widget. (The >>'changed' event in the above code has the model as a parameter to make >>this possible). >> > If I understand correctly, what you want to do is to have the handler > function live a life of its own (not be collected) until the widget > goes out of scope, at which point you want to drop the handler > function. > > class Holder( object ): > def __init__( self, method, client ): > self.method= method # strong reference, kept alive > self.client = saferef.safeRef( client, onDelete = self.cleanup ) > def __call__( self, *args, **named ): > if self.client: > client = self.client() > if client is not None: > return self.method( *args, **named ) > def cleanup( self, oldWeakRef ): > self.client = None > dispatcher.disconnect( self ) > > I haven't tested that, but it should outline the basic approach. You > use weak=False when registering the Holder instance, and the holder > takes care of managing the lifetime of the method (keeps a strong ref > until the client widget goes away). Mike, thanks for this suggestion. It didn't work exactly as shown above, but it showed me the way to do it. I assume you meant self.client = weakref.ref(client, self.cleanup) instead of self.client = saferef.safeRef( client, onDelete = self.cleanup ) or did I miss something. And sorry for the late reply. Thomas |
From: Mike C. F. <mcf...@ro...> - 2003-08-13 11:07:56
|
Thomas Heller wrote: >"Mike C. Fletcher" <mcf...@ro...> writes: > > ... >>class Holder( object ): >> def __init__( self, method, client ): >> self.method= method # strong reference, kept alive >> self.client = saferef.safeRef( client, onDelete = self.cleanup ) >> >> ... >Mike, thanks for this suggestion. It didn't work exactly as shown above, >but it showed me the way to do it. I assume you meant > self.client = weakref.ref(client, self.cleanup) >instead of > self.client = saferef.safeRef( client, onDelete = self.cleanup ) >or did I miss something. > Probably that I'm using the latest version of pydispatcher (the package) from sourceforge. It splits out the various pieces of functionality into seperate reusable modules, so that there's a saferef module which does all the "safe reference to a method" stuff from the dispatcher module. That way if the client object is itself a method of an object it won't just disappear when the function that accessed it exits. There's also a robustapply module which does all the "figure out which arguments apply to this callable object and call it with that subset of the passed arguments" stuff. The dispatcher module itself also has a few new convenience functions: def getAllReceivers( sender = Any, signal = Any ): """Get list of all receivers from global tables def liveReceivers(receivers): """Filter sequence of receivers to get resolved, live receivers def sendExact( signal=Any, sender=Anonymous, *arguments, **named ): """Send signal only to those receivers registered for exact message def getReceivers( sender = Any, signal = Any ): """Get list of receivers from global tables >And sorry for the late reply. > No problem, I'm far too busy these days myself. Glass houses and stones and all :) . Have fun, Mike _______________________________________ Mike C. Fletcher Designer, VR Plumber, Coder http://members.rogers.com/mcfletch/ |