[pygtk] Signal Blocking

Shandy Brown sjbrown at vmware.com
Fri Nov 30 05:04:34 WST 2007


I found the error.  I was trying to block the signal on the wrong
widget.  This change fixes it:

#-----------------------------------------------------------------------------
def SignalLoopProtected( widgetName, signalName ):
    '''This decorator ensures that a loop won't be created from a callback
    continually calling itself in a loop, or a loop between a pair of callbacks
    that call each other.  It takes the callback, "origFunc" and wraps it
    in calls to handler_block and handler_unblock.

    Requirements:
    a handler ID has to be created and stored in the instance.  It has to be
    accessible by calling GetHandlerID(widgetName, signalName)
    '''
    def Decorated( origFunc ):
        def NewFunc( self, widget, *args ):
            assert hasattr(widget, 'handler_block') \
               and hasattr(self,'GetHandlerID'), \
               'SignalLoopProtected can only decorate GTK callback funcs that'+\
               ' can be retrieved from self.GetHandlerID(widgetName,signalName)'
            foreignWidget, handlerID = self.GetHandlerID(widgetName, signalName)
            print "++ Blocking signal %s id: %d" % (signalName, handlerID)
            foreignWidget.handler_block( handlerID )
            rc = origFunc( self, widget, *args )
            print '-- Unblocking signal %s id: %d' % (signalName, handlerID)
            foreignWidget.handler_unblock( handlerID )
            return rc
        NewFunc.func_name = origFunc.func_name
        return NewFunc
    return Decorated

#-----------------------------------------------------------------------------
class GladeInspectingAutoconnecter(object):
    '''Base class that inspects Glade XML and autoconnects
    matching names to the instance

    Provides Autoconnect(), which should be called at the end of __init__,
    and GetHandlerID, which is useful for the above decorator,
    SignalLoopProtected.
    '''
    # Regular expression to match handler method names patterns
    # On_widget__signal and After_widget__signal.  Note that we use two
    # underscores between the Glade widget name and the signal name.
    handler_re = re.compile(r'(On|After)_(.*)__(.*)', re.IGNORECASE)
    def Autoconnect(self, xml):
        """Connects signal handling methods to Glade widgets.
        Methods named like On_widget__signal or After_widget__signal
        are connected to the appropriate widgets and signals.
        """
        self._handlerIDs = {}
        for attr in dir(self):
            match = self.handler_re.match(attr)
            if not match:
                continue
            when, widgetName, signalName = match.groups()
            method = getattr(self, attr)
            assert callable(method), 'Instance attr looks like method name'
            widget = xml.get_widget(widgetName)
            if not widget:
                continue
            handlerID = widget.connect(signalName.replace('_', '-'), method)
            self._handlerIDs[(widgetName, signalName)] = (widget, handlerID)

        print 'All handlers found:'
        for k,v in self._handlerIDs.items():
            print "%40s : %5s" % (k,v)

    def GetHandlerID( self, widgetName, signalName ):
        return self._handlerIDs[(widgetName, signalName)]


On Wed, 2007-11-28 at 11:34 -0800, Shandy Brown wrote:
> I didn't get a response to this question regarding the right way to
> block a signal.  I have reworked my example to be much much smaller so
> that hopefully it garners some eyeballs.
> 
> Again, if my approach is fundamentally wrong, please let me know.
> 
> To reiterate the problem: I basically want two widgets to affect each
> other's values.  But if I do this naievely, like so...
> 
> Spinbutton
>   on_value_changed:
>      Entry.set_text()
> Entry
>   on_changed:
>      Spinbutton.set_value()
> 
> That causes an infinite loop.  My idea is to block the signals before
> calling set_text() or set_value(), but, as the attached example will
> show, I get an error (min_clock.py:29: Warning: gsignal.c:1695: instance
> `0xb25840' has no handler with id `46')
> 
> Any guidance would be appreciated.
> 
> Shandy
> 
> On Tue, 2007-11-20 at 11:03 -0800, Shandy Brown wrote:
> > I'm trying to write a clock widget.  I want it to look nice and to
> > behave in a friendly manner.
> > 
> > * It should show colons between the hours, minutes, and seconds
> > * typing the time should "skip" over the colons
> > 
> > I've tried to implement this by 5 sequential text entry widgets, each
> > without a frame so they look like they're part of the same widget, laid
> > out like this:
> > 
> > +----+---+----+---+----+
> > | hh | : | mm | : | ss |
> > +----+---+----+---+----+
> > 
> > if the time is 10:20, I want focus to default to the hours, and the user
> > can type in "1020".
> > 
> > The best way I can think of doing this is by detecting that on the
> > keypress for "2", the number is out of bounds, then blocking the signals
> > for the minute text entry, and setting it's value to 2.
> > 
> > However, sometimes when I try to block the signals, I get the message 
> > 
> >  Warning: gsignal.c:1695: instance `0xaf6800' has no handler with id `115'
> > 
> > Which doesn't make sense to me, because I 115 definitely exists, as I
> > stored it previously.
> > 
> > I've attached the code which will exhibit the behaviour.  Just type
> > "python clock.py"
> > 
> > Any suggestions?  Anyone know why signal 115 disappears?
> > 
> > -sjbrown
> > _______________________________________________
> > pygtk mailing list   pygtk at daa.com.au
> > http://www.daa.com.au/mailman/listinfo/pygtk
> > Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
> _______________________________________________
> pygtk mailing list   pygtk at daa.com.au
> http://www.daa.com.au/mailman/listinfo/pygtk
> Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/



More information about the pygtk mailing list