diff options
author | Cole Robinson <crobinso@redhat.com> | 2013-02-16 17:26:43 -0500 |
---|---|---|
committer | Simon Feltman <sfeltman@src.gnome.org> | 2013-07-03 04:40:47 -0700 |
commit | e0084e7e73845fa2a2da29017d3622f361f11dfb (patch) | |
tree | fab1277f940f9771a5ba1b9d0081d28f7ea84a40 | |
parent | 466567373289e6f141709f08efa80ba588d3d64a (diff) | |
download | pygobject-e0084e7e73845fa2a2da29017d3622f361f11dfb.tar.gz |
GTK overrides: Make connect_signals handle tuple
This is used for passing extra arguments to callbacks during
signal emission in the form of:
builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)})
Co-Authored-By: Simon Feltman <sfeltman@src.gnome.org>
https://bugzilla.gnome.org/show_bug.cgi?id=693994
-rw-r--r-- | gi/overrides/Gtk.py | 51 | ||||
-rw-r--r-- | tests/test_overrides_gtk.py | 196 |
2 files changed, 165 insertions, 82 deletions
diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py index 60bd7575..58995969 100644 --- a/gi/overrides/Gtk.py +++ b/gi/overrides/Gtk.py @@ -19,6 +19,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +import collections import sys from gi.repository import GObject from ..overrides import override, strip_boolean_result @@ -357,32 +358,52 @@ __all__.append('MenuItem') class Builder(Gtk.Builder): + @staticmethod + def _extract_handler_and_args(obj_or_map, handler_name): + handler = None + if isinstance(obj_or_map, collections.Mapping): + handler = obj_or_map.get(handler_name, None) + else: + handler = getattr(obj_or_map, handler_name, None) - def connect_signals(self, obj_or_map): - def _full_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map): - handler = None - if isinstance(obj_or_map, dict): - handler = obj_or_map.get(handler_name, None) - else: - handler = getattr(obj_or_map, handler_name, None) + if handler is None: + raise AttributeError('Handler %s not found' % handler_name) + + args = () + if isinstance(handler, collections.Sequence): + if len(handler) == 0: + raise TypeError("Handler %s tuple can not be empty" % handler) + args = handler[1:] + handler = handler[0] - if handler is None: - raise AttributeError('Handler %s not found' % handler_name) + elif not _callable(handler): + raise TypeError('Handler %s is not a method, function or tuple' % handler) - if not _callable(handler): - raise TypeError('Handler %s is not a method or function' % handler_name) + return handler, args + + def connect_signals(self, obj_or_map): + """Connect signals specified by this builder to a name, handler mapping. + + Connect signal, name, and handler sets specified in the builder with + the given mapping "obj_or_map". The handler/value aspect of the mapping + can also contain a tuple in the form of (handler [,arg1 [,argN]]) + allowing for extra arguments to be passed to the handler. For example: + builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)}) + """ + def _full_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map): + handler, args = self._extract_handler_and_args(obj_or_map, handler_name) after = flags & GObject.ConnectFlags.AFTER if connect_obj is not None: if after: - gobj.connect_object_after(signal_name, handler, connect_obj) + gobj.connect_object_after(signal_name, handler, connect_obj, *args) else: - gobj.connect_object(signal_name, handler, connect_obj) + gobj.connect_object(signal_name, handler, connect_obj, *args) else: if after: - gobj.connect_after(signal_name, handler) + gobj.connect_after(signal_name, handler, *args) else: - gobj.connect(signal_name, handler) + gobj.connect(signal_name, handler, *args) self.connect_signals_full(_full_callback, obj_or_map) diff --git a/tests/test_overrides_gtk.py b/tests/test_overrides_gtk.py index fdb3ccbc..0d28540c 100644 --- a/tests/test_overrides_gtk.py +++ b/tests/test_overrides_gtk.py @@ -156,73 +156,6 @@ class TestGtk(unittest.TestCase): mi = ui.get_widget("/menubær1") self.assertEqual(type(mi), Gtk.MenuBar) - def test_builder(self): - self.assertEqual(Gtk.Builder, gi.overrides.Gtk.Builder) - - class SignalTest(GObject.GObject): - __gtype_name__ = "GIOverrideSignalTest" - __gsignals__ = { - "test-signal": (GObject.SignalFlags.RUN_FIRST, - None, - []), - } - - class SignalCheck: - def __init__(self): - self.sentinel = 0 - self.after_sentinel = 0 - - def on_signal_1(self, *args): - self.sentinel += 1 - self.after_sentinel += 1 - - def on_signal_3(self, *args): - self.sentinel += 3 - - def on_signal_after(self, *args): - if self.after_sentinel == 1: - self.after_sentinel += 1 - - signal_checker = SignalCheck() - builder = Gtk.Builder() - - # add object1 to the builder - builder.add_from_string(""" -<interface> - <object class="GIOverrideSignalTest" id="object1"> - <signal name="test-signal" after="yes" handler="on_signal_after" /> - <signal name="test-signal" handler="on_signal_1" /> - </object> -</interface> -""") - - # only add object3 to the builder - builder.add_objects_from_string(""" -<interface> - <object class="GIOverrideSignalTest" id="object2"> - <signal name="test-signal" handler="on_signal_2" /> - </object> - <object class="GIOverrideSignalTest" id="object3"> - <signal name="test-signal" handler="on_signal_3" /> - </object> - <object class="GIOverrideSignalTest" id="object4"> - <signal name="test-signal" handler="on_signal_4" /> - </object> -</interface> -""", ['object3']) - - # hook up signals - builder.connect_signals(signal_checker) - - # call their notify signals and check sentinel - objects = builder.get_objects() - self.assertEqual(len(objects), 2) - for obj in objects: - obj.emit('test-signal') - - self.assertEqual(signal_checker.sentinel, 4) - self.assertEqual(signal_checker.after_sentinel, 2) - def test_window(self): # standard Window w = Gtk.Window() @@ -657,6 +590,135 @@ class TestGtk(unittest.TestCase): @unittest.skipUnless(Gtk, 'Gtk not available') +class TestBuilder(unittest.TestCase): + class SignalTest(GObject.GObject): + __gtype_name__ = "GIOverrideSignalTest" + __gsignals__ = { + "test-signal": (GObject.SignalFlags.RUN_FIRST, + None, + []), + } + + def test_extract_handler_and_args_object(self): + class Obj(): + pass + + obj = Obj() + obj.foo = lambda: None + + handler, args = Gtk.Builder._extract_handler_and_args(obj, 'foo') + self.assertEqual(handler, obj.foo) + self.assertEqual(len(args), 0) + + def test_extract_handler_and_args_dict(self): + obj = {'foo': lambda: None} + + handler, args = Gtk.Builder._extract_handler_and_args(obj, 'foo') + self.assertEqual(handler, obj['foo']) + self.assertEqual(len(args), 0) + + def test_extract_handler_and_args_with_seq(self): + obj = {'foo': (lambda: None, 1, 2)} + + handler, args = Gtk.Builder._extract_handler_and_args(obj, 'foo') + self.assertEqual(handler, obj['foo'][0]) + self.assertSequenceEqual(args, [1, 2]) + + def test_extract_handler_and_args_no_handler_error(self): + obj = dict(foo=lambda: None) + self.assertRaises(AttributeError, + Gtk.Builder._extract_handler_and_args, + obj, 'not_a_handler') + + def test_builder_with_handler_and_args(self): + builder = Gtk.Builder() + builder.add_from_string(""" + <interface> + <object class="GIOverrideSignalTest" id="object_sig_test"> + <signal name="test-signal" handler="on_signal1" /> + <signal name="test-signal" handler="on_signal2" after="yes" /> + </object> + </interface> + """) + + args_collector = [] + + def on_signal(*args): + args_collector.append(args) + + builder.connect_signals({'on_signal1': (on_signal, 1, 2), + 'on_signal2': on_signal}) + + objects = builder.get_objects() + self.assertEqual(len(objects), 1) + obj, = objects + obj.emit('test-signal') + + self.assertEqual(len(args_collector), 2) + self.assertSequenceEqual(args_collector[0], (obj, 1, 2)) + self.assertSequenceEqual(args_collector[1], (obj, )) + + def test_builder(self): + self.assertEqual(Gtk.Builder, gi.overrides.Gtk.Builder) + + class SignalCheck: + def __init__(self): + self.sentinel = 0 + self.after_sentinel = 0 + + def on_signal_1(self, *args): + self.sentinel += 1 + self.after_sentinel += 1 + + def on_signal_3(self, *args): + self.sentinel += 3 + + def on_signal_after(self, *args): + if self.after_sentinel == 1: + self.after_sentinel += 1 + + signal_checker = SignalCheck() + builder = Gtk.Builder() + + # add object1 to the builder + builder.add_from_string(""" +<interface> + <object class="GIOverrideSignalTest" id="object1"> + <signal name="test-signal" after="yes" handler="on_signal_after" /> + <signal name="test-signal" handler="on_signal_1" /> + </object> +</interface> +""") + + # only add object3 to the builder + builder.add_objects_from_string(""" +<interface> + <object class="GIOverrideSignalTest" id="object2"> + <signal name="test-signal" handler="on_signal_2" /> + </object> + <object class="GIOverrideSignalTest" id="object3"> + <signal name="test-signal" handler="on_signal_3" /> + </object> + <object class="GIOverrideSignalTest" id="object4"> + <signal name="test-signal" handler="on_signal_4" /> + </object> +</interface> +""", ['object3']) + + # hook up signals + builder.connect_signals(signal_checker) + + # call their notify signals and check sentinel + objects = builder.get_objects() + self.assertEqual(len(objects), 2) + for obj in objects: + obj.emit('test-signal') + + self.assertEqual(signal_checker.sentinel, 4) + self.assertEqual(signal_checker.after_sentinel, 2) + + +@unittest.skipUnless(Gtk, 'Gtk not available') class TestTreeModel(unittest.TestCase): def test_tree_model_sort(self): self.assertEqual(Gtk.TreeModelSort, gi.overrides.Gtk.TreeModelSort) |