]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/commitdiff
Make callbacks when key is ready
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Tue, 26 Jul 2016 20:59:41 +0000 (22:59 +0200)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Tue, 26 Jul 2016 20:59:41 +0000 (22:59 +0200)
helpers/key.py
helpers/mapping.py

index bf46eebc73237520770daeb01dc225448a4685e4..7ad0565045b9a8bbea8955318059c1e8a915a5e1 100644 (file)
@@ -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()
index 6e3b29153e4d817205d015cf8a441f56da654189..9c059725dcb54bd4beda9fc151298c051c531e3f 100644 (file)
@@ -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):