diff options
-rw-r--r-- | gi/_gtktemplate.py | 49 | ||||
-rw-r--r-- | gi/overrides/Gtk.py | 64 | ||||
-rw-r--r-- | gi/pygi-struct-marshal.c | 5 |
3 files changed, 69 insertions, 49 deletions
diff --git a/gi/_gtktemplate.py b/gi/_gtktemplate.py index 15f6b2c5..a631a6eb 100644 --- a/gi/_gtktemplate.py +++ b/gi/_gtktemplate.py @@ -17,26 +17,57 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA +from collections import abc from functools import partial from gi.repository import GLib, GObject, Gio +def _extract_handler_and_args(obj_or_map, handler_name): + handler = None + if isinstance(obj_or_map, abc.Mapping): + 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, abc.Sequence): + if len(handler) == 0: + raise TypeError("Handler %s tuple can not be empty" % handler) + args = handler[1:] + handler = handler[0] + + elif not callable(handler): + raise TypeError('Handler %s is not a method, function or tuple' % handler) + + return handler, args + + def define_builder_scope(): from gi.repository import Gtk class BuilderScope(GObject.GObject, Gtk.BuilderScope): - def __init__(self): + def __init__(self, scope_object=None): super().__init__() + self._scope_object = scope_object def do_create_closure(self, builder, func_name, flags, obj): - current_object = builder.get_current_object() + current_object = builder.get_current_object() or self._scope_object - if func_name not in current_object.__gtktemplate_methods__: - return None + if not self._scope_object: + current_object = builder.get_current_object() + if func_name not in current_object.__gtktemplate_methods__: + return None - current_object.__gtktemplate_handlers__.add(func_name) + current_object.__gtktemplate_handlers__.add(func_name) + handler_name = current_object.__gtktemplate_methods__[func_name] + else: + current_object = self._scope_object + handler_name = func_name swapped = int(flags & Gtk.BuilderClosureFlags.SWAPPED) if swapped: @@ -44,12 +75,12 @@ def define_builder_scope(): "%r not supported" % GObject.ConnectFlags.SWAPPED) return None - handler_name = current_object.__gtktemplate_methods__[func_name] - handler = getattr(current_object, handler_name) + handler, args = _extract_handler_and_args(current_object, handler_name) + if obj: - return partial(handler, swap_data=obj) + return partial(handler, *args, swap_data=obj) - return handler + return partial(handler, *args) return BuilderScope diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py index 875e7c3f..7739751e 100644 --- a/gi/overrides/Gtk.py +++ b/gi/overrides/Gtk.py @@ -21,11 +21,10 @@ import sys import warnings -from collections import abc from gi.repository import GObject from .._ossighelper import wakeup_on_signal, register_sigint_fallback -from .._gtktemplate import Template +from .._gtktemplate import Template, _extract_handler_and_args from ..overrides import (override, strip_boolean_result, deprecated_init, wrap_list_store_sort_func) from ..module import get_introspection_module @@ -43,6 +42,10 @@ __all__ = [] Template = Template __all__.append('Template') +# Exposed for unit-testing. +_extract_handler_and_args = _extract_handler_and_args +__all__.append('_extract_handler_and_args') + if GTK2: warn_msg = "You have imported the Gtk 2.0 module. Because Gtk 2.0 \ was not designed for use with introspection some of the \ @@ -78,33 +81,6 @@ def _construct_target_list(targets): __all__.append('_construct_target_list') -def _extract_handler_and_args(obj_or_map, handler_name): - handler = None - if isinstance(obj_or_map, abc.Mapping): - 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, abc.Sequence): - if len(handler) == 0: - raise TypeError("Handler %s tuple can not be empty" % handler) - args = handler[1:] - handler = handler[0] - - elif not callable(handler): - raise TypeError('Handler %s is not a method, function or tuple' % handler) - - return handler, args - - -# Exposed for unit-testing. -__all__.append('_extract_handler_and_args') - - def _builder_connect_callback(builder, gobj, signal_name, handler_name, connect_obj, flags, obj_or_map): handler, args = _extract_handler_and_args(obj_or_map, handler_name) @@ -480,19 +456,29 @@ def _get_utf8_length(string): class Builder(Gtk.Builder): - def connect_signals(self, obj_or_map): - """Connect signals specified by this builder to a name, handler mapping. + if GTK4: + from .._gtktemplate import define_builder_scope + BuilderScope = define_builder_scope() - 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: + def __init__(self, scope_object_or_map=None): + super(Builder, self).__init__() + if scope_object_or_map: + self.set_scope(Builder.BuilderScope(scope_object_or_map)) - .. code-block:: python + else: + def connect_signals(self, obj_or_map): + """Connect signals specified by this builder to a name, handler mapping. - builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)}) - """ - self.connect_signals_full(_builder_connect_callback, obj_or_map) + 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: + + .. code-block:: python + + builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)}) + """ + self.connect_signals_full(_builder_connect_callback, obj_or_map) def add_from_string(self, buffer): if not isinstance(buffer, str): diff --git a/gi/pygi-struct-marshal.c b/gi/pygi-struct-marshal.c index 13715888..0e190b8e 100644 --- a/gi/pygi-struct-marshal.c +++ b/gi/pygi-struct-marshal.c @@ -193,16 +193,19 @@ pygi_arg_gclosure_from_py_marshal (PyObject *py_arg, if (partial && PyObject_IsInstance (py_arg, partial) > 0) { PyObject *partial_func; + PyObject *partial_args; PyObject *partial_keywords; PyObject *swap_data; partial_func = PyObject_GetAttrString (py_arg, "func"); + partial_args = PyObject_GetAttrString (py_arg, "args"); partial_keywords = PyObject_GetAttrString (py_arg, "keywords"); swap_data = PyDict_GetItemString (partial_keywords, "swap_data"); - closure = pyg_closure_new (partial_func, NULL, swap_data); + closure = pyg_closure_new (partial_func, partial_args, swap_data); Py_DECREF (partial_func); + Py_DECREF (partial_args); Py_DECREF (partial_keywords); g_closure_ref (closure); g_closure_sink (closure); |