]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blobdiff - music_sampler/action.py
Cleanup key and action workflows
[perso/Immae/Projets/Python/MusicSampler.git] / music_sampler / action.py
index 22a2bdc9feeb3231fa8a9c80f7b7d72f02c0d71a..bc62f33c7a24492757d8f801341351fb3b8b8770 100644 (file)
@@ -9,8 +9,9 @@ class Action:
         'failed',
         {
             'name': 'loaded',
-            'children': ['running']
-        }
+            'children': ['stopped', 'running']
+        },
+        'destroyed'
     ]
 
     TRANSITIONS = [
@@ -21,36 +22,42 @@ class Action:
         },
         {
             'trigger': 'fail',
-            'source': 'loading',
+            'source': ['loading', 'loaded'],
             'dest': 'failed',
-            'after': 'poll_loaded'
         },
         {
             'trigger': 'success',
             'source': 'loading',
-            'dest': 'loaded',
-            'after': 'poll_loaded'
+            'dest': 'loaded_stopped',
         },
         {
-            'trigger': 'run',
+            'trigger': 'reload',
             'source': 'loaded',
+            'dest': 'loading',
+        },
+        {
+            'trigger': 'run',
+            'source': 'loaded_stopped',
             'dest': 'loaded_running',
             'after': 'finish_action',
-            # if a child has no transitions, then it is bubbled to the parent,
-            # and we don't want that. Not useful in that machine precisely.
-            'conditions': ['is_loaded']
         },
         {
             'trigger': 'finish_action',
             'source': 'loaded_running',
-            'dest': 'loaded'
+            'dest': 'loaded_stopped'
+        },
+        {
+            'trigger': 'destroy',
+            'source': '*',
+            'dest': 'destroyed'
         }
     ]
 
     def __init__(self, action, key, **kwargs):
         Machine(model=self, states=self.STATES,
                 transitions=self.TRANSITIONS, initial='initial',
-                ignore_invalid_triggers=True, queued=True)
+                ignore_invalid_triggers=True, queued=True,
+                after_state_change=self.notify_state_change)
 
         self.action = action
         self.key = key
@@ -62,18 +69,31 @@ class Action:
     def is_loaded_or_failed(self):
         return self.is_loaded(allow_substates=True) or self.is_failed()
 
-    def callback_music_loaded(self, success):
-        if success:
-            self.success()
-        else:
+    def callback_music_state(self, new_state):
+        # If a music gets unloaded while the action is loaded_running and
+        # depending on the music, it won't be able to do the finish_action.
+        # Can that happen?
+        # a: play 'mp3';
+        # z: wait 'mp3';
+        # e: pause 'mp3';
+        # r: stop 'mp3'; unload_music 'mp3'
+        if new_state == 'failed':
             self.fail()
+        elif self.is_loaded(allow_substates=True) and\
+                new_state in ['initial', 'loading']:
+            self.reload(reloading=True)
+        elif self.is_loading() and new_state.startswith('loaded_'):
+            self.success()
 
     # Machine states / events
-    def on_enter_loading(self):
+    def on_enter_loading(self, reloading=False):
+        if reloading:
+            return
         if hasattr(actions, self.action):
-            if 'music' in self.arguments:
-                self.arguments['music'].subscribe_loaded(
-                        self.callback_music_loaded)
+            if 'music' in self.arguments and\
+                    self.action not in ['unload_music', 'load_music']:
+                self.arguments['music'].subscribe_state_change(
+                        self.callback_music_state)
             else:
                 self.success()
         else:
@@ -86,9 +106,13 @@ class Action:
             getattr(actions, self.action).run(self,
                     key_start_time=key_start_time, **self.arguments)
 
-    def poll_loaded(self):
-        self.key.callback_action_ready(self,
-                self.is_loaded(allow_substates=True))
+    def on_enter_destroyed(self):
+        if 'music' in self.arguments:
+            self.arguments['music'].unsubscribe_state_change(
+                    self.callback_music_state)
+
+    def notify_state_change(self, *args, **kwargs):
+        self.key.callback_action_state_changed()
 
     # This one cannot be in the Machine state since it would be queued to run
     # *after* the wait is ended...