Hello James,

sorry for hoarding patches/ideas... this is the last one.

GtkObject instances have a n to 1 relation to gtk objects. A signal
handler function get's a different class instance each time its
called, for the same gtk object (it has been discussed before on this
list).

Code like this does not work:

----------------------
def callback(o):
        print o.msg

w = GtkWindow()
w.connect("destroy",callback)
w.msg = "destroy"
w.destroy()
----------------------

A way to make it work consists of following code fragment. The idea is
to put a reference to the python class instance in the gtk object
data, to use this data whenever a class instance has to be made from
this gtk object, and to remove this reference when the object is
destroyed (to break the circular reference). It wouldn't work for
"destroy" Signal handlers which are added after our own handler, so in
this case is catched and our handler is moved to the end of the list
again.

-----------------------
destroy_ref_id = intern("python-obj-destroy") # key signal handler id in
                                              # GtkObject instance dict
                                              # "strange" name to avoid
                                              # conflicts with normal names

instance_ref = "python-obj-instance"          # key for gtk object data

def unref_gtkinstance(o):
        "remove the reference to the python class instance"
        _gtk.gtk_object_remove_data(o,instance_ref)
        if hasattr(o,destroy_ref_id):
                _gtk.gtk_signal_disconnect(o._o,getattr(o,destroy_ref_id))
                delattr(o,destroy_ref_id)

class GtkObject:
...
        def bind(self):
                """put a reference to the GtkObject instance into the
                gtk object data"""
                self.set_data(instance_ref,self)
                setattr(self,destroy_ref_id,
                        _gtk.gtk_signal_connect(self._o,
                                                "destroy",unbind_gtkobject))
        def unbind(self):
                unbind_gtkobject(self._o)
        def _set_o(self,obj):
                self._o = obj;
                self.bind()
        def __init__(self, _obj=None):
                if _obj:
                        self._set_o(_obj)
...
        def connect(self, name, f, *extra):
                callback = self.__cnv(f)
                id = _gtk.gtk_signal_connect(self._o, name,
                                             callback.__call__, extra)
                if name == "destroy":
                        if hasattr(self,destroy_ref_id):
                                _gtk.gtk_signal_disconnect(
                                        self._o,getattr(self,destroy_ref_id))
                        setattr(self,destroy_ref_id,
                                _gtk.gtk_signal_connect(
                                self._o,"destroy",unbind_gtkobject))
                return id
...
def _obj2inst(obj):
        o = _gtk.gtk_object_get_data(obj,instance_ref)
        if o != None:
                return o
...
-----------------------

Each assignment to self._o has to be changed into a call to
self._set_o(). I have tested this idea and it works (there may be some 
typos in the mail because I changed names etc).

The drawback of this change is additional runtim overhead (destroy
signal, setting/removing object data) and storage requirement (a bit
more memory for each GtkObject instance, and more GtkObject instances
since now there is always one instance for each non-destroyed gtk
object that has been in use by pygtk).

But on the other hand IMHO it gives a more consistent programming
model. Full patch is available ;)

ciao
Andreas
To unsubscribe: echo "unsubscribe" | mail [EMAIL PROTECTED]

Reply via email to