Exemple #1
0
 def __init__(self, *a, **k):
     super(ModesComponent, self).__init__(*a, **k)
     self._last_toggle_value = 0
     self._mode_toggle = None
     self._mode_toggle_task = self._tasks.add(
         Task.wait(Defaults.MOMENTARY_DELAY))
     self._mode_toggle_task.kill()
     self._mode_list = []
     self._mode_map = {}
     self._last_selected_mode = None
     self._mode_stack = StackingResource(self._do_enter_mode,
                                         self._do_leave_mode)
     self._shift_button = None
 def __init__(self, *a, **k):
     super(ModesComponent, self).__init__(*a, **k)
     self._last_toggle_value = 0
     self._mode_toggle = None
     self._mode_toggle_task = self._tasks.add(Task.wait(Defaults.MOMENTARY_DELAY))
     self._mode_toggle_task.kill()
     self._mode_list = []
     self._mode_map = {}
     self._last_selected_mode = None
     self._mode_stack = StackingResource(self._do_enter_mode, self._do_leave_mode)
     self._shift_button = None
class ModesComponent(CompoundComponent):
    """
    A ModesComponent handles the selection of different modes of the
    component. It improves the ModeSelectorComponent in several ways:
    
    - A mode is an object with two methods for entering and exiting
      the mode.  You do not need to know about all the modes
      registered.
    
    - Any object convertible by 'tomode' can be passed as mode.
    
    - Modes are identified by strings.
    
    - The component will dynamically generate methods of the form:
    
          set_[mode-name]_button(button)
    
      for setting the mode button.  Thanks to this, you can pass the mode
      buttons in a layer.
    
    The modes component behaves like a stack.  Several modes can be
    active at the same time, but the component will make sure that
    only the one at the top (aka 'selected_mode') will be entered at a
    given time.  This allows you to implement modes that can be
    'cancelled' or 'mode latch' (i.e. go to the previous mode under
    certain conditions).
    """
    __subject_events__ = ('selected_mode',)
    momentary_toggle = False
    default_behaviour = LatchingBehaviour()

    def __init__(self, *a, **k):
        super(ModesComponent, self).__init__(*a, **k)
        self._last_toggle_value = 0
        self._mode_toggle = None
        self._mode_toggle_task = self._tasks.add(Task.wait(Defaults.MOMENTARY_DELAY))
        self._mode_toggle_task.kill()
        self._mode_list = []
        self._mode_map = {}
        self._last_selected_mode = None
        self._mode_stack = StackingResource(self._do_enter_mode, self._do_leave_mode)
        self._shift_button = None

    def disconnect(self):
        self._mode_stack.release_all()
        super(ModesComponent, self).disconnect()

    def set_shift_button(self, button):
        raise not button or button.is_momentary() or AssertionError
        self._shift_button = button

    def _do_enter_mode(self, name):
        entry = self._mode_map[name]
        entry.mode.enter_mode()
        self._update_buttons(name)
        self.notify_selected_mode(name)

    def _do_leave_mode(self, name):
        self._mode_map[name].mode.leave_mode()
        if self._mode_stack.stack_size == 0:
            self._update_buttons(None)
            self.notify_selected_mode(None)

    def _get_selected_mode(self):
        """
        Mode that is currently the top of the mode stack. Setting the
        selected mode explictly will also cleanup the mode stack.
        """
        return self._mode_stack.owner or self._last_selected_mode

    def _set_selected_mode(self, mode):
        if not (mode in self._mode_map or mode is None):
            raise AssertionError
            if self.is_enabled():
                mode != None and self.push_mode(mode)
                self.pop_unselected_modes()
            else:
                self._mode_stack.release_all()
        else:
            self._last_selected_mode = mode

    selected_mode = property(_get_selected_mode, _set_selected_mode)

    @property
    def selected_groups(self):
        entry = self._mode_map.get(self.selected_mode, None)
        return entry.groups if entry else set()

    @property
    def active_modes(self):
        return self._mode_stack.stack_clients

    def push_mode(self, mode):
        """
        Selects the current 'mode', leaving the rest of the modes in
        the mode stack.
        """
        self._mode_stack.grab(mode)

    def pop_mode(self, mode):
        """
        Takes 'mode' away from the mode stack.  If the mode was the
        currently selected one, the last pushed mode will be selected.
        """
        self._mode_stack.release(mode)

    def pop_groups(self, groups):
        """
        Pops every mode in groups.
        """
        if not isinstance(groups, set):
            groups = set(groups)
        self._mode_stack.release_if(lambda client: self.get_mode_groups(client) & groups)

    def pop_unselected_modes(self):
        """
        Pops from the mode stack all the modes that are not the
        currently selected one.
        """
        self._mode_stack.release_stacked()

    def on_enabled_changed(self):
        super(ModesComponent, self).on_enabled_changed()
        if not self.is_enabled():
            self._last_selected_mode = self.selected_mode
            self._mode_stack.release_all()
        elif self._last_selected_mode:
            self.push_mode(self._last_selected_mode)

    def update(self):
        self._update_buttons(self.selected_mode)

    def add_mode(self, name, mode_or_component, toggle_value = False, groups = set(), behaviour = None):
        """
        Adds a mode of the given name into the component.  The mode
        object should be a Mode or ControlSurfaceComponent instance.
        
        The 'toggle_value' is the light value the toggle_botton will
        be set to when the component is on this mode.
        
        If 'group' is not None, the mode will be put in the group
        identified by the passed object.  When several modes are grouped:
        
          * All the buttons in the group will light up when any of the
            modes withing the group is selected.
        
          * Any of the group buttons will cancel the current mode when
            the current mode belongs to the group.
        """
        if not name not in self._mode_map.keys():
            raise AssertionError
            if not isinstance(groups, set):
                groups = set(groups)
            mode = tomode(mode_or_component)
            task = self._tasks.add(Task.sequence(Task.wait(Defaults.MOMENTARY_DELAY), Task.run(lambda : self._get_mode_behaviour(name).press_delayed(self, name))))
            task.kill()
            slot = self.register_slot(listener=partial(self._on_mode_button_value, name), event='value', extra_kws=dict(identify_sender=True))
            self._mode_list.append(name)
            self._mode_map[name] = _ModeEntry(mode=mode, toggle_value=toggle_value, behaviour=behaviour, subject_slot=slot, momentary_task=task, groups=groups)
            button_setter = 'set_' + name + '_button'
            hasattr(self, button_setter) or setattr(self, button_setter, partial(self.set_mode_button, name))

    def _get_mode_behaviour(self, name):
        entry = self._mode_map.get(name, None)
        return entry and entry.behaviour or self.default_behaviour

    def get_mode(self, name):
        entry = self._mode_map.get(name, None)
        return entry and entry.mode

    def get_mode_groups(self, name):
        entry = self._mode_map.get(name, None)
        return entry.groups if entry else set()

    def set_toggle_button(self, button):
        self._mode_toggle = button
        self._on_toggle_value.subject = button
        self._update_buttons(self.selected_mode)

    def set_mode_button(self, name, button):
        self._mode_map[name].subject_slot.subject = button
        self._update_buttons(self.selected_mode)

    def get_mode_button(self, name):
        return self._mode_map[name].subject_slot.subject

    def _update_buttons(self, selected):
        if self.is_enabled():
            for name, entry in self._mode_map.iteritems():
                if entry.subject_slot.subject != None:
                    self._get_mode_behaviour(name).update_button(self, name, selected)

            if self._mode_toggle:
                entry = self._mode_map.get(selected)
                value = entry and entry.toggle_value
                self._mode_toggle.set_light(value)

    def _on_mode_button_value(self, name, value, sender):
        shift = self._shift_button and self._shift_button.is_pressed()
        if not shift and self.is_enabled():
            behaviour = self._get_mode_behaviour(name)
            if sender.is_momentary():
                entry = self._mode_map[name]
                task = entry.momentary_task
                if value:
                    behaviour.press_immediate(self, name)
                    task.restart()
                elif task.is_killed:
                    behaviour.release_delayed(self, name)
                else:
                    behaviour.release_immediate(self, name)
                    task.kill()
            else:
                behaviour.press_immediate(self, name)
                behaviour.release_immediate(self, name)

    @subject_slot('value')
    def _on_toggle_value(self, value):
        if self._shift_button:
            shift = self._shift_button.is_pressed()
            if not shift and self.is_enabled() and len(self._mode_list):
                is_press = value and not self._last_toggle_value
                is_release = not value and self._last_toggle_value
                can_latch = self._mode_toggle_task.is_killed and self.selected_mode != self._mode_list[0]
                (not self._mode_toggle.is_momentary() or is_press) and self._cycle_mode(1)
                self._mode_toggle_task.restart()
            elif is_release and (self.momentary_toggle or can_latch):
                self._cycle_mode(-1)
            self._last_toggle_value = value

    def _cycle_mode(self, delta):
        current_index = self._mode_list.index(self.selected_mode) if self.selected_mode else -delta
        current_index = (current_index + delta) % len(self._mode_list)
        self.selected_mode = self._mode_list[current_index]
Exemple #4
0
class ModesComponent(CompoundComponent):
    """
    A ModesComponent handles the selection of different modes of the
    component. It improves the ModeSelectorComponent in several ways:
    
    - A mode is an object with two methods for entering and exiting
      the mode.  You do not need to know about all the modes
      registered.
    
    - Any object convertible by 'tomode' can be passed as mode.
    
    - Modes are identified by strings.
    
    - The component will dynamically generate methods of the form:
    
          set_[mode-name]_button(button)
    
      for setting the mode button.  Thanks to this, you can pass the mode
      buttons in a layer.
    
    The modes component behaves like a stack.  Several modes can be
    active at the same time, but the component will make sure that
    only the one at the top (aka 'selected_mode') will be entered at a
    given time.  This allows you to implement modes that can be
    'cancelled' or 'mode latch' (i.e. go to the previous mode under
    certain conditions).
    """
    __subject_events__ = ('selected_mode', )
    momentary_toggle = False
    default_behaviour = LatchingBehaviour()

    def __init__(self, *a, **k):
        super(ModesComponent, self).__init__(*a, **k)
        self._last_toggle_value = 0
        self._mode_toggle = None
        self._mode_toggle_task = self._tasks.add(
            Task.wait(Defaults.MOMENTARY_DELAY))
        self._mode_toggle_task.kill()
        self._mode_list = []
        self._mode_map = {}
        self._last_selected_mode = None
        self._mode_stack = StackingResource(self._do_enter_mode,
                                            self._do_leave_mode)
        self._shift_button = None

    def disconnect(self):
        self._mode_stack.release_all()
        super(ModesComponent, self).disconnect()

    def set_shift_button(self, button):
        raise not button or button.is_momentary() or AssertionError
        self._shift_button = button

    def _do_enter_mode(self, name):
        entry = self._mode_map[name]
        entry.mode.enter_mode()
        self._update_buttons(name)
        self.notify_selected_mode(name)

    def _do_leave_mode(self, name):
        self._mode_map[name].mode.leave_mode()
        if self._mode_stack.stack_size == 0:
            self._update_buttons(None)
            self.notify_selected_mode(None)

    def _get_selected_mode(self):
        """
        Mode that is currently the top of the mode stack. Setting the
        selected mode explictly will also cleanup the mode stack.
        """
        return self._mode_stack.owner or self._last_selected_mode

    def _set_selected_mode(self, mode):
        if not (mode in self._mode_map or mode is None):
            raise AssertionError
            if self.is_enabled():
                mode != None and self.push_mode(mode)
                self.pop_unselected_modes()
            else:
                self._mode_stack.release_all()
        else:
            self._last_selected_mode = mode

    selected_mode = property(_get_selected_mode, _set_selected_mode)

    @property
    def selected_groups(self):
        entry = self._mode_map.get(self.selected_mode, None)
        return entry.groups if entry else set()

    @property
    def active_modes(self):
        return self._mode_stack.clients

    def push_mode(self, mode):
        """
        Selects the current 'mode', leaving the rest of the modes in
        the mode stack.
        """
        self._mode_stack.grab(mode)

    def pop_mode(self, mode):
        """
        Takes 'mode' away from the mode stack.  If the mode was the
        currently selected one, the last pushed mode will be selected.
        """
        self._mode_stack.release(mode)

    def pop_groups(self, groups):
        """
        Pops every mode in groups.
        """
        if not isinstance(groups, set):
            groups = set(groups)
        for client in self._mode_stack.clients:
            if self.get_mode_groups(client) & groups:
                self._mode_stack.release(client)

    def pop_unselected_modes(self):
        """
        Pops from the mode stack all the modes that are not the
        currently selected one.
        """
        self._mode_stack.release_stacked()

    def on_enabled_changed(self):
        super(ModesComponent, self).on_enabled_changed()
        if not self.is_enabled():
            self._last_selected_mode = self.selected_mode
            self._mode_stack.release_all()
        elif self._last_selected_mode:
            self.push_mode(self._last_selected_mode)

    def update(self):
        self._update_buttons(self.selected_mode)

    def add_mode(self,
                 name,
                 mode_or_component,
                 toggle_value=False,
                 groups=set(),
                 behaviour=None):
        """
        Adds a mode of the given name into the component.  The mode
        object should be a Mode or ControlSurfaceComponent instance.
        
        The 'toggle_value' is the light value the toggle_botton will
        be set to when the component is on this mode.
        
        If 'group' is not None, the mode will be put in the group
        identified by the passed object.  When several modes are grouped:
        
          * All the buttons in the group will light up when any of the
            modes withing the group is selected.
        
          * Any of the group buttons will cancel the current mode when
            the current mode belongs to the group.
        """
        if not name not in self._mode_map.keys():
            raise AssertionError
            if not isinstance(groups, set):
                groups = set(groups)
            mode = tomode(mode_or_component)
            task = self._tasks.add(
                Task.sequence(
                    Task.wait(Defaults.MOMENTARY_DELAY),
                    Task.run(lambda: self._get_mode_behaviour(name).
                             press_delayed(self, name))))
            task.kill()
            slot = self.register_slot(listener=partial(
                self._on_mode_button_value, name),
                                      event='value',
                                      extra_kws=dict(identify_sender=True))
            self._mode_list.append(name)
            self._mode_map[name] = _ModeEntry(mode=mode,
                                              toggle_value=toggle_value,
                                              behaviour=behaviour,
                                              subject_slot=slot,
                                              momentary_task=task,
                                              groups=groups)
            button_setter = 'set_' + name + '_button'
            hasattr(self, button_setter) or setattr(
                self, button_setter, partial(self.set_mode_button, name))

    def _get_mode_behaviour(self, name):
        entry = self._mode_map.get(name, None)
        return entry and entry.behaviour or self.default_behaviour

    def get_mode(self, name):
        entry = self._mode_map.get(name, None)
        return entry and entry.mode

    def get_mode_groups(self, name):
        entry = self._mode_map.get(name, None)
        return entry.groups if entry else set()

    def set_toggle_button(self, button):
        if button and self.is_enabled():
            button.reset()
        self._mode_toggle = button
        self._on_toggle_value.subject = button
        self._update_buttons(self.selected_mode)

    def set_mode_button(self, name, button):
        if button and self.is_enabled():
            button.reset()
        self._mode_map[name].subject_slot.subject = button
        self._update_buttons(self.selected_mode)

    def get_mode_button(self, name):
        return self._mode_map[name].subject_slot.subject

    def _update_buttons(self, selected):
        if self.is_enabled():
            for name, entry in self._mode_map.iteritems():
                if entry.subject_slot.subject != None:
                    self._get_mode_behaviour(name).update_button(
                        self, name, selected)

            if self._mode_toggle:
                entry = self._mode_map.get(selected)
                value = entry and entry.toggle_value
                self._mode_toggle.set_light(value)

    def _on_mode_button_value(self, name, value, sender):
        shift = self._shift_button and self._shift_button.is_pressed()
        if not shift and self.is_enabled():
            behaviour = self._get_mode_behaviour(name)
            if sender.is_momentary():
                entry = self._mode_map[name]
                task = entry.momentary_task
                if value:
                    behaviour.press_immediate(self, name)
                    task.restart()
                elif task.is_killed:
                    behaviour.release_delayed(self, name)
                else:
                    behaviour.release_immediate(self, name)
                    task.kill()
            else:
                behaviour.press_immediate(self, name)
                behaviour.release_immediate(self, name)

    @subject_slot('value')
    def _on_toggle_value(self, value):
        if self._shift_button:
            shift = self._shift_button.is_pressed()
            if not shift and self.is_enabled() and len(self._mode_list):
                is_press = value and not self._last_toggle_value
                is_release = not value and self._last_toggle_value
                can_latch = self._mode_toggle_task.is_killed and self.selected_mode != self._mode_list[
                    0]
                (not self._mode_toggle.is_momentary()
                 or is_press) and self.cycle_mode(1)
                self._mode_toggle_task.restart()
            elif is_release and (self.momentary_toggle or can_latch):
                self.cycle_mode(-1)
            self._last_toggle_value = value

    def cycle_mode(self, delta=1):
        current_index = self._mode_list.index(
            self.selected_mode) if self.selected_mode else -delta
        current_index = (current_index + delta) % len(self._mode_list)
        self.selected_mode = self._mode_list[current_index]