Пример #1
0
class ButtonPress(CallbackState):
    def __init__(self, buttons=None, correct_resp=None, base_time=None,
                 duration=None, parent=None, save_log=True, name=None,
                 blocking=True):
        super(ButtonPress, self).__init__(parent=parent, 
                                          duration=duration,
                                          save_log=save_log,
                                          name=name,
                                          blocking=blocking)
        if buttons is None:
            self.__buttons = []
        elif type(buttons) not in (list, tuple):
            self.__buttons = [buttons]
        else:
            self.__buttons = buttons
        self._button_names = None
        self._init_correct_resp = correct_resp
        self._init_base_time = None

        self._pressed = ''
        self._press_time = {"time": None, "error": None}
        self._correct = False
        self._rt = None

        self.__pressed_ref = None

        # append log vars
        self._log_attrs.extend(['button_names', 'correct_resp', 'base_time',
                                'pressed', 'press_time', 'correct', 'rt'])

        self.__parallel = None

    def _enter(self):
        self._button_names = [button._name for button in self.__buttons]
        if self._correct_resp is None:
            self._correct_resp = []
        elif type(self.correct_resp) not in (list, tuple):
            self._correct_resp = [self._correct_resp]
        self.__pressed_ref = Ref(
            lambda lst: [name for name, down in lst if down],
            [(button.name, button.state == "down") for
             button in self.__buttons])
        super(ButtonPress, self)._enter()

    def _callback(self):
        if self._base_time is None:
            self._base_time = self._start_time
        self._pressed = ''
        self._press_time = None
        self._correct = False
        self._rt = None
        self.__pressed_ref.add_change_callback(self.button_callback)

    def button_callback(self):
        self.claim_exceptions()
        pressed_list = self.__pressed_ref.eval()
        if not len(pressed_list):
            return
        button = pressed_list[0]
        self._pressed = button
        self._press_time = self._exp._app.event_time

        # calc RT if something pressed
        self._rt = self._press_time['time'] - self._base_time

        if self._pressed in self._correct_resp:
            self._correct = True

        # let's leave b/c we're all done
        self.cancel(self._press_time['time'])

    def _leave(self):
        self.__pressed_ref.remove_change_callback(self.button_callback)
        super(ButtonPress, self)._leave()

    def __enter__(self):
        if self.__parallel is not None:
            raise RuntimeError("ButtonPress context is not reentrant!")  #!!!
        #TODO: make sure we're the previous state?
        self.__parallel = Parallel(name="BUTTONPRESS")
        self.__parallel.override_instantiation_context()
        self.__parallel.claim_child(self)
        self.__parallel.__enter__()
        return self

    def __exit__(self, type, value, tb):
        ret = self.__parallel.__exit__(type, value, tb)
        for child in self.__parallel._children[1:]:
            child._blocking = False
        self.__buttons.extend(iter_nested_buttons(self.__parallel))
        self.__parallel = None
        return ret
Пример #2
0
class ButtonPress(CallbackState):
    def __init__(self,
                 buttons=None,
                 correct_resp=None,
                 base_time=None,
                 duration=None,
                 parent=None,
                 save_log=True,
                 name=None,
                 blocking=True):
        super(ButtonPress, self).__init__(parent=parent,
                                          duration=duration,
                                          save_log=save_log,
                                          name=name,
                                          blocking=blocking)
        if buttons is None:
            self.__buttons = []
        elif type(buttons) not in (list, tuple):
            self.__buttons = [buttons]
        else:
            self.__buttons = buttons
        self._button_names = None
        self._init_correct_resp = correct_resp
        self._init_base_time = None

        self._pressed = ''
        self._press_time = {"time": None, "error": None}
        self._correct = False
        self._rt = None

        self.__pressed_ref = None

        # append log vars
        self._log_attrs.extend([
            'button_names', 'correct_resp', 'base_time', 'pressed',
            'press_time', 'correct', 'rt'
        ])

        self.__parallel = None

    def _enter(self):
        self._button_names = [button._name for button in self.__buttons]
        if self._correct_resp is None:
            self._correct_resp = []
        elif type(self.correct_resp) not in (list, tuple):
            self._correct_resp = [self._correct_resp]
        self.__pressed_ref = Ref(
            lambda lst: [name for name, down in lst if down],
            [(button.name, button.state == "down")
             for button in self.__buttons])
        super(ButtonPress, self)._enter()

    def _callback(self):
        if self._base_time is None:
            self._base_time = self._start_time
        self._pressed = ''
        self._press_time = None
        self._correct = False
        self._rt = None
        self.__pressed_ref.add_change_callback(self.button_callback)

    def button_callback(self):
        self.claim_exceptions()
        pressed_list = self.__pressed_ref.eval()
        if not len(pressed_list):
            return
        button = pressed_list[0]
        self._pressed = button
        self._press_time = self._exp._app.event_time

        # calc RT if something pressed
        self._rt = self._press_time['time'] - self._base_time

        if self._pressed in self._correct_resp:
            self._correct = True

        # let's leave b/c we're all done
        self.cancel(self._press_time['time'])

    def _leave(self):
        self.__pressed_ref.remove_change_callback(self.button_callback)
        super(ButtonPress, self)._leave()

    def __enter__(self):
        if self.__parallel is not None:
            raise RuntimeError("ButtonPress context is not reentrant!")  #!!!
        #TODO: make sure we're the previous state?
        self.__parallel = Parallel(name="BUTTONPRESS")
        self.__parallel.override_instantiation_context()
        self.__parallel.claim_child(self)
        self.__parallel.__enter__()
        return self

    def __exit__(self, type, value, tb):
        ret = self.__parallel.__exit__(type, value, tb)
        for child in self.__parallel._children[1:]:
            child._blocking = False
        self.__buttons.extend(iter_nested_buttons(self.__parallel))
        self.__parallel = None
        return ret
Пример #3
0
class WidgetState(VisualState):
    layout_stack = []
    property_aliases = {
        "left": "x",
        "bottom": "y",
        "left_bottom": "pos",
        "left_center": ("x", "center_y"),
        "left_top": ("x", "top"),
        "center_bottom": ("center_x", "y"),
        "center_top": ("center_x", "top"),
        "right_bottom": ("right", "y"),
        "right_center": ("right", "center_y"),
        "right_top": ("right", "top")
        }

    @classmethod
    def wrap(cls, widget_class, name=None):
        if not issubclass(widget_class, kivy.uix.widget.Widget):
            raise ValueError(
                "widget_class must be a subclass of kivy.uix.widget.Widget")
        if name is None:
            name = widget_class.__name__
        def __init__(self, *pargs, **kwargs):
            cls.__init__(self, widget_class, *pargs, **kwargs)
        return type(name, (cls,), {"__init__" : __init__})

    def __init__(self, widget_class, duration=None, parent=None, save_log=True,
                 name=None, blocking=True, index=0, layout=None, **params):
        super(WidgetState, self).__init__(parent=parent,
                                          duration=duration,
                                          save_log=save_log,
                                          name=name,
                                          blocking=blocking)

        self.__issued_refs = weakref.WeakValueDictionary()
        self.__widget_param_names = widget_class().properties().keys()
        self.__widget_class = widget_class
        self._init_index = index
        self._widget = None
        self.__parent_widget = None
        self._constructor_param_names = params.keys()
        self._init_constructor_params = params
        for name, value in params.iteritems():
            setattr(self, "_init_" + name, value)
        if layout is None:
            if len(WidgetState.layout_stack):
                self.__layout = WidgetState.layout_stack[-1]
            else:
                self.__layout = None
        else:
            self.__layout = layout

        self.__x_pos_mode = None
        self.__y_pos_mode = None

        # set the log attrs
        self._log_attrs.extend(['constructor_params'])

        self.__parallel = None

    def attribute_update_state(self, name, value):
        if name in self.__widget_param_names:
            return UpdateWidget(self, **{name : value})
        else:
            raise AttributeError("%r is not a property of this widget (%r)." %
                                 (name, self))

    def get_current_param(self, name):
        return getattr(self.current_clone._widget, name)

    def __getattr__(self, name):
        try:
            return self.__issued_refs[name]
        except KeyError:
            try:
                props = WidgetState.property_aliases[name]
            except KeyError:
                if name in self.__widget_param_names:
                    props = name
                else:
                    return super(WidgetState, self).__getattr__(name)
            if isinstance(props, str):
                ref = Ref(self.get_current_param, props)
            elif isinstance(props, tuple):
                ref = tuple(Ref(self.get_current_param, prop) for
                            prop in props)
            else:
                raise RuntimeError("Bad value for 'props': %r" % props)
            self.__issued_refs[name] = ref
            return ref

    def property_callback(self, name, *pargs):
        try:
            ref = self.__issued_refs[name]
        except KeyError:
            return
        ref.dep_changed()

    def eval_init_refs(self):
        return self.transform_params(self.apply_aliases(
            {name : getattr(self, "_" + name) for
             name in self._constructor_param_names}))

    def apply_aliases(self, params):
        new_params = {}
        for name, value in params.items():
            props = WidgetState.property_aliases.get(name, name)
            if isinstance(props, str):
                new_params[props] = value
            elif isinstance(props, tuple):
                for n, prop in enumerate(props):
                    new_params[prop] = value[n]
            else:
                raise RuntimeError("Bad value for 'props': %r" % props)
        return new_params

    def transform_params(self, params):
        for name, value in params.iteritems():
            params[name] = self.transform_param(name, value)
        return params

    def transform_param(self, name, value):
        value = val(value)

        # normalize color specifier...
        if value is not None and "color" in name:
            return normalize_color_spec(value)
        else:
            return value

    def resolve_params(self, params):
        # remove kivy's default size hints...
        if "size_hint" not in params:
            params.setdefault("size_hint_x", None)
            params.setdefault("size_hint_y", None)

        return params

    def construct(self, params):
        self._widget = self.__widget_class(**params)
        self.live_change(**params)
        self._widget.bind(**{name : partial(self.property_callback, name) for
                            name in self.__widget_param_names})

    def show(self):
        if self.__layout is None:
            self.__parent_widget = self._exp._app.wid
        else:
            self.__parent_widget = self.__layout._widget
        self.__parent_widget.add_widget(self._widget, index=self._index)

    def unshow(self):
        self.__parent_widget.remove_widget(self._widget)
        self.__parent_widget = None

    def live_change(self, **params):
        xy_pos_props = {"pos": "min", "center": "mid"}
        x_pos_props = {"x": "min", "center_x": "mid", "right": "max"}
        y_pos_props = {"y": "min", "center_y": "mid", "top": "max"}
        pos_props = (xy_pos_props.keys() +
                     x_pos_props.keys() +
                     y_pos_props.keys())
        new_x_pos_mode = None
        new_y_pos_mode = None
        for prop, mode in xy_pos_props.iteritems():
            if prop in params:
                new_x_pos_mode = mode
                new_y_pos_mode = mode
                break
        else:
            for prop, mode in x_pos_props.iteritems():
                if prop in params:
                    new_x_pos_mode = mode
                    break
            for prop, mode in y_pos_props.iteritems():
                if prop in params:
                    new_y_pos_mode = mode
                    break
        if new_x_pos_mode is not None:
            self.__x_pos_mode = new_x_pos_mode
        elif self.__x_pos_mode is None:
            params["center_x"] = self._exp.screen.center_x.eval()
        elif self.__x_pos_mode == "min":
            params["x"] = self._widget.x
        elif self.__x_pos_mode == "mid":
            params["center_x"] = self._widget.center_x
        elif self.__x_pos_mode == "max":
            params["right"] = self._widget.right
        if new_y_pos_mode is not None:
            self.__y_pos_mode = new_y_pos_mode
        elif self.__y_pos_mode is None:
            params["center_y"] = self._exp.screen.center_y.eval()
        elif self.__y_pos_mode == "min":
            params["y"] = self._widget.y
        elif self.__y_pos_mode == "mid":
            params["center_y"] = self._widget.center_y
        elif self.__y_pos_mode == "max":
            params["top"] = self._widget.top
        for name, value in params.iteritems():
            if name not in pos_props:
                setattr(self._widget, name, value)
        for name, value in params.iteritems():
            if name in pos_props:
                setattr(self._widget, name, value)

    def animate(self, duration=None, parent=None, save_log=True, name=None,
                **anim_params):
        anim = Animate(self, duration=duration, parent=parent, name=name,
                       save_log=save_log, **anim_params)
        anim.override_instantiation_context()
        return anim

    def slide(self, duration=None, speed=None, accel=None, parent=None,
              save_log=True, name=None, **params):
        def interp(a, b, w):
            if isinstance(a, dict):
                return {name : interp(a[name], b[name], w) for
                        name in set(a) & set(b)}
            elif hasattr(a, "__iter__"):
                return [interp(a_prime, b_prime, w) for
                        a_prime, b_prime in
                        zip(a, b)]
            else:
                return a * (1.0 - w) + b * w
        condition = duration is None, speed is None, accel is None
        if condition == (False, True, True):  # simple, linear interpolation
            anim_params = {}
            for param_name, value in params.items():
                def func(t, initial, value=value, param_name=param_name):
                    new_value = self.transform_param(param_name, value)
                    return interp(initial, new_value, t / duration)
                anim_params[param_name] = func
        #TODO: fancier interpolation modes!!!
        else:
            raise ValueError("Invalid combination of parameters.")  #...
        anim = self.animate(duration=duration, parent=parent,
                            save_log=save_log, name=name, **anim_params)
        anim.override_instantiation_context()
        return anim

    def set_appear_time(self, appear_time):
        self._appear_time = appear_time
        clock.schedule(self.leave)

    def set_disappear_time(self, disappear_time):
        self._disappear_time = disappear_time
        clock.schedule(self.finalize)

    def _enter(self):
        super(WidgetState, self)._enter()
        self.__x_pos_mode = None
        self.__y_pos_mode = None

        params = self.eval_init_refs()
        params = self.resolve_params(params)
        self.construct(params)

    def __enter__(self):
        if self.__parallel is not None:
            raise RuntimeError("WidgetState context is not reentrant!")  #!!!
        #TODO: make sure we're the previous state?
        WidgetState.layout_stack.append(self)
        self.__parallel = Parallel(name="LAYOUT")
        self.__parallel.override_instantiation_context()
        self.__parallel.claim_child(self)
        self.__parallel.__enter__()
        return self

    def __exit__(self, type, value, tb):
        ret = self.__parallel.__exit__(type, value, tb)
        if self._init_duration is None:
            self.__parallel._children[0]._blocking = False
        else:
            for child in self.__parallel._children[1:]:
                child._blocking = False
        self.__parallel = None
        if len(WidgetState.layout_stack):
            WidgetState.layout_stack.pop()
        return ret
Пример #4
0
class WidgetState(VisualState):
    layout_stack = []
    property_aliases = {
        "left": "x",
        "bottom": "y",
        "left_bottom": "pos",
        "left_center": ("x", "center_y"),
        "left_top": ("x", "top"),
        "center_bottom": ("center_x", "y"),
        "center_top": ("center_x", "top"),
        "right_bottom": ("right", "y"),
        "right_center": ("right", "center_y"),
        "right_top": ("right", "top")
    }

    @classmethod
    def wrap(cls, widget_class, name=None):
        if not issubclass(widget_class, kivy.uix.widget.Widget):
            raise ValueError(
                "widget_class must be a subclass of kivy.uix.widget.Widget")
        if name is None:
            name = widget_class.__name__

        def __init__(self, *pargs, **kwargs):
            cls.__init__(self, widget_class, *pargs, **kwargs)

        return type(name, (cls, ), {"__init__": __init__})

    def __init__(self,
                 widget_class,
                 duration=None,
                 parent=None,
                 save_log=True,
                 name=None,
                 blocking=True,
                 index=0,
                 layout=None,
                 **params):
        super(WidgetState, self).__init__(parent=parent,
                                          duration=duration,
                                          save_log=save_log,
                                          name=name,
                                          blocking=blocking)

        self.__issued_refs = weakref.WeakValueDictionary()
        self.__widget_param_names = widget_class().properties().keys()
        self.__widget_class = widget_class
        self._init_index = index
        self._widget = None
        self.__parent_widget = None
        self._constructor_param_names = params.keys()
        self._init_constructor_params = params
        for name, value in params.iteritems():
            setattr(self, "_init_" + name, value)
        if layout is None:
            if len(WidgetState.layout_stack):
                self.__layout = WidgetState.layout_stack[-1]
            else:
                self.__layout = None
        else:
            self.__layout = layout

        self.__x_pos_mode = None
        self.__y_pos_mode = None

        # set the log attrs
        self._log_attrs.extend(['constructor_params'])

        self.__parallel = None

    def attribute_update_state(self, name, value):
        if name in self.__widget_param_names:
            return UpdateWidget(self, **{name: value})
        else:
            raise AttributeError("%r is not a property of this widget (%r)." %
                                 (name, self))

    def get_current_param(self, name):
        return getattr(self.current_clone._widget, name)

    def __getattr__(self, name):
        try:
            return self.__issued_refs[name]
        except KeyError:
            try:
                props = WidgetState.property_aliases[name]
            except KeyError:
                if name in self.__widget_param_names:
                    props = name
                else:
                    return super(WidgetState, self).__getattr__(name)
            if isinstance(props, str):
                ref = Ref(self.get_current_param, props)
            elif isinstance(props, tuple):
                ref = tuple(
                    Ref(self.get_current_param, prop) for prop in props)
            else:
                raise RuntimeError("Bad value for 'props': %r" % props)
            self.__issued_refs[name] = ref
            return ref

    def property_callback(self, name, *pargs):
        try:
            ref = self.__issued_refs[name]
        except KeyError:
            return
        ref.dep_changed()

    def eval_init_refs(self):
        return self.transform_params(
            self.apply_aliases({
                name: getattr(self, "_" + name)
                for name in self._constructor_param_names
            }))

    def apply_aliases(self, params):
        new_params = {}
        for name, value in params.items():
            props = WidgetState.property_aliases.get(name, name)
            if isinstance(props, str):
                new_params[props] = value
            elif isinstance(props, tuple):
                for n, prop in enumerate(props):
                    new_params[prop] = value[n]
            else:
                raise RuntimeError("Bad value for 'props': %r" % props)
        return new_params

    def transform_params(self, params):
        for name, value in params.iteritems():
            params[name] = self.transform_param(name, value)
        return params

    def transform_param(self, name, value):
        value = val(value)

        # normalize color specifier...
        if value is not None and "color" in name:
            return normalize_color_spec(value)
        else:
            return value

    def resolve_params(self, params):
        # remove kivy's default size hints...
        if "size_hint" not in params:
            params.setdefault("size_hint_x", None)
            params.setdefault("size_hint_y", None)

        return params

    def construct(self, params):
        self._widget = self.__widget_class(**params)
        self.live_change(**params)
        self._widget.bind(
            **{
                name: partial(self.property_callback, name)
                for name in self.__widget_param_names
            })

    def show(self):
        if self.__layout is None:
            self.__parent_widget = self._exp._app.wid
        else:
            self.__parent_widget = self.__layout._widget
        self.__parent_widget.add_widget(self._widget, index=self._index)

    def unshow(self):
        self.__parent_widget.remove_widget(self._widget)
        self.__parent_widget = None

    def live_change(self, **params):
        xy_pos_props = {"pos": "min", "center": "mid"}
        x_pos_props = {"x": "min", "center_x": "mid", "right": "max"}
        y_pos_props = {"y": "min", "center_y": "mid", "top": "max"}
        pos_props = (xy_pos_props.keys() + x_pos_props.keys() +
                     y_pos_props.keys())
        new_x_pos_mode = None
        new_y_pos_mode = None
        for prop, mode in xy_pos_props.iteritems():
            if prop in params:
                new_x_pos_mode = mode
                new_y_pos_mode = mode
                break
        else:
            for prop, mode in x_pos_props.iteritems():
                if prop in params:
                    new_x_pos_mode = mode
                    break
            for prop, mode in y_pos_props.iteritems():
                if prop in params:
                    new_y_pos_mode = mode
                    break
        if new_x_pos_mode is not None:
            self.__x_pos_mode = new_x_pos_mode
        elif self.__x_pos_mode is None:
            params["center_x"] = self._exp.screen.center_x.eval()
        elif self.__x_pos_mode == "min":
            params["x"] = self._widget.x
        elif self.__x_pos_mode == "mid":
            params["center_x"] = self._widget.center_x
        elif self.__x_pos_mode == "max":
            params["right"] = self._widget.right
        if new_y_pos_mode is not None:
            self.__y_pos_mode = new_y_pos_mode
        elif self.__y_pos_mode is None:
            params["center_y"] = self._exp.screen.center_y.eval()
        elif self.__y_pos_mode == "min":
            params["y"] = self._widget.y
        elif self.__y_pos_mode == "mid":
            params["center_y"] = self._widget.center_y
        elif self.__y_pos_mode == "max":
            params["top"] = self._widget.top
        for name, value in params.iteritems():
            if name not in pos_props:
                setattr(self._widget, name, value)
        for name, value in params.iteritems():
            if name in pos_props:
                setattr(self._widget, name, value)

    def animate(self,
                duration=None,
                parent=None,
                save_log=True,
                name=None,
                **anim_params):
        anim = Animate(self,
                       duration=duration,
                       parent=parent,
                       name=name,
                       save_log=save_log,
                       **anim_params)
        anim.override_instantiation_context()
        return anim

    def slide(self,
              duration=None,
              speed=None,
              accel=None,
              parent=None,
              save_log=True,
              name=None,
              **params):
        def interp(a, b, w):
            if isinstance(a, dict):
                return {
                    name: interp(a[name], b[name], w)
                    for name in set(a) & set(b)
                }
            elif hasattr(a, "__iter__"):
                return [
                    interp(a_prime, b_prime, w)
                    for a_prime, b_prime in zip(a, b)
                ]
            else:
                return a * (1.0 - w) + b * w

        condition = duration is None, speed is None, accel is None
        if condition == (False, True, True):  # simple, linear interpolation
            anim_params = {}
            for param_name, value in params.items():

                def func(t, initial, value=value, param_name=param_name):
                    new_value = self.transform_param(param_name, value)
                    return interp(initial, new_value, t / duration)

                anim_params[param_name] = func
        #TODO: fancier interpolation modes!!!
        else:
            raise ValueError("Invalid combination of parameters.")  #...
        anim = self.animate(duration=duration,
                            parent=parent,
                            save_log=save_log,
                            name=name,
                            **anim_params)
        anim.override_instantiation_context()
        return anim

    def set_appear_time(self, appear_time):
        self._appear_time = appear_time
        clock.schedule(self.leave)

    def set_disappear_time(self, disappear_time):
        self._disappear_time = disappear_time
        clock.schedule(self.finalize)

    def _enter(self):
        super(WidgetState, self)._enter()
        self.__x_pos_mode = None
        self.__y_pos_mode = None

        params = self.eval_init_refs()
        params = self.resolve_params(params)
        self.construct(params)

    def __enter__(self):
        if self.__parallel is not None:
            raise RuntimeError("WidgetState context is not reentrant!")  #!!!
        #TODO: make sure we're the previous state?
        WidgetState.layout_stack.append(self)
        self.__parallel = Parallel(name="LAYOUT")
        self.__parallel.override_instantiation_context()
        self.__parallel.claim_child(self)
        self.__parallel.__enter__()
        return self

    def __exit__(self, type, value, tb):
        ret = self.__parallel.__exit__(type, value, tb)
        if self._init_duration is None:
            self.__parallel._children[0]._blocking = False
        else:
            for child in self.__parallel._children[1:]:
                child._blocking = False
        self.__parallel = None
        if len(WidgetState.layout_stack):
            WidgetState.layout_stack.pop()
        return ret