]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blame - music_sampler/action.py
Cleanup key and action workflows
[perso/Immae/Projets/Python/MusicSampler.git] / music_sampler / action.py
CommitLineData
b7ca3fc2 1from transitions.extensions import HierarchicalMachine as Machine
6ebe6247 2from .helpers import debug_print, error_print
c4f4f2a1 3from . import actions
a24c34bc 4
be27763f 5class Action:
b7ca3fc2
IB
6 STATES = [
7 'initial',
8 'loading',
9 'failed',
10 {
11 'name': 'loaded',
93a3e51e
IB
12 'children': ['stopped', 'running']
13 },
14 'destroyed'
b7ca3fc2
IB
15 ]
16
17 TRANSITIONS = [
18 {
19 'trigger': 'load',
20 'source': 'initial',
21 'dest': 'loading'
22 },
23 {
24 'trigger': 'fail',
93a3e51e 25 'source': ['loading', 'loaded'],
e55b29bb 26 'dest': 'failed',
b7ca3fc2
IB
27 },
28 {
29 'trigger': 'success',
30 'source': 'loading',
93a3e51e 31 'dest': 'loaded_stopped',
b7ca3fc2
IB
32 },
33 {
93a3e51e 34 'trigger': 'reload',
b7ca3fc2 35 'source': 'loaded',
93a3e51e
IB
36 'dest': 'loading',
37 },
38 {
39 'trigger': 'run',
40 'source': 'loaded_stopped',
b7ca3fc2 41 'dest': 'loaded_running',
e55b29bb 42 'after': 'finish_action',
b7ca3fc2
IB
43 },
44 {
45 'trigger': 'finish_action',
46 'source': 'loaded_running',
93a3e51e
IB
47 'dest': 'loaded_stopped'
48 },
49 {
50 'trigger': 'destroy',
51 'source': '*',
52 'dest': 'destroyed'
b7ca3fc2
IB
53 }
54 ]
55
be27763f 56 def __init__(self, action, key, **kwargs):
b7ca3fc2
IB
57 Machine(model=self, states=self.STATES,
58 transitions=self.TRANSITIONS, initial='initial',
93a3e51e
IB
59 ignore_invalid_triggers=True, queued=True,
60 after_state_change=self.notify_state_change)
be27763f 61
b7ca3fc2 62 self.action = action
be27763f 63 self.key = key
1b4b78f5 64 self.mapping = key.parent
be27763f 65 self.arguments = kwargs
0deb82a5 66 self.sleep_event = None
b7ca3fc2 67 self.waiting_music = None
be27763f 68
e55b29bb
IB
69 def is_loaded_or_failed(self):
70 return self.is_loaded(allow_substates=True) or self.is_failed()
b7ca3fc2 71
93a3e51e
IB
72 def callback_music_state(self, new_state):
73 # If a music gets unloaded while the action is loaded_running and
74 # depending on the music, it won't be able to do the finish_action.
75 # Can that happen?
76 # a: play 'mp3';
77 # z: wait 'mp3';
78 # e: pause 'mp3';
79 # r: stop 'mp3'; unload_music 'mp3'
80 if new_state == 'failed':
b7ca3fc2 81 self.fail()
93a3e51e
IB
82 elif self.is_loaded(allow_substates=True) and\
83 new_state in ['initial', 'loading']:
84 self.reload(reloading=True)
85 elif self.is_loading() and new_state.startswith('loaded_'):
86 self.success()
b7ca3fc2
IB
87
88 # Machine states / events
93a3e51e
IB
89 def on_enter_loading(self, reloading=False):
90 if reloading:
91 return
c4f4f2a1 92 if hasattr(actions, self.action):
93a3e51e
IB
93 if 'music' in self.arguments and\
94 self.action not in ['unload_music', 'load_music']:
95 self.arguments['music'].subscribe_state_change(
96 self.callback_music_state)
b7ca3fc2
IB
97 else:
98 self.success()
be27763f 99 else:
b7ca3fc2
IB
100 error_print("Unknown action {}".format(self.action))
101 self.fail()
102
62a8b07a 103 def on_enter_loaded_running(self, key_start_time):
a24c34bc 104 debug_print(self.description())
c4f4f2a1 105 if hasattr(actions, self.action):
62a8b07a
IB
106 getattr(actions, self.action).run(self,
107 key_start_time=key_start_time, **self.arguments)
be27763f 108
93a3e51e
IB
109 def on_enter_destroyed(self):
110 if 'music' in self.arguments:
111 self.arguments['music'].unsubscribe_state_change(
112 self.callback_music_state)
113
114 def notify_state_change(self, *args, **kwargs):
115 self.key.callback_action_state_changed()
e55b29bb
IB
116
117 # This one cannot be in the Machine state since it would be queued to run
118 # *after* the wait is ended...
119 def interrupt(self):
c4f4f2a1
IB
120 if getattr(actions, self.action, None) and\
121 hasattr(getattr(actions, self.action), 'interrupt'):
122 return getattr(getattr(actions, self.action), 'interrupt')(
123 self, **self.arguments)
0deb82a5 124
d4217fda
IB
125 def pause(self):
126 if getattr(actions, self.action, None) and\
127 hasattr(getattr(actions, self.action), 'pause'):
128 return getattr(getattr(actions, self.action), 'pause')(
129 self, **self.arguments)
130
131 def unpause(self):
132 if getattr(actions, self.action, None) and\
133 hasattr(getattr(actions, self.action), 'unpause'):
134 return getattr(getattr(actions, self.action), 'unpause')(
135 self, **self.arguments)
136
137 def reset(self):
138 if getattr(actions, self.action, None) and\
139 hasattr(getattr(actions, self.action), 'reset'):
140 return getattr(getattr(actions, self.action), 'reset')(
141 self, **self.arguments)
142
b7ca3fc2 143 # Helpers
29597680 144 def music_list(self, music):
be27763f 145 if music is not None:
29597680 146 return [music]
be27763f 147 else:
1b4b78f5 148 return self.mapping.open_files.values()
29597680 149
b7ca3fc2 150 def description(self):
c4f4f2a1
IB
151 if hasattr(actions, self.action):
152 return getattr(actions, self.action)\
153 .description(self, **self.arguments)
b7ca3fc2 154 else:
6a327173 155 return _("unknown action {}").format(self.action)