class OptimizedOwnershipHandler(ElementOwnershipHandler): """ Control element ownership handler that delays notification of ownership changes and minimizes the number of actual owernship changes that are delivered. """ def __init__(self, *a, **k): super(OptimizedOwnershipHandler, self).__init__(*a, **k) self._ownership_changes = {} self._sequence_number = 0 def handle_ownership_change(self, control, client, status): if (control, client, not status) in self._ownership_changes: del self._ownership_changes[control, client, not status] else: self._ownership_changes[control, client, status] = self._sequence_number self._sequence_number += 1 @depends(log_message=const(print_message), traceback=const(traceback)) def commit_ownership_changes(self, log_message = None, traceback = None): notify = super(OptimizedOwnershipHandler, self).handle_ownership_change while self._ownership_changes: notifications = sorted(self._ownership_changes.iteritems(), key=second) self._ownership_changes.clear() for (control, client, status), _ in notifications: try: notify(control, client, status) except Exception: log_message('Error when trying to give control:', control.name) traceback.print_exc() self._ownership_changes.clear() self._sequence_number = 0
def __init__(self, *a, **k): super(TransportComponent, self).__init__(*a, **k) self._ffwd_button = None self._rwd_button = None self._tap_tempo_button = None self._tempo_control = None self._tempo_fine_control = None self._song_position_control = None self._rwd_task = Task.Task() self._ffwd_task = Task.Task() self._fine_tempo_needs_pickup = True self._prior_fine_tempo_value = -1 self._end_undo_step_task = self._tasks.add( Task.sequence(Task.wait(1.5), Task.run(self.song().end_undo_step))) self._end_undo_step_task.kill() song = self.song() self._loop_toggle, self._punch_in_toggle, self._punch_out_toggle, self._record_toggle, self._play_toggle, self._stop_toggle, self._nudge_down_toggle, self._nudge_up_toggle, self._metronome_toggle, self._session_record_toggle, self.arrangement_overdub_toggle, self._overdub_toggle = self.register_components( ToggleComponent('loop', song), ToggleComponent('punch_in', song, is_momentary=True), ToggleComponent('punch_out', song, is_momentary=True), ToggleComponent('record_mode', song), ToggleComponent('is_playing', song, model_transform=const(True)), ToggleComponent('is_playing', song, model_transform=const(False), view_transform=const(False)), ToggleComponent('nudge_down', song, is_momentary=True), ToggleComponent('nudge_up', song, is_momentary=True), ToggleComponent('metronome', song), ToggleComponent('session_record', song), ToggleComponent('arrangement_overdub', song), ToggleComponent('overdub', song))
def __init__(self, c_instance=None, publish_self=True, *a, **k): """ Define and Initialize standard behavior """ super(ControlSurface, self).__init__(*a, **k) if not c_instance: raise AssertionError self.canonical_parent = None publish_self and publish_control_surface(self) self._c_instance = c_instance self.log_message('Initialising...') self._pad_translations = None self._suggested_input_port = str('') self._suggested_output_port = str('') self._components = [] self._displays = [] self.controls = [] self._highlighting_session_component = None self._device_component = None self._device_selection_follows_track_selection = False self._forwarding_long_identifier_registry = {} self._forwarding_registry = {} self._is_sending_scheduled_messages = BooleanContext() self._remaining_scheduled_messages = [] self._task_group = Task.TaskGroup(auto_kill=False) self._in_build_midi_map = BooleanContext() self._suppress_requests_counter = 0 self._rebuild_requests_during_suppression = 0 self._enabled = True self._in_component_guard = BooleanContext() self._accumulate_midi_messages = BooleanContext() self._midi_message_dict = {} self._midi_message_list = [] self._midi_message_count = 0 self._control_surface_injector = inject( parent_task_group=const(self._task_group), show_message=const(self.show_message), log_message=const(self.log_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere() with self.setting_listener_caller(): self.song().add_visible_tracks_listener( self._on_track_list_changed) self.song().add_scenes_listener(self._on_scene_list_changed) self.song().view.add_selected_track_listener( self._on_selected_track_changed) self.song().view.add_selected_scene_listener( self._on_selected_scene_changed) return
def __init__(self, play_toggle_model_transform = const(True), *a, **k): super(TransportComponent, self).__init__(*a, **k) self._ffwd_button = None self._rwd_button = None self._tap_tempo_button = None self._tempo_control = None self._tempo_fine_control = None self._song_position_control = None self._rwd_task = Task.Task() self._ffwd_task = Task.Task() self._fine_tempo_needs_pickup = True self._prior_fine_tempo_value = -1 self._end_undo_step_task = self._tasks.add(Task.sequence(Task.wait(1.5), Task.run(self.song().end_undo_step))) self._end_undo_step_task.kill() song = self.song() self._loop_toggle, self._punch_in_toggle, self._punch_out_toggle, self._record_toggle, self._play_toggle, self._stop_toggle, self._nudge_down_toggle, self._nudge_up_toggle, self._metronome_toggle, self._session_record_toggle, self.arrangement_overdub_toggle, self._overdub_toggle = self.register_components(ToggleComponent('loop', song), ToggleComponent('punch_in', song, is_momentary=True), ToggleComponent('punch_out', song, is_momentary=True), ToggleComponent('record_mode', song), ToggleComponent('is_playing', song, model_transform=play_toggle_model_transform), ToggleComponent('is_playing', song, model_transform=const(False), view_transform=const(False)), ToggleComponent('nudge_down', song, is_momentary=True), ToggleComponent('nudge_up', song, is_momentary=True), ToggleComponent('metronome', song), ToggleComponent('session_record', song), ToggleComponent('arrangement_overdub', song), ToggleComponent('overdub', song))
def __init__(self, c_instance, publish_self=True, *a, **k): """ Define and Initialize standard behavior """ super(ControlSurface, self).__init__(*a, **k) self.canonical_parent = None if publish_self: if isinstance(__builtins__, dict): if CS_LIST_KEY not in __builtins__.keys(): __builtins__[CS_LIST_KEY] = [] __builtins__[CS_LIST_KEY].append(self) else: if not hasattr(__builtins__, CS_LIST_KEY): setattr(__builtins__, CS_LIST_KEY, []) cs_list = getattr(__builtins__, CS_LIST_KEY) cs_list.append(self) setattr(__builtins__, CS_LIST_KEY, cs_list) self._c_instance = c_instance self._pad_translations = None self._suggested_input_port = str('') self._suggested_output_port = str('') self._components = [] self._displays = [] self.controls = [] self._highlighting_session_component = None self._device_component = None self._device_selection_follows_track_selection = False self._forwarding_long_identifier_registry = {} self._forwarding_registry = {} self._is_sending_scheduled_messages = BooleanContext() self._remaining_scheduled_messages = [] self._task_group = Task.TaskGroup(auto_kill=False) self._in_build_midi_map = BooleanContext() self._suppress_requests_counter = 0 self._rebuild_requests_during_suppression = 0 self._enabled = True self._in_component_guard = BooleanContext() self._accumulate_midi_messages = BooleanContext() self._midi_message_dict = {} self._midi_message_list = [] self._midi_message_count = 0 self._control_surface_injector = inject( parent_task_group=const(self._task_group), show_message=const(self.show_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere() with self.setting_listener_caller(): self.song().add_visible_tracks_listener( self._on_track_list_changed) self.song().add_scenes_listener(self._on_scene_list_changed) self.song().view.add_selected_track_listener( self._on_selected_track_changed) self.song().view.add_selected_scene_listener( self._on_selected_scene_changed)
def __init__(self, c_instance, publish_self = True, *a, **k): """ Define and Initialize standard behavior """ super(ControlSurface, self).__init__(*a, **k) self.canonical_parent = None if publish_self: if isinstance(__builtins__, dict): if CS_LIST_KEY not in __builtins__.keys(): __builtins__[CS_LIST_KEY] = [] __builtins__[CS_LIST_KEY].append(self) else: if not hasattr(__builtins__, CS_LIST_KEY): setattr(__builtins__, CS_LIST_KEY, []) cs_list = getattr(__builtins__, CS_LIST_KEY) cs_list.append(self) setattr(__builtins__, CS_LIST_KEY, cs_list) self._c_instance = c_instance self._pad_translations = None self._suggested_input_port = str('') self._suggested_output_port = str('') self._components = [] self._displays = [] self.controls = [] self._highlighting_session_component = None self._device_component = None self._device_selection_follows_track_selection = False self._forwarding_long_identifier_registry = {} self._forwarding_registry = {} self._is_sending_scheduled_messages = BooleanContext() self._remaining_scheduled_messages = [] self._task_group = Task.TaskGroup(auto_kill=False) self._in_build_midi_map = BooleanContext() self._suppress_requests_counter = 0 self._rebuild_requests_during_suppression = 0 self._enabled = True self._in_component_guard = BooleanContext() self._accumulate_midi_messages = BooleanContext() self._midi_message_dict = {} self._midi_message_list = [] self._midi_message_count = 0 self._control_surface_injector = inject(parent_task_group=const(self._task_group), show_message=const(self.show_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere() with self.setting_listener_caller(): self.song().add_visible_tracks_listener(self._on_track_list_changed) self.song().add_scenes_listener(self._on_scene_list_changed) self.song().view.add_selected_track_listener(self._on_selected_track_changed) self.song().view.add_selected_scene_listener(self._on_selected_scene_changed)
def __init__(self, c_instance = None, publish_self = True, *a, **k): """ Define and Initialize standard behavior """ super(ControlSurface, self).__init__(*a, **k) if not c_instance: raise AssertionError self.canonical_parent = None publish_self and publish_control_surface(self) self._c_instance = c_instance self.log_message('Initialising...') self._pad_translations = None self._suggested_input_port = str('') self._suggested_output_port = str('') self._components = [] self._displays = [] self.controls = [] self._highlighting_session_component = None self._device_component = None self._device_selection_follows_track_selection = False self._forwarding_long_identifier_registry = {} self._forwarding_registry = {} self._is_sending_scheduled_messages = BooleanContext() self._remaining_scheduled_messages = [] self._task_group = Task.TaskGroup(auto_kill=False) self._in_build_midi_map = BooleanContext() self._suppress_requests_counter = 0 self._rebuild_requests_during_suppression = 0 self._enabled = True self._in_component_guard = BooleanContext() self._accumulate_midi_messages = BooleanContext() self._midi_message_dict = {} self._midi_message_list = [] self._midi_message_count = 0 self._control_surface_injector = inject(parent_task_group=const(self._task_group), show_message=const(self.show_message), log_message=const(self.log_message), register_component=const(self._register_component), register_control=const(self._register_control), request_rebuild_midi_map=const(self.request_rebuild_midi_map), send_midi=const(self._send_midi), song=self.song).everywhere() with self.setting_listener_caller(): self.song().add_visible_tracks_listener(self._on_track_list_changed) self.song().add_scenes_listener(self._on_scene_list_changed) self.song().view.add_selected_track_listener(self._on_selected_track_changed) self.song().view.add_selected_scene_listener(self._on_selected_scene_changed) return
def on_nested_control_element_value(self, value, sender): x, y = self._button_coordinates[sender] raise self._buttons[y][x] or AssertionError is_momentary = getattr(sender, 'is_momentary', const(None))() self.notify_value(value, x, y, is_momentary)
def __init__(self, *a, **k): super(OptimizedControlSurface, self).__init__(*a, **k) self._optimized_ownership_handler = OptimizedOwnershipHandler() injecting = inject( element_ownership_handler=const(self._optimized_ownership_handler)) self._ownership_handler_injector = injecting.everywhere()
class ProxiedInterface(NotifyingControlElement.ProxiedInterface): send_value = nop receive_value = nop use_default_message = nop set_channel = nop message_channel = const(None)
def nested_display_resource_factory(self, display): wrapper = ClientWrapper(wrap=lambda c: (display, c), unwrap=partial(maybe(second))) return const(ProxyResource(proxied_resource=self._central_resource, client_wrapper=wrapper))
class TaskGroup(Task): auto_kill = True auto_remove = True loop = False def __init__(self, tasks=[], auto_kill=None, auto_remove=None, loop=None, *a, **k): super(TaskGroup, self).__init__(*a, **k) if auto_kill is not None: self.auto_kill = auto_kill if auto_remove is not None: self.auto_remove = auto_remove if loop is not None: self.loop = loop self._tasks = [] for task in tasks: self.add(task) def clear(self): for t in self._tasks: t._set_parent(None) self._tasks = [] super(TaskGroup, self).clear() @depends(log_message=const(print_message), traceback=const(traceback)) def do_update(self, timer, log_message=None, traceback=None): super(TaskGroup, self).do_update(timer) for task in self._tasks: if not task.is_killed: try: task.update(timer) except Exception: task.kill() log_message('Error when executing task') traceback.print_exc() if self.auto_remove: self._tasks = remove_if(lambda t: t.is_killed, self._tasks) all_killed = len(filter(lambda t: t.is_killed, self._tasks)) == self.count if self.auto_kill and all_killed: self.kill() elif self.loop and all_killed: self.restart() def add(self, task): task = totask(task) task._set_parent(self) self._tasks.append(task) if self.is_killed: super(TaskGroup, self).restart() return task def remove(self, task): self._tasks.remove(task) task._set_parent(None) def find(self, task): return find_if(lambda t: t._task_equivalent(task), self._tasks) def restart(self): super(TaskGroup, self).restart() for x in self._tasks: x.restart() @property def count(self): return len(self._tasks)
class ProxiedInterface(ControlElement.ProxiedInterface): set_num_segments = nop set_data_sources = nop segment = const(LogicalDisplaySegment(1, nop))
def nested_display_resource_factory(self, display): wrapper = ClientWrapper(wrap=lambda c: (display, c), unwrap=partial(maybe(second))) return const( ProxyResource(proxied_resource=self._central_resource, client_wrapper=wrapper))
class ControlElement(Disconnectable): """ Base class for all classes representing control elements on a control surface """ class ProxiedInterface(object): """ Declaration of the interface to be used when the ControlElement is wrapped in any form of Proxy object. """ send_midi = nop def __init__(self, outer = None, *a, **k): super(ControlElement.ProxiedInterface, self).__init__(*a, **k) self._outer = outer @property def outer(self): return self._outer @lazy_attribute def proxied_interface(self): return self.ProxiedInterface(outer=self) canonical_parent = None name = '' optimized_send_midi = True _has_resource = False _resource_type = StackingResource _has_task_group = False @depends(send_midi=None, register_control=None) def __init__(self, name = '', resource_type = None, optimized_send_midi = None, send_midi = None, register_control = None, *a, **k): super(ControlElement, self).__init__(*a, **k) self._send_midi = send_midi self.name = name if resource_type is not None: self._resource_type = resource_type if optimized_send_midi is not None: self.optimized_send_midi = optimized_send_midi register_control(self) def disconnect(self): self.reset() super(ControlElement, self).disconnect() def send_midi(self, message): raise message != None or AssertionError return self._send_midi(message, optimized=self.optimized_send_midi) def clear_send_cache(self): pass def reset(self): raise NotImplementedError @property def resource(self): return self._resource @lazy_attribute def _resource(self): self._has_resource = True return self._resource_type(self._on_resource_received, self._on_resource_lost) @lazy_attribute @depends(parent_task_group=Task.TaskGroup) def _tasks(self, parent_task_group = None): tasks = parent_task_group.add(Task.TaskGroup()) self._has_task_group = True return tasks def _on_resource_received(self, client, *a, **k): self.notify_ownership_change(client, True) def _on_resource_lost(self, client): self.notify_ownership_change(client, False) @depends(element_ownership_handler=const(ElementOwnershipHandler())) def notify_ownership_change(self, client, grabbed, element_ownership_handler = None): element_ownership_handler.handle_ownership_change(self, client, grabbed)
class InputControlElement(NotifyingControlElement): """ Base class for all classes representing control elements on a controller """ __subject_events__ = (SubjectEvent(name='value', signal=InputSignal, override=True),) _input_signal_listener_count = 0 num_delayed_messages = 1 send_depends_on_forwarding = True @depends(request_rebuild_midi_map=const(nop)) def __init__(self, msg_type = None, channel = None, identifier = None, sysex_identifier = None, request_rebuild_midi_map = None, *a, **k): raise msg_type in MIDI_MSG_TYPES or AssertionError raise in_range(channel, 0, 16) or channel is None or AssertionError raise in_range(identifier, 0, 128) or identifier is None or AssertionError raise msg_type != MIDI_SYSEX_TYPE or channel == None or AssertionError raise msg_type != MIDI_SYSEX_TYPE or identifier == None or AssertionError raise msg_type == MIDI_SYSEX_TYPE or sysex_identifier == None or AssertionError super(InputControlElement, self).__init__(*a, **k) self._request_rebuild = request_rebuild_midi_map self._msg_type = msg_type self._msg_channel = channel self._msg_identifier = identifier self._msg_sysex_identifier = sysex_identifier self._original_channel = channel self._original_identifier = identifier self._needs_takeover = True self._is_mapped = True self._is_being_forwarded = True self._delayed_messages = [] self._force_next_send = False self._mapping_feedback_delay = 0 self._mapping_sensitivity = 1.0 self._send_delayed_messages_task = self._tasks.add(Task.run(self._send_delayed_messages)) self._send_delayed_messages_task.kill() self._parameter_to_map_to = None self._in_parameter_gesture = False self._last_sent_message = None self._report_input = False self._report_output = False def message_type(self): return self._msg_type def message_channel(self): return self._msg_channel def message_identifier(self): return self._msg_identifier def message_sysex_identifier(self): return self._msg_sysex_identifier def message_map_mode(self): raise NotImplementedError def _get_mapping_sensitivity(self): return self._mapping_sensitivity def _set_mapping_sensitivity(self, sensitivity): self._mapping_sensitivity = sensitivity mapping_sensitivity = property(_get_mapping_sensitivity, _set_mapping_sensitivity) def force_next_send(self): """ Enforces sending the next value regardless of wether the control is mapped to the script. """ self._force_next_send = True def set_channel(self, channel): if not self._msg_type != MIDI_SYSEX_TYPE: raise AssertionError raise in_range(channel, 0, 16) or channel == None or AssertionError self._msg_channel = self._msg_channel != channel and channel self._request_rebuild() def set_identifier(self, identifier): if not self._msg_type != MIDI_SYSEX_TYPE: raise AssertionError raise in_range(identifier, 0, 128) or identifier == None or AssertionError self._msg_identifier = self._msg_identifier != identifier and identifier self._request_rebuild() def set_needs_takeover(self, needs_takeover): raise self.message_type() != MIDI_NOTE_TYPE or AssertionError self._needs_takeover = needs_takeover def set_feedback_delay(self, delay): raise delay >= -1 or AssertionError self._mapping_feedback_delay = delay def needs_takeover(self): raise self.message_type() != MIDI_NOTE_TYPE or AssertionError return self._needs_takeover def use_default_message(self): if (self._msg_channel, self._msg_identifier) != (self._original_channel, self._original_identifier): self._msg_channel = self._original_channel self._msg_identifier = self._original_identifier self._request_rebuild() def _mapping_feedback_values(self): value_map = tuple() if self._mapping_feedback_delay != 0: if self._msg_type != MIDI_PB_TYPE: value_map = tuple(range(128)) else: value_pairs = [] for value in xrange(16384): value_pairs.append((value >> 7 & 127, value & 127)) value_map = tuple(value_pairs) return value_map def install_connections(self, install_translation, install_mapping, install_forwarding): self._send_delayed_messages_task.kill() self._is_mapped = False self._is_being_forwarded = False if self._msg_channel != self._original_channel or self._msg_identifier != self._original_identifier: install_translation(self._msg_type, self._original_identifier, self._original_channel, self._msg_identifier, self._msg_channel) if self._parameter_to_map_to != None: self._is_mapped = install_mapping(self, self._parameter_to_map_to, self._mapping_feedback_delay, self._mapping_feedback_values()) if self.script_wants_forwarding(): self._is_being_forwarded = install_forwarding(self) if self._is_being_forwarded and self.send_depends_on_forwarding: self._send_delayed_messages_task.restart() def script_wants_forwarding(self): """ Returns wether the script wants to receive receive the values, otherwise, the control will be mapped to the track. Subclasses that overload this should _request_rebuild() whenever the property changes. """ return self._input_signal_listener_count > 0 or self._report_input def begin_gesture(self): """ Begins a modification on the input control element, meaning that we should consider the next flow of input data as a consistent gesture from the user. """ if self._parameter_to_map_to and not self._in_parameter_gesture: self._in_parameter_gesture = True self._parameter_to_map_to.begin_gesture() def end_gesture(self): """ Ends a modification of the input control element. See begin_gesture. """ if self._parameter_to_map_to and self._in_parameter_gesture: self._in_parameter_gesture = False self._parameter_to_map_to.end_gesture() def connect_to(self, parameter): """ parameter is a Live.Device.DeviceParameter """ if not parameter != None: raise AssertionError self._parameter_to_map_to = self._parameter_to_map_to != parameter and parameter self._request_rebuild() def release_parameter(self): if self._parameter_to_map_to != None: self.end_gesture() self._parameter_to_map_to = None self._request_rebuild() def mapped_parameter(self): return self._parameter_to_map_to def _status_byte(self, channel): status_byte = channel if self._msg_type == MIDI_NOTE_TYPE: status_byte += MIDI_NOTE_ON_STATUS elif self._msg_type == MIDI_CC_TYPE: status_byte += MIDI_CC_STATUS elif self._msg_type == MIDI_PB_TYPE: status_byte += MIDI_PB_STATUS else: raise NotImplementedError return status_byte def identifier_bytes(self): """ Returns a list with all the MIDI message prefixes that identify this control element. """ if self._msg_type == MIDI_PB_TYPE: return ((self._status_byte(self._msg_channel),),) elif self._msg_type == MIDI_SYSEX_TYPE: return (self.message_sysex_identifier(),) elif self._msg_type == MIDI_NOTE_TYPE: return ((self._status_byte(self._msg_channel), self.message_identifier()), (self._status_byte(self._msg_channel) - 16, self.message_identifier())) else: return ((self._status_byte(self._msg_channel), self.message_identifier()),) def _send_delayed_messages(self): self.clear_send_cache() for value, channel in self._delayed_messages: self._do_send_value(value, channel=channel) self._delayed_messages[:] = [] def send_value(self, value, force_send = False, channel = None): value = int(value) self._verify_value(value) if force_send or self._force_next_send: self._do_send_value(value, channel) elif self.send_depends_on_forwarding and not self._is_being_forwarded or self._send_delayed_messages_task.is_running: first = 1 - self.num_delayed_messages self._delayed_messages = self._delayed_messages[first:] + [(value, channel)] elif (value, channel) != self._last_sent_message: self._do_send_value(value, channel) self._force_next_send = False def _do_send_value(self, value, channel = None): data_byte1 = self._original_identifier data_byte2 = value status_byte = self._status_byte(channel or self._original_channel) if self._msg_type == MIDI_PB_TYPE: data_byte1 = value & 127 data_byte2 = value >> 7 & 127 if self.send_midi((status_byte, data_byte1, data_byte2)): self._last_sent_message = (value, channel) if self._report_output: is_input = True self._report_value(value, not is_input) self._delayed_value_to_send = None self._delayed_channel = None def clear_send_cache(self): self._last_sent_message = None def reset(self): """ Send 0 to reset motorized faders and turn off LEDs """ self.send_value(0) def receive_value(self, value): self._verify_value(value) self._last_sent_message = None self.notify_value(value) if self._report_input: is_input = True self._report_value(value, is_input) def set_report_values(self, report_input, report_output): """ Set boolean values report_input and report_output enabling debug information. """ self._report_input = report_input self._report_output = report_output def _verify_value(self, value): upper_bound = self._msg_type < MIDI_SYSEX_TYPE and (16384 if self._msg_type == MIDI_PB_TYPE else 128) if not in_range(value, 0, upper_bound): raise AssertionError def _report_value(self, value, is_input): self._verify_value(value) message = str(self.__class__.__name__) + ' (' if self._msg_type == MIDI_NOTE_TYPE: message += 'Note ' + str(self._msg_identifier) + ', ' elif self._msg_type == MIDI_CC_TYPE: message += 'CC ' + str(self._msg_identifier) + ', ' else: message += 'PB ' message += 'Chan. ' + str(self._msg_channel) message += ') ' message += 'received value ' if is_input else 'sent value ' message += str(value) debug_print(message) @property def _last_sent_value(self): return self._last_sent_message[0] if self._last_sent_message else -1
def __init__(self, *a, **k): super(OptimizedControlSurface, self).__init__(*a, **k) self._optimized_ownership_handler = OptimizedOwnershipHandler() injecting = inject(element_ownership_handler=const(self._optimized_ownership_handler)) self._ownership_handler_injector = injecting.everywhere()