aboutsummaryrefslogtreecommitdiff
path: root/helpers/action.py
diff options
context:
space:
mode:
Diffstat (limited to 'helpers/action.py')
-rw-r--r--helpers/action.py324
1 files changed, 86 insertions, 238 deletions
diff --git a/helpers/action.py b/helpers/action.py
index ec8fcb6..1f374ec 100644
--- a/helpers/action.py
+++ b/helpers/action.py
@@ -1,263 +1,111 @@
1import threading 1from transitions.extensions import HierarchicalMachine as Machine
2import time 2from . import debug_print, error_print
3 3from . import actions
4from . import debug_print
5 4
6class Action: 5class Action:
7 action_types = [ 6 STATES = [
8 'command', 7 'initial',
9 'interrupt_wait', 8 'loading',
10 'pause', 9 'failed',
11 'play', 10 {
12 'seek', 11 'name': 'loaded',
13 'stop', 12 'children': ['running']
14 'stop_all_actions', 13 }
15 'unpause', 14 ]
16 'volume', 15
17 'wait', 16 TRANSITIONS = [
17 {
18 'trigger': 'load',
19 'source': 'initial',
20 'dest': 'loading'
21 },
22 {
23 'trigger': 'fail',
24 'source': 'loading',
25 'dest': 'failed',
26 'after': 'poll_loaded'
27 },
28 {
29 'trigger': 'success',
30 'source': 'loading',
31 'dest': 'loaded',
32 'after': 'poll_loaded'
33 },
34 {
35 'trigger': 'run',
36 'source': 'loaded',
37 'dest': 'loaded_running',
38 'after': 'finish_action',
39 # if a child has no transitions, then it is bubbled to the parent,
40 # and we don't want that. Not useful in that machine precisely.
41 'conditions': ['is_loaded']
42 },
43 {
44 'trigger': 'finish_action',
45 'source': 'loaded_running',
46 'dest': 'loaded'
47 }
18 ] 48 ]
19 49
20 def __init__(self, action, key, **kwargs): 50 def __init__(self, action, key, **kwargs):
21 if action in self.action_types: 51 Machine(model=self, states=self.STATES,
22 self.action = action 52 transitions=self.TRANSITIONS, initial='initial',
23 else: 53 ignore_invalid_triggers=True, queued=True)
24 raise Exception("Unknown action {}".format(action))
25 54
55 self.action = action
26 self.key = key 56 self.key = key
27 self.mapping = key.parent 57 self.mapping = key.parent
28 self.arguments = kwargs 58 self.arguments = kwargs
29 self.sleep_event = None 59 self.sleep_event = None
60 self.waiting_music = None
61
62 def is_loaded_or_failed(self):
63 return self.is_loaded(allow_substates=True) or self.is_failed()
30 64
31 def ready(self): 65 def callback_music_loaded(self, success):
32 if 'music' in self.arguments: 66 if success:
33 return self.arguments['music'].is_loaded(allow_substates=True) 67 self.success()
34 else: 68 else:
35 return True 69 self.fail()
36 70
37 def run(self): 71 # Machine states / events
72 def on_enter_loading(self):
73 if hasattr(actions, self.action):
74 if 'music' in self.arguments:
75 self.arguments['music'].subscribe_loaded(self.callback_music_loaded)
76 else:
77 self.success()
78 else:
79 error_print("Unknown action {}".format(self.action))
80 self.fail()
81
82 def on_enter_loaded_running(self):
38 debug_print(self.description()) 83 debug_print(self.description())
39 getattr(self, self.action)(**self.arguments) 84 if hasattr(actions, self.action):
85 getattr(actions, self.action).run(self, **self.arguments)
40 86
41 def description(self): 87 def poll_loaded(self):
42 return getattr(self, self.action + "_print")(**self.arguments) 88 self.key.callback_action_ready(self,
89 self.is_loaded(allow_substates=True))
43 90
91 # This one cannot be in the Machine state since it would be queued to run
92 # *after* the wait is ended...
44 def interrupt(self): 93 def interrupt(self):
45 if getattr(self, self.action + "_interrupt", None): 94 if getattr(actions, self.action, None) and\
46 return getattr(self, self.action + "_interrupt")(**self.arguments) 95 hasattr(getattr(actions, self.action), 'interrupt'):
96 return getattr(getattr(actions, self.action), 'interrupt')(
97 self, **self.arguments)
47 98
99 # Helpers
48 def music_list(self, music): 100 def music_list(self, music):
49 if music is not None: 101 if music is not None:
50 return [music] 102 return [music]
51 else: 103 else:
52 return self.mapping.open_files.values() 104 return self.mapping.open_files.values()
53 105
54 # Actions 106 def description(self):
55 def command(self, command="", **kwargs): 107 if hasattr(actions, self.action):
56 # FIXME: todo 108 return getattr(actions, self.action)\
57 pass 109 .description(self, **self.arguments)
58
59 def pause(self, music=None, **kwargs):
60 for music in self.music_list(music):
61 if music.is_loaded_playing():
62 music.pause()
63
64 def unpause(self, music=None, **kwargs):
65 for music in self.music_list(music):
66 if music.is_loaded_paused():
67 music.unpause()
68
69 def play(self, music=None, fade_in=0, start_at=0,
70 restart_if_running=False, volume=100,
71 loop=0, **kwargs):
72 for music in self.music_list(music):
73 if restart_if_running:
74 if music.is_in_use():
75 music.stop()
76 music.play(
77 volume=volume,
78 fade_in=fade_in,
79 start_at=start_at,
80 loop=loop)
81 elif not music.is_in_use():
82 music.play(
83 volume=volume,
84 fade_in=fade_in,
85 start_at=start_at,
86 loop=loop)
87
88 def seek(self, music=None, value=0, delta=False, **kwargs):
89 for music in self.music_list(music):
90 music.seek(value=value, delta=delta)
91
92 def interrupt_wait(self, wait_id=None):
93 self.mapping.interrupt_wait(wait_id)
94
95 def stop(self, music=None, fade_out=0, wait=False,
96 set_wait_id=None, **kwargs):
97 previous = None
98 for music in self.music_list(music):
99 if music.is_loaded_paused() or music.is_loaded_playing():
100 if previous is not None:
101 previous.stop(fade_out=fade_out)
102 previous = music
103 else:
104 music.stop(fade_out=fade_out)
105
106 if previous is not None:
107 previous.stop(
108 fade_out=fade_out,
109 wait=wait,
110 set_wait_id=set_wait_id)
111
112 def stop_all_actions(self, **kwargs):
113 self.mapping.stop_all_running()
114
115 def volume(self, music=None, value=100, fade=0, delta=False, **kwargs):
116 if music is not None:
117 music.set_volume(value, delta=delta, fade=fade)
118 else:
119 self.mapping.set_master_volume(value, delta=delta, fade=fade)
120
121 def wait(self, duration=0, music=None, set_wait_id=None, **kwargs):
122 if set_wait_id is not None:
123 self.mapping.add_wait_id(set_wait_id, self)
124
125 self.sleep_event = threading.Event()
126
127 if music is not None:
128 music.wait_end()
129
130 threading.Timer(duration, self.sleep_event.set).start()
131 self.sleep_event.wait()
132
133 # Action messages
134 def command_print(self, command="", **kwargs):
135 return "running command {}".format(command)
136
137 def interrupt_wait_print(self, wait_id=None, **kwargs):
138 return "interrupt wait with id {}".format(wait_id)
139
140 def pause_print(self, music=None, **kwargs):
141 if music is not None:
142 return "pausing « {} »".format(music.name)
143 else:
144 return "pausing all musics"
145
146 def unpause_print(self, music=None, **kwargs):
147 if music is not None:
148 return "unpausing « {} »".format(music.name)
149 else:
150 return "unpausing all musics"
151
152 def play_print(self, music=None, fade_in=0, start_at=0,
153 restart_if_running=False, volume=100, loop=0, **kwargs):
154 message = "starting "
155 if music is not None:
156 message += "« {} »".format(music.name)
157 else:
158 message += "all musics"
159
160 if start_at != 0:
161 message += " at {}s".format(start_at)
162
163 if fade_in != 0:
164 message += " with {}s fade_in".format(fade_in)
165
166 message += " at volume {}%".format(volume)
167
168 if loop > 0:
169 message += " {} times".format(loop + 1)
170 elif loop < 0:
171 message += " in loop"
172
173 if restart_if_running:
174 message += " (restarting if already running)"
175
176 return message
177
178 def stop_print(self, music=None, fade_out=0, wait=False,
179 set_wait_id=None, **kwargs):
180
181 message = "stopping "
182 if music is not None:
183 message += "music « {} »".format(music.name)
184 else:
185 message += "all musics"
186
187 if fade_out > 0:
188 message += " with {}s fadeout".format(fade_out)
189 if wait:
190 if set_wait_id is not None:
191 message += " (waiting the end of fadeout, with id {})"\
192 .format(set_wait_id)
193 else:
194 message += " (waiting the end of fadeout)"
195
196 return message
197
198 def stop_all_actions_print(self, **kwargs):
199 return "stopping all actions"
200
201 def seek_print(self, music=None, value=0, delta=False, **kwargs):
202 if delta:
203 if music is not None:
204 return "moving music « {} » by {:+d}s" \
205 .format(music.name, value)
206 else:
207 return "moving all musics by {:+d}s" \
208 .format(value)
209 else:
210 if music is not None:
211 return "moving music « {} » to position {}s" \
212 .format(music.name, value)
213 else:
214 return "moving all musics to position {}s" \
215 .format(value)
216
217 def volume_print(self, music=None,
218 value=100, delta=False, fade=0, **kwargs):
219 message = ""
220 if delta:
221 if music is not None:
222 message += "{:+d}% to volume of « {} »" \
223 .format(value, music.name)
224 else:
225 message += "{:+d}% to volume" \
226 .format(value)
227 else:
228 if music is not None:
229 message += "setting volume of « {} » to {}%" \
230 .format(music.name, value)
231 else:
232 message += "setting volume to {}%" \
233 .format(value)
234
235 if fade > 0:
236 message += " with {}s fade".format(fade)
237
238 return message
239
240 def wait_print(self, duration=0, music=None, set_wait_id=None, **kwargs):
241 message = ""
242 if music is None:
243 message += "waiting {}s" \
244 .format(duration)
245 elif duration == 0:
246 message += "waiting the end of « {} »" \
247 .format(music.name)
248 else: 110 else:
249 message += "waiting the end of « {} » + {}s" \ 111 return "unknown action {}".format(self.action)
250 .format(music.name, duration)
251
252 if set_wait_id is not None:
253 message += " (setting id = {})".format(set_wait_id)
254
255 return message
256
257 # Interruptions
258 def wait_interrupt(self, duration=0, music=None, **kwargs):
259 if self.sleep_event is not None:
260 self.sleep_event.set()
261 if music is not None:
262 music.wait_event.set()
263