summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCole Robinson <crobinso@redhat.com>2013-02-16 17:26:43 -0500
committerSimon Feltman <sfeltman@src.gnome.org>2013-07-03 04:40:47 -0700
commite0084e7e73845fa2a2da29017d3622f361f11dfb (patch)
treefab1277f940f9771a5ba1b9d0081d28f7ea84a40
parent466567373289e6f141709f08efa80ba588d3d64a (diff)
downloadpygobject-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.py51
-rw-r--r--tests/test_overrides_gtk.py196
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)