]>
Commit | Line | Data |
---|---|---|
1 | from transitions.extensions import HierarchicalMachine as Machine | |
2 | from .helpers import debug_print, error_print | |
3 | from . import actions | |
4 | ||
5 | class Action: | |
6 | STATES = [ | |
7 | 'initial', | |
8 | 'loading', | |
9 | 'failed', | |
10 | { | |
11 | 'name': 'loaded', | |
12 | 'children': ['stopped', 'running'] | |
13 | }, | |
14 | 'destroyed' | |
15 | ] | |
16 | ||
17 | TRANSITIONS = [ | |
18 | { | |
19 | 'trigger': 'load', | |
20 | 'source': 'initial', | |
21 | 'dest': 'loading' | |
22 | }, | |
23 | { | |
24 | 'trigger': 'fail', | |
25 | 'source': ['loading', 'loaded'], | |
26 | 'dest': 'failed', | |
27 | }, | |
28 | { | |
29 | 'trigger': 'success', | |
30 | 'source': 'loading', | |
31 | 'dest': 'loaded_stopped', | |
32 | }, | |
33 | { | |
34 | 'trigger': 'reload', | |
35 | 'source': 'loaded', | |
36 | 'dest': 'loading', | |
37 | }, | |
38 | { | |
39 | 'trigger': 'run', | |
40 | 'source': 'loaded_stopped', | |
41 | 'dest': 'loaded_running', | |
42 | 'after': 'finish_action', | |
43 | }, | |
44 | { | |
45 | 'trigger': 'finish_action', | |
46 | 'source': 'loaded_running', | |
47 | 'dest': 'loaded_stopped' | |
48 | }, | |
49 | { | |
50 | 'trigger': 'destroy', | |
51 | 'source': '*', | |
52 | 'dest': 'destroyed' | |
53 | } | |
54 | ] | |
55 | ||
56 | def __init__(self, action, key, **kwargs): | |
57 | Machine(model=self, states=self.STATES, | |
58 | transitions=self.TRANSITIONS, initial='initial', | |
59 | ignore_invalid_triggers=True, queued=True, | |
60 | after_state_change=self.notify_state_change) | |
61 | ||
62 | self.action = action | |
63 | self.key = key | |
64 | self.mapping = key.parent | |
65 | self.arguments = kwargs | |
66 | self.sleep_event = None | |
67 | self.waiting_music = None | |
68 | ||
69 | def is_loaded_or_failed(self): | |
70 | return self.is_loaded(allow_substates=True) or self.is_failed() | |
71 | ||
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': | |
81 | self.fail() | |
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() | |
87 | ||
88 | # Machine states / events | |
89 | def on_enter_loading(self, reloading=False): | |
90 | if reloading: | |
91 | return | |
92 | if hasattr(actions, self.action): | |
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) | |
97 | else: | |
98 | self.success() | |
99 | else: | |
100 | error_print("Unknown action {}".format(self.action)) | |
101 | self.fail() | |
102 | ||
103 | def on_enter_loaded_running(self, key_start_time): | |
104 | debug_print(self.description()) | |
105 | if hasattr(actions, self.action): | |
106 | getattr(actions, self.action).run(self, | |
107 | key_start_time=key_start_time, **self.arguments) | |
108 | ||
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() | |
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): | |
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) | |
124 | ||
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 | ||
143 | # Helpers | |
144 | def music_list(self, music): | |
145 | if music is not None: | |
146 | return [music] | |
147 | else: | |
148 | return self.mapping.open_files.values() | |
149 | ||
150 | def description(self): | |
151 | if hasattr(actions, self.action): | |
152 | return getattr(actions, self.action)\ | |
153 | .description(self, **self.arguments) | |
154 | else: | |
155 | return _("unknown action {}").format(self.action) |