[pygobject] GTK overrides: Make connect_signals handle tuple



commit e0084e7e73845fa2a2da29017d3622f361f11dfb
Author: Cole Robinson <crobinso redhat com>
Date:   Sat Feb 16 17:26:43 2013 -0500

    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

 gi/overrides/Gtk.py         |   51 ++++++++----
 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 60bd757..5899596 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 fdb3ccb..0d28540 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)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]