diff --git a/formation/loader.py b/formation/loader.py index 022fc14..355584f 100644 --- a/formation/loader.py +++ b/formation/loader.py @@ -4,6 +4,7 @@ # ======================================================================= # # Copyright (c) 2020 Hoverset Group. # # ======================================================================= # +import functools import logging import os import warnings @@ -17,7 +18,7 @@ from formation.meth import Meth from formation.handlers.image import parse_image from formation.handlers.scroll import apply_scroll_config -from formation.utils import is_class_toplevel, is_class_root +from formation.utils import is_class_toplevel, is_class_root, callback_parse, event_handler import formation logger = logging.getLogger(__name__) @@ -84,7 +85,7 @@ def _get_class(cls, node): if hasattr(module, impl): return getattr(module, impl) - raise AttributeError("class {} not found in module {}".format(impl, module)) + raise AttributeError("class {} in module {}".format(impl, module)) @classmethod def load(cls, node, builder, parent): @@ -425,24 +426,32 @@ def on_keypress(self, event): } for widget, events in self._event_map.items(): for event in events: - handler = callback_map.get(event.get("handler")) + handler_string = event.get("handler") + parsed = callback_parse(handler_string) + + # parsed[0] is the function name. + handler = callback_map.get(parsed[0]) if handler is not None: + # parsed[1] is function args/ parsed[2] is function kwargs. + partial_handler = functools.partial(event_handler, func=handler, args=parsed[1], kwargs=parsed[2]) widget.bind( event.get("sequence"), - handler, + partial_handler, event.get("add") ) else: - logger.warning("Callback '%s' not found", event.get("handler")) + logger.warning("Callback '%s' not found", parsed[0]) for prop, val, handle_method in self._command_map: - handler = callback_map.get(val) + parsed = callback_parse(val) + handler = callback_map.get(parsed[0]) if handle_method is None: raise ValueError("Handle method is None, unable to apply binding") if handler is not None: - handle_method(**{prop: handler}) + partial_handler = functools.partial(handler, *parsed[1], **parsed[2]) + handle_method(**{prop: partial_handler}) else: - logger.warning("Callback '%s' not found", val) + logger.warning("Callback '%s' not found", parsed[0]) def load_path(self, path): """ diff --git a/formation/utils.py b/formation/utils.py index 8c8c487..5347047 100644 --- a/formation/utils.py +++ b/formation/utils.py @@ -147,3 +147,26 @@ def __setitem__(self, key, value): getattr(self, self.prop_info[key]["setter"])(value) else: super().__setitem__(key, value) + + +def event_handler(e, func, args, kwargs): + """ + A utility function to handle events and pass them to the + appropriate callback function with the event object as the first argument. + """ + return func(e, *args, **kwargs) + + +def callback_parse(command: str): + """ + Returns parts of a command after parsing it using eval method. + + :param command: A string in the form ``funcname arg1, arg2, arg3, ..., kwarg1=value, kwarg2=value, ...`` + :return: A tuple containing (funcname, args, kwargs) + """ + + command_list: list = command.split(' ') + command_func: str = command_list.pop(0) + command_string = ",".join(command_list).strip(",") + args, kwargs = eval(f'(lambda *args, **kwargs: (args, kwargs))({command_string})') + return command_func, args, kwargs