]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/commitdiff
Move classes to separate file
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Sat, 18 Jun 2016 20:13:19 +0000 (22:13 +0200)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Sat, 18 Jun 2016 20:13:19 +0000 (22:13 +0200)
helpers/__init__.py
helpers/action.py [new file with mode: 0644]
helpers/key.py [new file with mode: 0644]
helpers/mapping.py [new file with mode: 0644]
helpers/music_file.py [new file with mode: 0644]
helpers/rounded_rect.py [new file with mode: 0644]
music_sampler.py

index 8570008f265efe7e86ae1f21be28c9c68213cae8..6935342cc34a74f0304f4bd83bb42a335a7a1cb2 100644 (file)
 # -*- coding: utf-8 -*-
-from pygame import *
-import pydub
-import sys
-import time
 import threading
+from .music_file import *
+from .mapping import *
 
 draw_lock = threading.RLock()
 
-class Action:
-    action_types = [
-        'command',
-        'pause',
-        'play',
-        'stop',
-        'stop_all_actions',
-        'volume',
-        'wait',
-    ]
-
-    def __init__(self, action, key, **kwargs):
-        if action in self.action_types:
-            self.action = action
-        else:
-            raise Exception("Unknown action {}".format(action))
-
-        self.key = key
-        self.arguments = kwargs
-
-    def ready(self):
-        if 'music' in self.arguments:
-            return self.arguments['music'].loaded
-        else:
-            return True
-
-    def run(self):
-        print(getattr(self, self.action + "_print")(**self.arguments))
-        return getattr(self, self.action)(**self.arguments)
-
-    def command(self, command = "", **kwargs):
-        pass
-
-    def pause(self, music = None, **kwargs):
-        if music is not None:
-            music.pause()
-        else:
-            mixer.pause()
-
-    def play(self, music = None, fade_in = 0, start_at = 0,
-            restart_if_running = False, volume = 100, **kwargs):
-        if music is not None:
-            music.play()
-        else:
-            mixer.unpause()
-
-    def stop(self, music = None, fade_out = 0, **kwargs):
-        if music is not None:
-            music.stop()
-        else:
-            mixer.stop()
-
-    def stop_all_actions(self, **kwargs):
-        self.key.mapping.stop_all_running()
-
-    def volume(self, music = None, value = 100, **kwargs):
-        pass
-
-    def wait(self, duration = 0, **kwargs):
-        time.sleep(duration)
-
-    def command_print(self, command = "", **kwargs):
-        return "running command {}".format(command)
-
-    def pause_print(self, music = None, **kwargs):
-        if music is not None:
-            return "pausing {}".format(music.filename)
-        else:
-            return "pausing all musics"
-
-    def play_print(self, music = None, fade_in = 0, start_at = 0,
-            restart_if_running = False, volume = 100, **kwargs):
-        message = "starting "
-        if music is not None:
-            message += music.filename
-        else:
-            message += "music"
-
-        if start_at != 0:
-            message += " at {}s".format(start_at)
-
-        if fade_in != 0:
-            message += " with {}s fade_in".format(fade_in)
-
-        message += " at volume {}%".format(volume)
-
-        if restart_if_running:
-            message += " (restarting if already running)"
-
-        return message
-
-    def stop_print(self, music = None, fade_out = 0, **kwargs):
-        if music is not None:
-            if fade_out == 0:
-                return "stopping music {}".format(music.filename)
-            else:
-                return "stopping music {} with {}s fadeout".format(music.filename, fade_out)
-        else:
-            if fade_out == 0:
-                return "stopping all musics"
-            else:
-                return "stopping all musics with {}s fadeout".format(fade_out)
-
-    def stop_all_actions_print(self):
-        return "stopping all actions"
-
-    def volume_print(self, music = None, value = 100, *kwargs):
-        if music is not None:
-            return "setting volume of {} to {}%".format(music.filename, value)
-        else:
-            return "setting volume to {}%".format(value)
-
-    def wait_print(self, duration, **kwargs):
-        return "waiting {}s".format(duration)
-
-class Key:
-    row_positions = {
-        'first':    0,
-        'second':  50,
-        'third':  100,
-        'fourth': 150,
-        'fifth':  200,
-        'sixth':  250,
-    }
-
-    default_outer_color = (120, 120, 120)
-    lighter_outer_color = (200, 200, 200)
-    default_inner_color = (255, 255, 255)
-    mapped_inner_color  = (  0, 255,   0)
-    mapped_unready_inner_color = (255, 165, 0)
-
-    def __init__(self, mapping, key_name, key_sym, top, left, width = 48, height = 48, disabled = False):
-        self.mapping = mapping
-        self.key_name = key_name
-        self.key_sym  = key_sym
-
-        if isinstance(top, str):
-            self.top = self.row_positions[top]
-        else:
-            self.top = top
-
-        self.left   = left
-        self.width  = width
-        self.height = height
-
-        self.bottom = self.top  + self.height
-        self.right  = self.left + self.width
-
-        self.rect     = (self.left, self.top, self.right, self.bottom)
-        self.position = (self.left, self.top)
-
-        if disabled:
-            self.outer_color = self.lighter_outer_color
-            self.linewidth = 1
-        else:
-            self.outer_color = self.default_outer_color
-            self.linewidth = 3
-
-        self.inner_color = self.default_inner_color
-        self.actions = []
-
-    def square(self, all_actions_ready):
-        if self.has_actions():
-            if all_actions_ready:
-                self.inner_color = self.mapped_inner_color
-            else:
-                self.inner_color = self.mapped_unready_inner_color
-
-        return RoundedRect((0, 0, self.width, self.height),
-            self.outer_color, self.inner_color, self.linewidth)
-
-    def collidepoint(self, position):
-        return self.surface.get_rect().collidepoint(
-                position[0] - self.position[0],
-                position[1] - self.position[1]
-                )
-
-    def draw(self, background_surface):
-        draw_lock.acquire()
-        all_actions_ready = self.all_actions_ready()
-
-        self.surface = self.square(all_actions_ready).surface()
-
-        if getattr(sys, 'frozen', False):
-            police = font.Font(sys._MEIPASS + "/Ubuntu-Regular.ttf", 14)
-        else:
-            police = font.Font("Ubuntu-Regular.ttf", 14)
-
-        text = police.render(self.key_sym, True, (0,0,0))
-        self.surface.blit(text, (5,5))
-        background_surface.blit(self.surface, self.position)
-        draw_lock.release()
-
-        return not all_actions_ready
-
-    def poll_redraw(self, background):
-        while True:
-            time.sleep(1)
-            if self.all_actions_ready():
-                self.draw(background)
-                self.mapping.blit()
-                break
-
-    def has_actions(self):
-        return len(self.actions) > 0
-
-    def all_actions_ready(self):
-        return all(action.ready() for action in self.actions)
-
-    def add_action(self, action_name, **arguments):
-        self.actions.append(Action(action_name, self, **arguments))
-
-    def do_actions(self):
-        print("running actions for {}".format(self.key_sym))
-        start_time = time.time()
-        self.mapping.start_running(self, start_time)
-        for action in self.actions:
-            if self.mapping.keep_running(self, start_time):
-                action.run()
-
-        self.mapping.finished_running(self, start_time)
-
-    def list_actions(self, surface):
-        # FIXME: Todo
-        print("bouh", self.key_sym)
-        surface.fill((255, 0, 0))
-
-
-class Mapping:
-    WIDTH  = 903
-    HEIGHT = 298
-    SIZE   = WIDTH, HEIGHT
-
-    KEYS = [
-        (K_ESCAPE, 'ESC', 'first',   0, {}),
-
-        (K_F1,     'F1',  'first', 100, {}),
-        (K_F2,     'F2',  'first', 150, {}),
-        (K_F3,     'F3',  'first', 200, {}),
-        (K_F4,     'F4',  'first', 250, {}),
-
-        (K_F5,     'F5',  'first', 325, {}),
-        (K_F6,     'F6',  'first', 375, {}),
-        (K_F7,     'F7',  'first', 425, {}),
-        (K_F8,     'F8',  'first', 475, {}),
-
-        (K_F9,     'F9',  'first', 550, {}),
-        (K_F10,    'F10', 'first', 600, {}),
-        (K_F11,    'F11', 'first', 650, {}),
-        (K_F12,    'F12', 'first', 700, {}),
-
-
-        (178,          '²', 'second',   0, {}),
-        (K_AMPERSAND,  '&', 'second',  50, {}),
-        (233,          'é', 'second', 100, {}),
-        (K_QUOTEDBL,   '"', 'second', 150, {}),
-        (K_QUOTE,      "'", 'second', 200, {}),
-        (K_LEFTPAREN,  '(', 'second', 250, {}),
-        (K_MINUS,      '-', 'second', 300, {}),
-        (232,          'è', 'second', 350, {}),
-        (K_UNDERSCORE, '_', 'second', 400, {}),
-        (231,          'ç', 'second', 450, {}),
-        (224,          'à', 'second', 500, {}),
-        (K_RIGHTPAREN, ')', 'second', 550, {}),
-        (K_EQUALS,     '=', 'second', 600, {}),
-
-        (K_BACKSPACE,  '<-', 'second', 650, { 'width': 98 }),
-
-
-        (K_TAB,        'tab', 'third',   0, { 'width' : 73 }),
-        (K_a,          'a',   'third',  75, {}),
-        (K_z,          'z',   'third', 125, {}),
-        (K_e,          'e',   'third', 175, {}),
-        (K_r,          'r',   'third', 225, {}),
-        (K_t,          't',   'third', 275, {}),
-        (K_y,          'y',   'third', 325, {}),
-        (K_u,          'u',   'third', 375, {}),
-        (K_i,          'i',   'third', 425, {}),
-        (K_o,          'o',   'third', 475, {}),
-        (K_p,          'p',   'third', 525, {}),
-        (K_CARET,      '^',   'third', 575, {}),
-        (K_DOLLAR,     '$',   'third', 625, {}),
-
-        (K_RETURN, 'Enter', 'third', 692, { 'width': 56, 'height': 98 }),
-
-        (K_CAPSLOCK,    'CAPS', 'fourth',  0, { 'width': 88, 'disabled': True }),
-
-        (K_q,        'q', 'fourth',  90, {}),
-        (K_s,        's', 'fourth', 140, {}),
-        (K_d,        'd', 'fourth', 190, {}),
-        (K_f,        'f', 'fourth', 240, {}),
-        (K_g,        'g', 'fourth', 290, {}),
-        (K_h,        'h', 'fourth', 340, {}),
-        (K_j,        'j', 'fourth', 390, {}),
-        (K_k,        'k', 'fourth', 440, {}),
-        (K_l,        'l', 'fourth', 490, {}),
-        (K_m,        'm', 'fourth', 540, {}),
-        (249,        'ù', 'fourth', 590, {}),
-        (K_ASTERISK, '*', 'fourth', 640, {}),
-
-
-        (K_LSHIFT, 'LShift', 'fifth', 0, { 'width': 63, 'disabled': True }),
-
-        (K_LESS,      '<', 'fifth',  65, {}),
-        (K_w,         'w', 'fifth', 115, {}),
-        (K_x,         'x', 'fifth', 165, {}),
-        (K_c,         'c', 'fifth', 215, {}),
-        (K_v,         'v', 'fifth', 265, {}),
-        (K_b,         'b', 'fifth', 315, {}),
-        (K_n,         'n', 'fifth', 365, {}),
-        (K_COMMA,     ',', 'fifth', 415, {}),
-        (K_SEMICOLON, ';', 'fifth', 465, {}),
-        (K_COLON,     ':', 'fifth', 515, {}),
-        (K_EXCLAIM,   '!', 'fifth', 565, {}),
-
-        (K_RSHIFT, 'RShift', 'fifth', 615, { 'width': 133, 'disabled': True }),
-
-        (K_LCTRL,    'LCtrl',   'sixth',   0, { 'width': 63, 'disabled': True }),
-        (K_LSUPER,   'LSuper',  'sixth', 115, { 'disabled': True }),
-        (K_LALT,     'LAlt',    'sixth', 165, { 'disabled': True }),
-        (K_SPACE,    'Espace',  'sixth', 215, { 'width': 248 }),
-        (K_MODE,     'AltGr',   'sixth', 465, { 'disabled': True }),
-        (314,        'Compose', 'sixth', 515, { 'disabled': True }),
-        (K_RCTRL,    'RCtrl',   'sixth', 565, { 'width': 63, 'disabled': True }),
-
-
-        (K_INSERT,   'ins',  'second', 755, {}),
-        (K_HOME,     'home', 'second', 805, {}),
-        (K_PAGEUP,   'pg_u', 'second', 855, {}),
-        (K_DELETE,   'del',  'third',  755, {}),
-        (K_END,      'end',  'third',  805, {}),
-        (K_PAGEDOWN, 'pg_d', 'third',  855, {}),
-
-
-        (K_UP,       'up',    'fifth',  805, {}),
-        (K_DOWN,     'down',  'sixth',  805, {}),
-        (K_LEFT,     'left',  'sixth',  755, {}),
-        (K_RIGHT,    'right', 'sixth',  855, {}),
-    ]
-
-    def __init__(self, screen):
-        self.screen = screen
-        self.background = Surface(self.SIZE).convert()
-        self.background.fill((250, 250, 250))
-        self.keys = {}
-        self.running = []
-        for key in self.KEYS:
-            self.keys[key[0]] = Key(self, *key[0:4], **key[4])
-
-    def draw(self):
-        for key_name in self.keys:
-            key = self.keys[key_name]
-            should_redraw_key = key.draw(self.background)
-
-            if should_redraw_key:
-                threading.Thread(target = key.poll_redraw, args = [self.background]).start()
-        self.blit()
-
-    def blit(self):
-        draw_lock.acquire()
-        self.screen.blit(self.background, (5, 5))
-        display.flip()
-        draw_lock.release()
-
-    def find_by_key_num(self, key_num):
-        if key_num in self.keys:
-            return self.keys[key_num]
-        return None
-
-    def find_by_collidepoint(self, position):
-        for key in self.keys:
-            if self.keys[key].collidepoint(position):
-                return self.keys[key]
-        return None
-
-    def find_by_unicode(self, key_sym):
-        for key in self.keys:
-            if self.keys[key].key_sym == key_sym:
-                return self.keys[key]
-        return None
-
-    def stop_all_running(self):
-        self.running = []
-
-    def start_running(self, key, start_time):
-        self.running.append((key, start_time))
-
-    def keep_running(self, key, start_time):
-        return (key, start_time) in self.running
-
-    def finished_running(self, key, start_time):
-        if (key, start_time) in self.running:
-            self.running.remove((key, start_time))
-
-
-class MusicFile:
-    def __init__(self, filename, lock):
-        self.filename = filename
-        self.channel = None
-        self.raw_data = None
-        self.sound = None
-
-        self.loaded = False
-        threading.Thread(target = self.load_sound, args = [lock]).start()
-
-    def load_sound(self, lock):
-        lock.acquire()
-        print("Loading {}".format(self.filename))
-        self.raw_data = pydub.AudioSegment.from_file(self.filename).raw_data
-        self.sound = mixer.Sound(self.raw_data)
-        print("Loaded {}".format(self.filename))
-        self.loaded = True
-        lock.release()
-
-    def play(self):
-        self.channel = self.sound.play()
-
-    def pause(self):
-        if self.channel is not None:
-            self.channel.pause()
-
-    def stop(self):
-        self.channel = None
-        self.sound.stop()
-
-
-class RoundedRect:
-    def __init__(self, rect, outer_color, inner_color, linewidth = 2, radius = 0.4):
-        self.rect        = Rect(rect)
-        self.outer_color = Color(*outer_color)
-        self.inner_color = Color(*inner_color)
-        self.linewidth   = linewidth
-        self.radius      = radius
-
-    def surface(self):
-        rectangle       = self.filledRoundedRect(self.rect, self.outer_color, self.radius)
-
-        inner_rect      = Rect((
-                self.rect.left   + 2 * self.linewidth,
-                self.rect.top    + 2 * self.linewidth,
-                self.rect.right  - 2 * self.linewidth,
-                self.rect.bottom - 2 * self.linewidth
-                ))
-
-        inner_rectangle = self.filledRoundedRect(inner_rect, self.inner_color, self.radius)
-
-        rectangle.blit(inner_rectangle, (self.linewidth, self.linewidth))
-
-        return rectangle
-
-    def filledRoundedRect(self, rect, color, radius=0.4):
-        """
-        filledRoundedRect(rect,color,radius=0.4)
-
-        rect    : rectangle
-        color   : rgb or rgba
-        radius  : 0 <= radius <= 1
-        """
-
-        alpha        = color.a
-        color.a      = 0
-        pos          = rect.topleft
-        rect.topleft = 0,0
-        rectangle    = Surface(rect.size,SRCALPHA)
-
-        circle       = Surface([min(rect.size)*3]*2,SRCALPHA)
-        draw.ellipse(circle,(0,0,0),circle.get_rect(),0)
-        circle       = transform.smoothscale(circle,[int(min(rect.size)*radius)]*2)
-
-        radius              = rectangle.blit(circle,(0,0))
-        radius.bottomright  = rect.bottomright
-        rectangle.blit(circle,radius)
-        radius.topright     = rect.topright
-        rectangle.blit(circle,radius)
-        radius.bottomleft   = rect.bottomleft
-        rectangle.blit(circle,radius)
-
-        rectangle.fill((0,0,0),rect.inflate(-radius.w,0))
-        rectangle.fill((0,0,0),rect.inflate(0,-radius.h))
-
-        rectangle.fill(color,special_flags=BLEND_RGBA_MAX)
-        rectangle.fill((255,255,255,alpha),special_flags=BLEND_RGBA_MIN)
-
-        return rectangle
-
-
 def parse_config(mapping):
     import yaml
     stream = open("config.yml", "r")
diff --git a/helpers/action.py b/helpers/action.py
new file mode 100644 (file)
index 0000000..a68de90
--- /dev/null
@@ -0,0 +1,119 @@
+import pygame
+import time
+
+class Action:
+    action_types = [
+        'command',
+        'pause',
+        'play',
+        'stop',
+        'stop_all_actions',
+        'volume',
+        'wait',
+    ]
+
+    def __init__(self, action, key, **kwargs):
+        if action in self.action_types:
+            self.action = action
+        else:
+            raise Exception("Unknown action {}".format(action))
+
+        self.key = key
+        self.arguments = kwargs
+
+    def ready(self):
+        if 'music' in self.arguments:
+            return self.arguments['music'].loaded
+        else:
+            return True
+
+    def run(self):
+        print(getattr(self, self.action + "_print")(**self.arguments))
+        return getattr(self, self.action)(**self.arguments)
+
+    def command(self, command = "", **kwargs):
+        pass
+
+    def pause(self, music = None, **kwargs):
+        if music is not None:
+            music.pause()
+        else:
+            pygame.mixer.pause()
+
+    def play(self, music = None, fade_in = 0, start_at = 0,
+            restart_if_running = False, volume = 100, **kwargs):
+        if music is not None:
+            music.play()
+        else:
+            pygame.mixer.unpause()
+
+    def stop(self, music = None, fade_out = 0, **kwargs):
+        if music is not None:
+            music.stop()
+        else:
+            pygame.mixer.stop()
+
+    def stop_all_actions(self, **kwargs):
+        self.key.mapping.stop_all_running()
+
+    def volume(self, music = None, value = 100, **kwargs):
+        pass
+
+    def wait(self, duration = 0, **kwargs):
+        time.sleep(duration)
+
+    def command_print(self, command = "", **kwargs):
+        return "running command {}".format(command)
+
+    def pause_print(self, music = None, **kwargs):
+        if music is not None:
+            return "pausing {}".format(music.filename)
+        else:
+            return "pausing all musics"
+
+    def play_print(self, music = None, fade_in = 0, start_at = 0,
+            restart_if_running = False, volume = 100, **kwargs):
+        message = "starting "
+        if music is not None:
+            message += music.filename
+        else:
+            message += "music"
+
+        if start_at != 0:
+            message += " at {}s".format(start_at)
+
+        if fade_in != 0:
+            message += " with {}s fade_in".format(fade_in)
+
+        message += " at volume {}%".format(volume)
+
+        if restart_if_running:
+            message += " (restarting if already running)"
+
+        return message
+
+    def stop_print(self, music = None, fade_out = 0, **kwargs):
+        if music is not None:
+            if fade_out == 0:
+                return "stopping music {}".format(music.filename)
+            else:
+                return "stopping music {} with {}s fadeout".format(music.filename, fade_out)
+        else:
+            if fade_out == 0:
+                return "stopping all musics"
+            else:
+                return "stopping all musics with {}s fadeout".format(fade_out)
+
+    def stop_all_actions_print(self):
+        return "stopping all actions"
+
+    def volume_print(self, music = None, value = 100, *kwargs):
+        if music is not None:
+            return "setting volume of {} to {}%".format(music.filename, value)
+        else:
+            return "setting volume to {}%".format(value)
+
+    def wait_print(self, duration, **kwargs):
+        return "waiting {}s".format(duration)
+
+
diff --git a/helpers/key.py b/helpers/key.py
new file mode 100644 (file)
index 0000000..2e4a313
--- /dev/null
@@ -0,0 +1,120 @@
+from .rounded_rect import *
+from .action import *
+import time
+import sys
+import pygame
+
+class Key:
+    row_positions = {
+        'first':    0,
+        'second':  50,
+        'third':  100,
+        'fourth': 150,
+        'fifth':  200,
+        'sixth':  250,
+    }
+
+    default_outer_color = (120, 120, 120)
+    lighter_outer_color = (200, 200, 200)
+    default_inner_color = (255, 255, 255)
+    mapped_inner_color  = (  0, 255,   0)
+    mapped_unready_inner_color = (255, 165, 0)
+
+    def __init__(self, mapping, draw_lock, key_name, key_sym, top, left, width = 48, height = 48, disabled = False):
+        self.draw_lock = draw_lock
+        self.mapping = mapping
+        self.key_name = key_name
+        self.key_sym  = key_sym
+
+        if isinstance(top, str):
+            self.top = self.row_positions[top]
+        else:
+            self.top = top
+
+        self.left   = left
+        self.width  = width
+        self.height = height
+
+        self.bottom = self.top  + self.height
+        self.right  = self.left + self.width
+
+        self.rect     = (self.left, self.top, self.right, self.bottom)
+        self.position = (self.left, self.top)
+
+        if disabled:
+            self.outer_color = self.lighter_outer_color
+            self.linewidth = 1
+        else:
+            self.outer_color = self.default_outer_color
+            self.linewidth = 3
+
+        self.inner_color = self.default_inner_color
+        self.actions = []
+
+    def square(self, all_actions_ready):
+        if self.has_actions():
+            if all_actions_ready:
+                self.inner_color = self.mapped_inner_color
+            else:
+                self.inner_color = self.mapped_unready_inner_color
+
+        return RoundedRect((0, 0, self.width, self.height),
+            self.outer_color, self.inner_color, self.linewidth)
+
+    def collidepoint(self, position):
+        return self.surface.get_rect().collidepoint(
+                position[0] - self.position[0],
+                position[1] - self.position[1]
+                )
+
+    def draw(self, background_surface):
+        self.draw_lock.acquire()
+        all_actions_ready = self.all_actions_ready()
+
+        self.surface = self.square(all_actions_ready).surface()
+
+        if getattr(sys, 'frozen', False):
+            police = pygame.font.Font(sys._MEIPASS + "/Ubuntu-Regular.ttf", 14)
+        else:
+            police = pygame.font.Font("Ubuntu-Regular.ttf", 14)
+
+        text = police.render(self.key_sym, True, (0,0,0))
+        self.surface.blit(text, (5,5))
+        background_surface.blit(self.surface, self.position)
+        self.draw_lock.release()
+
+        return not all_actions_ready
+
+    def poll_redraw(self, background):
+        while True:
+            time.sleep(1)
+            if self.all_actions_ready():
+                self.draw(background)
+                self.mapping.blit()
+                break
+
+    def has_actions(self):
+        return len(self.actions) > 0
+
+    def all_actions_ready(self):
+        return all(action.ready() for action in self.actions)
+
+    def add_action(self, action_name, **arguments):
+        self.actions.append(Action(action_name, self, **arguments))
+
+    def do_actions(self):
+        print("running actions for {}".format(self.key_sym))
+        start_time = time.time()
+        self.mapping.start_running(self, start_time)
+        for action in self.actions:
+            if self.mapping.keep_running(self, start_time):
+                action.run()
+
+        self.mapping.finished_running(self, start_time)
+
+    def list_actions(self, surface):
+        # FIXME: Todo
+        print("bouh", self.key_sym)
+        surface.fill((255, 0, 0))
+
+
diff --git a/helpers/mapping.py b/helpers/mapping.py
new file mode 100644 (file)
index 0000000..ee5623a
--- /dev/null
@@ -0,0 +1,171 @@
+import threading
+import pygame
+from .key import *
+
+class Mapping:
+    WIDTH  = 903
+    HEIGHT = 298
+    SIZE   = WIDTH, HEIGHT
+
+    KEYS = [
+        (pygame.K_ESCAPE, 'ESC', 'first',   0, {}),
+
+        (pygame.K_F1,     'F1',  'first', 100, {}),
+        (pygame.K_F2,     'F2',  'first', 150, {}),
+        (pygame.K_F3,     'F3',  'first', 200, {}),
+        (pygame.K_F4,     'F4',  'first', 250, {}),
+
+        (pygame.K_F5,     'F5',  'first', 325, {}),
+        (pygame.K_F6,     'F6',  'first', 375, {}),
+        (pygame.K_F7,     'F7',  'first', 425, {}),
+        (pygame.K_F8,     'F8',  'first', 475, {}),
+
+        (pygame.K_F9,     'F9',  'first', 550, {}),
+        (pygame.K_F10,    'F10', 'first', 600, {}),
+        (pygame.K_F11,    'F11', 'first', 650, {}),
+        (pygame.K_F12,    'F12', 'first', 700, {}),
+
+
+        (178,          '²', 'second',   0, {}),
+        (pygame.K_AMPERSAND,  '&', 'second',  50, {}),
+        (233,          'é', 'second', 100, {}),
+        (pygame.K_QUOTEDBL,   '"', 'second', 150, {}),
+        (pygame.K_QUOTE,      "'", 'second', 200, {}),
+        (pygame.K_LEFTPAREN,  '(', 'second', 250, {}),
+        (pygame.K_MINUS,      '-', 'second', 300, {}),
+        (232,          'è', 'second', 350, {}),
+        (pygame.K_UNDERSCORE, '_', 'second', 400, {}),
+        (231,          'ç', 'second', 450, {}),
+        (224,          'à', 'second', 500, {}),
+        (pygame.K_RIGHTPAREN, ')', 'second', 550, {}),
+        (pygame.K_EQUALS,     '=', 'second', 600, {}),
+
+        (pygame.K_BACKSPACE,  '<-', 'second', 650, { 'width': 98 }),
+
+
+        (pygame.K_TAB,        'tab', 'third',   0, { 'width' : 73 }),
+        (pygame.K_a,          'a',   'third',  75, {}),
+        (pygame.K_z,          'z',   'third', 125, {}),
+        (pygame.K_e,          'e',   'third', 175, {}),
+        (pygame.K_r,          'r',   'third', 225, {}),
+        (pygame.K_t,          't',   'third', 275, {}),
+        (pygame.K_y,          'y',   'third', 325, {}),
+        (pygame.K_u,          'u',   'third', 375, {}),
+        (pygame.K_i,          'i',   'third', 425, {}),
+        (pygame.K_o,          'o',   'third', 475, {}),
+        (pygame.K_p,          'p',   'third', 525, {}),
+        (pygame.K_CARET,      '^',   'third', 575, {}),
+        (pygame.K_DOLLAR,     '$',   'third', 625, {}),
+
+        (pygame.K_RETURN, 'Enter', 'third', 692, { 'width': 56, 'height': 98 }),
+
+        (pygame.K_CAPSLOCK,    'CAPS', 'fourth',  0, { 'width': 88, 'disabled': True }),
+
+        (pygame.K_q,        'q', 'fourth',  90, {}),
+        (pygame.K_s,        's', 'fourth', 140, {}),
+        (pygame.K_d,        'd', 'fourth', 190, {}),
+        (pygame.K_f,        'f', 'fourth', 240, {}),
+        (pygame.K_g,        'g', 'fourth', 290, {}),
+        (pygame.K_h,        'h', 'fourth', 340, {}),
+        (pygame.K_j,        'j', 'fourth', 390, {}),
+        (pygame.K_k,        'k', 'fourth', 440, {}),
+        (pygame.K_l,        'l', 'fourth', 490, {}),
+        (pygame.K_m,        'm', 'fourth', 540, {}),
+        (249,        'ù', 'fourth', 590, {}),
+        (pygame.K_ASTERISK, '*', 'fourth', 640, {}),
+
+
+        (pygame.K_LSHIFT, 'LShift', 'fifth', 0, { 'width': 63, 'disabled': True }),
+
+        (pygame.K_LESS,      '<', 'fifth',  65, {}),
+        (pygame.K_w,         'w', 'fifth', 115, {}),
+        (pygame.K_x,         'x', 'fifth', 165, {}),
+        (pygame.K_c,         'c', 'fifth', 215, {}),
+        (pygame.K_v,         'v', 'fifth', 265, {}),
+        (pygame.K_b,         'b', 'fifth', 315, {}),
+        (pygame.K_n,         'n', 'fifth', 365, {}),
+        (pygame.K_COMMA,     ',', 'fifth', 415, {}),
+        (pygame.K_SEMICOLON, ';', 'fifth', 465, {}),
+        (pygame.K_COLON,     ':', 'fifth', 515, {}),
+        (pygame.K_EXCLAIM,   '!', 'fifth', 565, {}),
+
+        (pygame.K_RSHIFT, 'RShift', 'fifth', 615, { 'width': 133, 'disabled': True }),
+
+        (pygame.K_LCTRL,    'LCtrl',   'sixth',   0, { 'width': 63, 'disabled': True }),
+        (pygame.K_LSUPER,   'LSuper',  'sixth', 115, { 'disabled': True }),
+        (pygame.K_LALT,     'LAlt',    'sixth', 165, { 'disabled': True }),
+        (pygame.K_SPACE,    'Espace',  'sixth', 215, { 'width': 248 }),
+        (pygame.K_MODE,     'AltGr',   'sixth', 465, { 'disabled': True }),
+        (314,        'Compose', 'sixth', 515, { 'disabled': True }),
+        (pygame.K_RCTRL,    'RCtrl',   'sixth', 565, { 'width': 63, 'disabled': True }),
+
+
+        (pygame.K_INSERT,   'ins',  'second', 755, {}),
+        (pygame.K_HOME,     'home', 'second', 805, {}),
+        (pygame.K_PAGEUP,   'pg_u', 'second', 855, {}),
+        (pygame.K_DELETE,   'del',  'third',  755, {}),
+        (pygame.K_END,      'end',  'third',  805, {}),
+        (pygame.K_PAGEDOWN, 'pg_d', 'third',  855, {}),
+
+
+        (pygame.K_UP,       'up',    'fifth',  805, {}),
+        (pygame.K_DOWN,     'down',  'sixth',  805, {}),
+        (pygame.K_LEFT,     'left',  'sixth',  755, {}),
+        (pygame.K_RIGHT,    'right', 'sixth',  855, {}),
+    ]
+
+    def __init__(self, screen, draw_lock):
+        self.draw_lock = draw_lock
+        self.screen = screen
+        self.background = pygame.Surface(self.SIZE).convert()
+        self.background.fill((250, 250, 250))
+        self.keys = {}
+        self.running = []
+        for key in self.KEYS:
+            self.keys[key[0]] = Key(self, self.draw_lock, *key[0:4], **key[4])
+
+    def draw(self):
+        for key_name in self.keys:
+            key = self.keys[key_name]
+            should_redraw_key = key.draw(self.background)
+
+            if should_redraw_key:
+                threading.Thread(target = key.poll_redraw, args = [self.background]).start()
+        self.blit()
+
+    def blit(self):
+        self.draw_lock.acquire()
+        self.screen.blit(self.background, (5, 5))
+        pygame.display.flip()
+        self.draw_lock.release()
+
+    def find_by_key_num(self, key_num):
+        if key_num in self.keys:
+            return self.keys[key_num]
+        return None
+
+    def find_by_collidepoint(self, position):
+        for key in self.keys:
+            if self.keys[key].collidepoint(position):
+                return self.keys[key]
+        return None
+
+    def find_by_unicode(self, key_sym):
+        for key in self.keys:
+            if self.keys[key].key_sym == key_sym:
+                return self.keys[key]
+        return None
+
+    def stop_all_running(self):
+        self.running = []
+
+    def start_running(self, key, start_time):
+        self.running.append((key, start_time))
+
+    def keep_running(self, key, start_time):
+        return (key, start_time) in self.running
+
+    def finished_running(self, key, start_time):
+        if (key, start_time) in self.running:
+            self.running.remove((key, start_time))
+
diff --git a/helpers/music_file.py b/helpers/music_file.py
new file mode 100644 (file)
index 0000000..39b0566
--- /dev/null
@@ -0,0 +1,35 @@
+import threading
+import pydub
+import pygame
+
+class MusicFile:
+    def __init__(self, filename, lock):
+        self.filename = filename
+        self.channel = None
+        self.raw_data = None
+        self.sound = None
+
+        self.loaded = False
+        threading.Thread(target = self.load_sound, args = [lock]).start()
+
+    def load_sound(self, lock):
+        lock.acquire()
+        print("Loading {}".format(self.filename))
+        self.raw_data = pydub.AudioSegment.from_file(self.filename).raw_data
+        self.sound = pygame.mixer.Sound(self.raw_data)
+        print("Loaded {}".format(self.filename))
+        self.loaded = True
+        lock.release()
+
+    def play(self):
+        self.channel = self.sound.play()
+
+    def pause(self):
+        if self.channel is not None:
+            self.channel.pause()
+
+    def stop(self):
+        self.channel = None
+        self.sound.stop()
+
+
diff --git a/helpers/rounded_rect.py b/helpers/rounded_rect.py
new file mode 100644 (file)
index 0000000..f168e0e
--- /dev/null
@@ -0,0 +1,62 @@
+import pygame
+
+class RoundedRect:
+    def __init__(self, rect, outer_color, inner_color, linewidth = 2, radius = 0.4):
+        self.rect        = pygame.Rect(rect)
+        self.outer_color = pygame.Color(*outer_color)
+        self.inner_color = pygame.Color(*inner_color)
+        self.linewidth   = linewidth
+        self.radius      = radius
+
+    def surface(self):
+        rectangle       = self.filledRoundedRect(self.rect, self.outer_color, self.radius)
+
+        inner_rect      = pygame.Rect((
+                self.rect.left   + 2 * self.linewidth,
+                self.rect.top    + 2 * self.linewidth,
+                self.rect.right  - 2 * self.linewidth,
+                self.rect.bottom - 2 * self.linewidth
+                ))
+
+        inner_rectangle = self.filledRoundedRect(inner_rect, self.inner_color, self.radius)
+
+        rectangle.blit(inner_rectangle, (self.linewidth, self.linewidth))
+
+        return rectangle
+
+    def filledRoundedRect(self, rect, color, radius=0.4):
+        """
+        filledRoundedRect(rect,color,radius=0.4)
+
+        rect    : rectangle
+        color   : rgb or rgba
+        radius  : 0 <= radius <= 1
+        """
+
+        alpha        = color.a
+        color.a      = 0
+        pos          = rect.topleft
+        rect.topleft = 0,0
+        rectangle    = pygame.Surface(rect.size,pygame.SRCALPHA)
+
+        circle       = pygame.Surface([min(rect.size)*3]*2,pygame.SRCALPHA)
+        pygame.draw.ellipse(circle,(0,0,0),circle.get_rect(),0)
+        circle       = pygame.transform.smoothscale(circle,[int(min(rect.size)*radius)]*2)
+
+        radius              = rectangle.blit(circle,(0,0))
+        radius.bottomright  = rect.bottomright
+        rectangle.blit(circle,radius)
+        radius.topright     = rect.topright
+        rectangle.blit(circle,radius)
+        radius.bottomleft   = rect.bottomleft
+        rectangle.blit(circle,radius)
+
+        rectangle.fill((0,0,0),rect.inflate(-radius.w,0))
+        rectangle.fill((0,0,0),rect.inflate(0,-radius.h))
+
+        rectangle.fill(color,special_flags=pygame.BLEND_RGBA_MAX)
+        rectangle.fill((255,255,255,alpha),special_flags=pygame.BLEND_RGBA_MIN)
+
+        return rectangle
+
+
index 6cc49dff4884a7f0391f796ddd097321afe146b0..9f14d561fe127c419f4b0955f5f05ad78ee1b1ca 100644 (file)
@@ -10,7 +10,7 @@ size = width, height = 1024, 600
 
 helpers.draw_lock.acquire()
 screen = pygame.display.set_mode(size)
-mapping = helpers.Mapping(screen)
+mapping = helpers.Mapping(screen, helpers.draw_lock)
 
 action_surface = pygame.Surface((600, 250)).convert()
 action_surface.fill((0,0,0))
@@ -38,6 +38,11 @@ while 1:
             event.type == pygame.KEYDOWN and 
             event.mod  == 4160 and
             event.key  == pygame.K_c):
+        for thread in threading.enumerate():
+            if thread is threading.current_thread():
+                continue
+            thread.join()
+
         pygame.quit()
         sys.exit()