# -*- 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")
--- /dev/null
+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)
+
+
--- /dev/null
+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))
+
+
--- /dev/null
+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))
+
--- /dev/null
+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()
+
+
--- /dev/null
+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
+
+
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))
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()