From 8ad7ac07b1ba1700894101a012ec329b5c5761ae Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Mon, 18 Jan 2010 13:17:35 +0000 Subject: [PATCH] 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 --- terminatorlib/container.py | 27 ++++---------- terminatorlib/paned.py | 13 ++----- terminatorlib/signalman.py | 54 +++++++++++++++++++++++++++ terminatorlib/terminal.py | 1 + terminatorlib/tests/test_doctests.py | 1 + terminatorlib/testsignalman.py | 56 ++++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 29 deletions(-) create mode 100755 terminatorlib/signalman.py create mode 100755 terminatorlib/testsignalman.py diff --git a/terminatorlib/container.py b/terminatorlib/container.py index e9be2d92..a0914bfd 100755 --- a/terminatorlib/container.py +++ b/terminatorlib/container.py @@ -10,6 +10,7 @@ from factory import Factory from config import Config from util import dbg, err from translation import _ +from signalman import Signalman # pylint: disable-msg=R0921 class Container(object): @@ -20,13 +21,13 @@ class Container(object): children = None config = None signals = None - cnxids = None + signalman = None def __init__(self): """Class initialiser""" self.children = [] self.signals = [] - self.cnxids = {} + self.cnxids = Signalman() self.config = Config() def register_signals(self, widget): @@ -49,28 +50,14 @@ class Container(object): err('Container:: registering signal for %s on %s failed' % (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""" - if not self.cnxids.has_key(widget): - self.cnxids[widget] = [] - - 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)) + self.cnxids.new(widget, signal, handler, *args) + return def disconnect_child(self, widget): """De-register the signals for a child""" - if self.cnxids.has_key(widget): - for cnxid in self.cnxids[widget]: - dbg('Container::disconnect_child: removing handler on %s' % - type(widget)) - widget.disconnect(cnxid) - del(self.cnxids[widget]) + self.cnxids.remove_widget(widget) def get_offspring(self): """Return a list of child widgets, if any""" diff --git a/terminatorlib/paned.py b/terminatorlib/paned.py index cbc5c1c4..914acec2 100755 --- a/terminatorlib/paned.py +++ b/terminatorlib/paned.py @@ -37,8 +37,7 @@ class Paned(Container): dbg("Paned::set_initial_position: Setting position to: %d" % position) self.set_position(position) - self.disconnect(self.cnxids['init']) - del(self.cnxids['init']) + self.cnxids.remove_signal(self, 'expose-event') # pylint: disable-msg=W0613 def split_axis(self, widget, vertical=True, sibling=None): @@ -78,7 +77,6 @@ class Paned(Container): else: raise ValueError('Paned widgets can only have two children') - self.cnxids[widget] = [] if maker.isinstance(widget, 'Terminal'): top_window = get_top_window(self) @@ -92,8 +90,7 @@ class Paned(Container): for signal in signals: self.connect_child(widget, signal, signals[signal]) - self.connect_child(widget, 'maximise', top_window.zoom, - False) + self.connect_child(widget, 'maximise', top_window.zoom, False) widget.grab_focus() @@ -176,8 +173,7 @@ class HPaned(Paned, gtk.HPaned): Paned.__init__(self) gtk.HPaned.__init__(self) self.register_signals(HPaned) - self.cnxids['init'] = self.connect('expose-event', - self.set_initial_position) + self.cnxids.new(self, 'expose-event', self.set_initial_position) class VPaned(Paned, gtk.VPaned): """Merge gtk.VPaned into our base Paned Container""" @@ -186,8 +182,7 @@ class VPaned(Paned, gtk.VPaned): Paned.__init__(self) gtk.VPaned.__init__(self) self.register_signals(VPaned) - self.cnxids['init'] = self.connect('expose-event', - self.set_initial_position) + self.cnxids.new(self, 'expose-event', self.set_initial_position) gobject.type_register(HPaned) gobject.type_register(VPaned) diff --git a/terminatorlib/signalman.py b/terminatorlib/signalman.py new file mode 100755 index 00000000..70de7d2a --- /dev/null +++ b/terminatorlib/signalman.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# Terminator by Chris Jones +# 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) + diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index 18b3bc12..8ae2b018 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -97,6 +97,7 @@ class Terminal(gtk.VBox): self.connect('focus-in', self.terminator.focus_changed) self.matches = {} + # FIXME: Port cnxids to using Signalman self.cnxids = {} self.config = Config() diff --git a/terminatorlib/tests/test_doctests.py b/terminatorlib/tests/test_doctests.py index fd964210..ff5165c4 100644 --- a/terminatorlib/tests/test_doctests.py +++ b/terminatorlib/tests/test_doctests.py @@ -11,6 +11,7 @@ def test_suite(): 'cwd', 'factory', 'util', + 'testsignalman', ): suite.addTest(DocTestSuite('terminatorlib.' + name)) return suite diff --git a/terminatorlib/testsignalman.py b/terminatorlib/testsignalman.py new file mode 100755 index 00000000..8aa6971e --- /dev/null +++ b/terminatorlib/testsignalman.py @@ -0,0 +1,56 @@ +#!/usr/bin/python +# Terminator by Chris Jones +# 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)