From: Ismaƫl Bouya Date: Tue, 26 Jul 2016 20:59:41 +0000 (+0200) Subject: Make callbacks when key is ready X-Git-Tag: 1.0.0~20 X-Git-Url: https://git.immae.eu/?p=perso%2FImmae%2FProjets%2FPython%2FMusicSampler.git;a=commitdiff_plain;h=8ba7d831a1b8da01ba9e86491d7740f674910a53 Make callbacks when key is ready --- diff --git a/helpers/key.py b/helpers/key.py index bf46eeb..7ad0565 100644 --- a/helpers/key.py +++ b/helpers/key.py @@ -30,7 +30,8 @@ class Key(ButtonBehavior, Widget): { 'trigger': 'fail', 'source': 'configuring', - 'dest': 'failed' + 'dest': 'failed', + 'after': 'key_loaded_callback' }, { 'trigger': 'success', @@ -42,6 +43,7 @@ class Key(ButtonBehavior, Widget): 'trigger': 'no_config', 'source': 'configuring', 'dest': 'loaded_no_config', + 'after': 'key_loaded_callback' }, { 'trigger': 'load', @@ -51,22 +53,26 @@ class Key(ButtonBehavior, Widget): { 'trigger': 'fail', 'source': 'loading', - 'dest': 'failed' + 'dest': 'failed', + 'after': 'key_loaded_callback' }, { 'trigger': 'success', 'source': 'loading', - 'dest': 'loaded' + 'dest': 'loaded', + 'after': 'key_loaded_callback' }, { 'trigger': 'no_actions', 'source': 'loading', 'dest': 'loaded_no_actions', + 'after': 'key_loaded_callback' }, { 'trigger': 'reload', 'source': 'loaded', - 'dest': 'configuring' + 'dest': 'configuring', + 'after': 'key_loaded_callback' }, { 'trigger': 'run', @@ -171,6 +177,9 @@ class Key(ButtonBehavior, Widget): self.current_action.interrupt() # Callbacks + def key_loaded_callback(self): + self.parent.key_loaded_callback() + def callback_action_ready(self, action, success): if not success: self.fail() diff --git a/helpers/mapping.py b/helpers/mapping.py index 6e3b291..9c05972 100644 --- a/helpers/mapping.py +++ b/helpers/mapping.py @@ -8,13 +8,57 @@ import yaml import sys from collections import defaultdict +from transitions.extensions import HierarchicalMachine as Machine + from .music_file import MusicFile from .mixer import Mixer from . import Config, gain, error_print, warn_print from .action import Action class Mapping(RelativeLayout): - expected_keys = NumericProperty(0) + STATES = [ + 'initial', + 'configuring', + 'configured', + 'loading', + 'loaded', + 'failed' + ] + + TRANSITIONS = [ + { + 'trigger': 'configure', + 'source': 'initial', + 'dest': 'configuring' + }, + { + 'trigger': 'fail', + 'source': 'configuring', + 'dest': 'failed' + }, + { + 'trigger': 'success', + 'source': 'configuring', + 'dest': 'configured', + 'after': 'load' + }, + { + 'trigger': 'load', + 'source': 'configured', + 'dest': 'loading' + }, + { + 'trigger': 'fail', + 'source': 'loading', + 'dest': 'failed' + }, + { + 'trigger': 'success', + 'source': 'loading', + 'dest': 'loaded' + } + ] + master_volume = NumericProperty(100) ready_color = ListProperty([1, 165/255, 0, 1]) @@ -31,42 +75,30 @@ class Mapping(RelativeLayout): with_trace=True) sys.exit() - super(Mapping, self).__init__(**kwargs) - self._keyboard = Window.request_keyboard(self._keyboard_closed, self) - self._keyboard.bind(on_key_down=self._on_keyboard_down) + self.keys = [] self.running = [] self.wait_ids = {} - Clock.schedule_interval(self.not_all_keys_ready, 1) - - @property - def master_gain(self): - return gain(self.master_volume) - def set_master_volume(self, value, delta=False, fade=0): - [db_gain, self.master_volume] = gain( - value + int(delta) * self.master_volume, - self.master_volume) - - for music in self.open_files.values(): - music.set_gain_with_effect(db_gain, fade=fade) + super(Mapping, self).__init__(**kwargs) + self.keyboard = Window.request_keyboard(self.on_keyboard_closed, self) + self.keyboard.bind(on_key_down=self.on_keyboard_down) - def add_wait_id(self, wait_id, action_or_wait): - self.wait_ids[wait_id] = action_or_wait + # Kivy events + def add_widget(self, widget, index=0): + if type(widget).__name__ == "Key" and widget not in self.keys: + self.keys.append(widget) + return super(Mapping, self).add_widget(widget, index) - def interrupt_wait(self, wait_id): - if wait_id in self.wait_ids: - action_or_wait = self.wait_ids[wait_id] - del(self.wait_ids[wait_id]) - if isinstance(action_or_wait, Action): - action_or_wait.interrupt() - else: - action_or_wait.set() + def remove_widget(self, widget, index=0): + if type(widget).__name__ == "Key" and widget in self.keys: + self.keys.remove(widget) + return super(Mapping, self).remove_widget(widget, index) - def _keyboard_closed(self): - self._keyboard.unbind(on_key_down=self._on_keyboard_down) - self._keyboard = None + def on_keyboard_closed(self): + self.keyboard.unbind(on_key_down=self.on_keyboard_down) + self.keyboard = None - def _on_keyboard_down(self, keyboard, keycode, text, modifiers): + def on_keyboard_down(self, keyboard, keycode, text, modifiers): key = self.find_by_key_code(keycode) if len(modifiers) == 0 and key is not None: threading.Thread(name="MSKeyAction", target=key.run).start() @@ -85,21 +117,60 @@ class Mapping(RelativeLayout): return self.ids["Key_" + str(key_code[0])] return None - def not_all_keys_ready(self, dt): - for key in self.children: - if not type(key).__name__ == "Key": - continue + def all_keys_ready(self): + partial = False + for key in self.keys: if not key.is_loaded_or_failed(): - return True - self.ready_color = [0, 1, 0, 1] - return False + return "not_ready" + partial = partial or key.is_failed() + if partial: + return "partial" + else: + return "success" + + # Callbacks + def key_loaded_callback(self): + result = self.all_keys_ready() + if result == "success": + self.ready_color = [0, 1, 0, 1] + elif result == "partial": + self.ready_color = [1, 0, 0, 1] + + ## Some global actions def stop_all_running(self): running = self.running self.running = [] for (key, start_time) in running: key.interrupt() + # Master volume methods + @property + def master_gain(self): + return gain(self.master_volume) + + def set_master_volume(self, value, delta=False, fade=0): + [db_gain, self.master_volume] = gain( + value + int(delta) * self.master_volume, + self.master_volume) + + for music in self.open_files.values(): + music.set_gain_with_effect(db_gain, fade=fade) + + # Wait handler methods + def add_wait_id(self, wait_id, action_or_wait): + self.wait_ids[wait_id] = action_or_wait + + def interrupt_wait(self, wait_id): + if wait_id in self.wait_ids: + action_or_wait = self.wait_ids[wait_id] + del(self.wait_ids[wait_id]) + if isinstance(action_or_wait, Action): + action_or_wait.interrupt() + else: + action_or_wait.set() + + # Methods to control running keys def start_running(self, key, start_time): self.running.append((key, start_time)) @@ -110,6 +181,7 @@ class Mapping(RelativeLayout): if (key, start_time) in self.running: self.running.remove((key, start_time)) + # YML config parser def parse_config(self): def update_alias(prop_hash, aliases, key): if isinstance(aliases[key], dict):