X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=music_sampler%2Fkey.py;h=e05bb169354a7e6afc395cdb655a27a306f0ac4b;hb=HEAD;hp=b73a29cf1b8b83a8ecef8587c628791d014e5daf;hpb=814c30c6835b25d06977af1dd2d1e565c45121cc;p=perso%2FImmae%2FProjets%2FPython%2FMusicSampler.git diff --git a/music_sampler/key.py b/music_sampler/key.py index b73a29c..e05bb16 100644 --- a/music_sampler/key.py +++ b/music_sampler/key.py @@ -9,7 +9,11 @@ import time import threading from transitions.extensions import HierarchicalMachine as Machine -class KeyMachine(Widget): +# All drawing operations should happen in the main thread +# https://github.com/kivy/kivy/wiki/Working-with-Python-threads-inside-a-Kivy-application +from kivy.clock import mainthread + +class KeyMachine(): STATES = [ 'initial', 'configuring', @@ -97,11 +101,15 @@ class KeyMachine(Widget): { 'trigger': 'repeat_protection_finished', 'source': 'loaded_protecting_repeat', - 'dest': 'loaded' + 'dest': 'loaded', + 'after': 'callback_action_state_changed' }, ] - state = StringProperty("") + def __setattr__(self, name, value): + if hasattr(self, 'initialized') and name == 'state': + self.key.update_state(value) + super().__setattr__(name, value) def __init__(self, key, **kwargs): self.key = key @@ -109,7 +117,8 @@ class KeyMachine(Widget): Machine(model=self, states=self.STATES, transitions=self.TRANSITIONS, initial='initial', ignore_invalid_triggers=True, queued=True) - super(KeyMachine, self).__init__(**kwargs) + + self.initialized = True # Machine states / events def is_loaded_or_failed(self): @@ -118,22 +127,22 @@ class KeyMachine(Widget): def is_loaded_inactive(self): return self.is_loaded_no_config() or self.is_loaded_no_actions() + @mainthread def on_enter_configuring(self): + self.destroy_actions() + self.key.unset_description() + self.key.unset_color() + if self.key.key_sym in self.key.parent.key_config: self.key.config = self.key.parent.key_config[self.key.key_sym] - self.key.actions = [] for key_action in self.key.config['actions']: self.key.add_action(key_action[0], **key_action[1]) if 'description' in self.key.config['properties']: self.key.set_description(self.key.config['properties']['description']) - else: - self.key.unset_description() if 'color' in self.key.config['properties']: self.key.set_color(self.key.config['properties']['color']) - else: - self.key.unset_color() self.success() else: self.no_config() @@ -145,18 +154,23 @@ class KeyMachine(Widget): else: self.no_actions() + def destroy_actions(self): + for action in self.key.actions: + action.destroy() + self.key.actions = [] + def run_actions(self, modifiers): self.key.parent.parent.ids['KeyList'].append(self.key.key_sym) debug_print("running actions for {}".format(self.key.key_sym)) start_time = time.time() - self.key.parent.start_running(self, start_time) + self.key.parent.start_running(self.key, start_time) for self.key.current_action in self.key.actions: - if self.key.parent.keep_running(self, start_time): + if self.key.parent.keep_running(self.key, start_time): self.key.list_actions() self.key.current_action.run(start_time) self.key.list_actions(last_action_finished=True) - self.key.parent.finished_running(self, start_time) + self.key.parent.finished_running(self.key, start_time) def on_enter_loaded_protecting_repeat(self, modifiers): if self.key.repeat_delay > 0: @@ -168,9 +182,21 @@ class KeyMachine(Widget): self.key.repeat_protection_finished() # Callbacks + @mainthread def key_loaded_callback(self): self.key.parent.key_loaded_callback() + def callback_action_state_changed(self): + if self.state not in ['failed', 'loading', 'loaded']: + return + + if any(action.is_failed() for action in self.key.actions): + self.to_failed() + elif any(action.is_loading() for action in self.key.actions): + self.to_loading() + else: + self.to_loaded() + self.key_loaded_callback() class Key(ButtonBehavior, Widget): @@ -234,18 +260,18 @@ class Key(ButtonBehavior, Widget): else: raise AttributeError - def machine_state_changed(self, instance, machine_state): - self.machine_state = self.machine.state - def __init__(self, **kwargs): self.actions = [] self.current_action = None self.machine = KeyMachine(self) - self.machine.bind(state=self.machine_state_changed) super(Key, self).__init__(**kwargs) # Kivy events + @mainthread + def update_state(self, value): + self.machine_state = value + def on_key_sym(self, key, key_sym): if key_sym != "": self.configure() @@ -258,13 +284,6 @@ class Key(ButtonBehavior, Widget): def interrupt(self): self.current_action.interrupt() - # Callbacks - def callback_action_ready(self, action, success): - if not success: - self.fail() - elif all(action.is_loaded_or_failed() for action in self.actions): - self.success() - # Setters def set_description(self, description): if description[0] is not None: @@ -290,8 +309,9 @@ class Key(ButtonBehavior, Widget): # Helpers @property def repeat_delay(self): - if 'repeat_delay' in self.key.config['properties']: - return self.key.config['properties']['repeat_delay'] + if hasattr(self, 'config') and\ + 'repeat_delay' in self.config['properties']: + return self.config['properties']['repeat_delay'] else: return 0