Create a new class Signalman which is used to track the glib signals on a widget since at least two of our classes were doing this themselves. Also integrated it into one of those classes, Container() where it is only used by VPaned and HPaned. Also add doctests for it

This commit is contained in:
Chris Jones 2010-01-18 13:17:35 +00:00
parent 8c7218121f
commit 8ad7ac07b1
6 changed files with 123 additions and 29 deletions

View File

@ -10,6 +10,7 @@ from factory import Factory
from config import Config from config import Config
from util import dbg, err from util import dbg, err
from translation import _ from translation import _
from signalman import Signalman
# pylint: disable-msg=R0921 # pylint: disable-msg=R0921
class Container(object): class Container(object):
@ -20,13 +21,13 @@ class Container(object):
children = None children = None
config = None config = None
signals = None signals = None
cnxids = None signalman = None
def __init__(self): def __init__(self):
"""Class initialiser""" """Class initialiser"""
self.children = [] self.children = []
self.signals = [] self.signals = []
self.cnxids = {} self.cnxids = Signalman()
self.config = Config() self.config = Config()
def register_signals(self, widget): def register_signals(self, widget):
@ -49,28 +50,14 @@ class Container(object):
err('Container:: registering signal for %s on %s failed' % err('Container:: registering signal for %s on %s failed' %
(signal['name'], widget)) (signal['name'], widget))
def connect_child(self, widget, signal, handler, data=None): def connect_child(self, widget, signal, handler, *args):
"""Register the requested signal and record its connection ID""" """Register the requested signal and record its connection ID"""
if not self.cnxids.has_key(widget): self.cnxids.new(widget, signal, handler, *args)
self.cnxids[widget] = [] return
if data is not None:
self.cnxids[widget].append(widget.connect(signal, handler, data))
dbg('Container::connect_child: connecting %s(%s) for %s::%s' %
(handler.__name__, data, widget.__class__.__name__, signal))
else:
self.cnxids[widget].append(widget.connect(signal, handler))
dbg('Container::connect_child: registering %s to handle %s::%s' %
(handler.__name__, widget.__class__.__name__, signal))
def disconnect_child(self, widget): def disconnect_child(self, widget):
"""De-register the signals for a child""" """De-register the signals for a child"""
if self.cnxids.has_key(widget): self.cnxids.remove_widget(widget)
for cnxid in self.cnxids[widget]:
dbg('Container::disconnect_child: removing handler on %s' %
type(widget))
widget.disconnect(cnxid)
del(self.cnxids[widget])
def get_offspring(self): def get_offspring(self):
"""Return a list of child widgets, if any""" """Return a list of child widgets, if any"""

View File

@ -37,8 +37,7 @@ class Paned(Container):
dbg("Paned::set_initial_position: Setting position to: %d" % position) dbg("Paned::set_initial_position: Setting position to: %d" % position)
self.set_position(position) self.set_position(position)
self.disconnect(self.cnxids['init']) self.cnxids.remove_signal(self, 'expose-event')
del(self.cnxids['init'])
# pylint: disable-msg=W0613 # pylint: disable-msg=W0613
def split_axis(self, widget, vertical=True, sibling=None): def split_axis(self, widget, vertical=True, sibling=None):
@ -78,7 +77,6 @@ class Paned(Container):
else: else:
raise ValueError('Paned widgets can only have two children') raise ValueError('Paned widgets can only have two children')
self.cnxids[widget] = []
if maker.isinstance(widget, 'Terminal'): if maker.isinstance(widget, 'Terminal'):
top_window = get_top_window(self) top_window = get_top_window(self)
@ -92,8 +90,7 @@ class Paned(Container):
for signal in signals: for signal in signals:
self.connect_child(widget, signal, signals[signal]) self.connect_child(widget, signal, signals[signal])
self.connect_child(widget, 'maximise', top_window.zoom, self.connect_child(widget, 'maximise', top_window.zoom, False)
False)
widget.grab_focus() widget.grab_focus()
@ -176,8 +173,7 @@ class HPaned(Paned, gtk.HPaned):
Paned.__init__(self) Paned.__init__(self)
gtk.HPaned.__init__(self) gtk.HPaned.__init__(self)
self.register_signals(HPaned) self.register_signals(HPaned)
self.cnxids['init'] = self.connect('expose-event', self.cnxids.new(self, 'expose-event', self.set_initial_position)
self.set_initial_position)
class VPaned(Paned, gtk.VPaned): class VPaned(Paned, gtk.VPaned):
"""Merge gtk.VPaned into our base Paned Container""" """Merge gtk.VPaned into our base Paned Container"""
@ -186,8 +182,7 @@ class VPaned(Paned, gtk.VPaned):
Paned.__init__(self) Paned.__init__(self)
gtk.VPaned.__init__(self) gtk.VPaned.__init__(self)
self.register_signals(VPaned) self.register_signals(VPaned)
self.cnxids['init'] = self.connect('expose-event', self.cnxids.new(self, 'expose-event', self.set_initial_position)
self.set_initial_position)
gobject.type_register(HPaned) gobject.type_register(HPaned)
gobject.type_register(VPaned) gobject.type_register(VPaned)

54
terminatorlib/signalman.py Executable file
View File

@ -0,0 +1,54 @@
#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""signalman.py - class providing a glib signal tracker"""
from util import dbg, err
class Signalman(object):
"""Class providing glib signal tracking and management"""
cnxids = None
def __init__(self):
"""Class initialiser"""
self.cnxids = {}
def __del__(self):
"""Class destructor"""
if len(self.cnxids.keys()) > 0:
err('Signals remain. This is likely a bug: %s' % self.cnxids)
def new(self, widget, signal, handler, *args):
"""Register a new signal on a widget"""
if not self.cnxids.has_key(widget):
dbg('creating new bucket for %s' % type(widget))
self.cnxids[widget] = {}
if self.cnxids[widget].has_key(signal):
err('%s already has a handler for %s' % (id(widget), signal))
self.cnxids[widget][signal] = widget.connect(signal, handler, *args)
dbg('connected %s::%s to %s' % (type(widget), signal, handler))
def remove_signal(self, widget, signal):
"""Remove a signal handler"""
if not self.cnxids[widget].has_key(signal):
err('%s not registered for %s' % (signal, type(widget)))
return
dbg('removing %s::%s' % (type(widget), signal))
widget.disconnect(self.cnxids[widget][signal])
del(self.cnxids[widget][signal])
if len(self.cnxids[widget].keys()) == 0:
dbg('no more signals for widget')
del(self.cnxids[widget])
def remove_widget(self, widget):
"""Remove all signal handlers for a widget"""
if not self.cnxids.has_key(widget):
dbg('%s not registered' % widget)
return
signals = self.cnxids[widget].keys()
for signal in signals:
self.remove_signal(widget, signal)

View File

@ -97,6 +97,7 @@ class Terminal(gtk.VBox):
self.connect('focus-in', self.terminator.focus_changed) self.connect('focus-in', self.terminator.focus_changed)
self.matches = {} self.matches = {}
# FIXME: Port cnxids to using Signalman
self.cnxids = {} self.cnxids = {}
self.config = Config() self.config = Config()

View File

@ -11,6 +11,7 @@ def test_suite():
'cwd', 'cwd',
'factory', 'factory',
'util', 'util',
'testsignalman',
): ):
suite.addTest(DocTestSuite('terminatorlib.' + name)) suite.addTest(DocTestSuite('terminatorlib.' + name))
return suite return suite

56
terminatorlib/testsignalman.py Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/python
# Terminator by Chris Jones <cmsj@tenshu.net>
# GPL v2 only
"""testsignalman.py - Test the signalman class
>>> widget = TestWidget()
>>> signalman = Signalman()
>>> signalman.new(widget, 'test1', handler)
>>> signalman.cnxids[widget].keys()
['test1']
>>> widget.signals.values()
['test1']
>>> signalman.remove_widget(widget)
>>> signalman.cnxids.has_key(widget)
False
>>> widget.signals.values()
[]
>>> signalman.new(widget, 'test2', handler)
>>> signalman.new(widget, 'test3', handler)
>>> signalman.remove_signal(widget, 'test2')
>>> signalman.cnxids[widget].keys()
['test3']
>>> widget.signals.values()
['test3']
>>> signalman.remove_widget(widget)
>>>
"""
from signalman import Signalman
class TestWidget():
signals = None
count = None
def __init__(self):
self.signals = {}
self.count = 0
def connect(self, signal, handler, *args):
self.count = self.count + 1
self.signals[self.count] = signal
return(self.count)
def disconnect(self, signalid):
del(self.signals[signalid])
def handler():
print "I am a test handler"
if __name__ == '__main__':
import sys
import doctest
(failed, attempted) = doctest.testmod()
print "%d/%d tests failed" % (failed, attempted)
sys.exit(failed)