# -*- coding: utf-8 -*- from pygame import * class Action: action_types = [ 'command', 'pause', 'play', 'stop', 'volume', 'wait', ] def __init__(self, action, **kwargs): if action in self.action_types: self.action = action else: raise Exception("Unknown action {}".format(action)) self.arguments = kwargs def run(self, callback): getattr(self, self.action)(callback, **self.arguments) def command(self, callback, command = "", **kwargs): pass def pause(self, callback, music = None, **kwargs): pass def play(self, callback, music = None, fade_in = 0, restart_if_running = False, volume = 100, **kwargs): pass def stop(self, callback, music = None, fade_out = 0, **kwargs): print('stopping') return callback() def volume(self, callback, music = None, value = 100, **kwargs): pass def wait(self, callback, time = 0, **kwargs): pass class Key: row_positions = { 'first': 5, 'second': 55, 'third': 105, 'fourth': 155, 'fifth': 205, 'sixth': 255, } default_outer_color = (120, 120, 120) lighter_outer_color = (200, 200, 200) default_inner_color = (255, 255, 255) mapped_inner_color = ( 0, 255, 0) def __init__(self, key_name, key_sym, top, left, width = 48, height = 48, disabled = False): 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): if self.has_action(): self.inner_color = self.mapped_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.surface = self.square().surface() police = font.Font(None, 20) text = police.render(self.key_sym, True, (0,0,0)) self.surface.blit(text, (5,5)) background_surface.blit(self.surface, self.position) def has_action(self): return len(self.actions) > 0 def add_action(self, action_name, **arguments): self.actions.append(Action(action_name, **arguments)) def next_action(self): print("running next action") def do_actions(self): self.current_action = 0 print("running actions for {}".format(self.key_sym)) if len(self.actions) > 0: self.actions[0].run(self.next_action) def list_actions(self, surface): print("bouh", self.key_sym) surface.fill((255, 0, 0)) def find_by_key_num(key_num): if key_num in Mapping.KEYS: return Mapping.KEYS[key_num] return None def find_by_collidepoint(position): for key in Mapping.KEYS: if Mapping.KEYS[key].collidepoint(position): return Mapping.KEYS[key] return None def find_by_unicode(key_sym): for key in Mapping.KEYS: if Mapping.KEYS[key].key_sym == key_sym: return Mapping.KEYS[key] return None class Mapping: KEYS = { K_ESCAPE: Key(K_ESCAPE, 'ESC', 'first', 0), K_F1: Key(K_F1, 'F1', 'first', 100), K_F2: Key(K_F2, 'F2', 'first', 150), K_F3: Key(K_F3, 'F3', 'first', 200), K_F4: Key(K_F4, 'F4', 'first', 250), K_F5: Key(K_F5, 'F5', 'first', 325), K_F6: Key(K_F6, 'F6', 'first', 375), K_F7: Key(K_F7, 'F7', 'first', 425), K_F8: Key(K_F8, 'F8', 'first', 475), K_F9: Key(K_F9, 'F9', 'first', 550), K_F10: Key(K_F10, 'F10', 'first', 600), K_F11: Key(K_F11, 'F11', 'first', 650), K_F12: Key(K_F12, 'F12', 'first', 700), 178: Key(178, '²', 'second', 0), K_AMPERSAND: Key(K_AMPERSAND, '&', 'second', 50), 233: Key(233, 'é', 'second', 100), K_QUOTEDBL: Key(K_QUOTEDBL, '"', 'second', 150), K_QUOTE: Key(K_QUOTE, "'", 'second', 200), K_LEFTPAREN: Key(K_LEFTPAREN, '(', 'second', 250), K_MINUS: Key(K_MINUS, '-', 'second', 300), 232: Key(232, 'è', 'second', 350), K_UNDERSCORE: Key(K_UNDERSCORE, '_', 'second', 400), 231: Key(231, 'ç', 'second', 450), 224: Key(224, 'à', 'second', 500), K_RIGHTPAREN: Key(K_RIGHTPAREN, ')', 'second', 550), K_EQUALS: Key(K_EQUALS, '=', 'second', 600), K_BACKSPACE: Key(K_BACKSPACE, '<-', 'second', 650, width = 98), K_TAB: Key(K_TAB, 'tab', 'third', 0, width = 73), K_a: Key(K_a, 'a', 'third', 75), K_z: Key(K_z, 'z', 'third', 125), K_e: Key(K_e, 'e', 'third', 175), K_r: Key(K_r, 'r', 'third', 225), K_t: Key(K_t, 't', 'third', 275), K_y: Key(K_y, 'y', 'third', 325), K_u: Key(K_u, 'u', 'third', 375), K_i: Key(K_i, 'i', 'third', 425), K_o: Key(K_o, 'o', 'third', 475), K_p: Key(K_p, 'p', 'third', 525), K_CARET: Key(K_CARET, '^', 'third', 575), K_DOLLAR: Key(K_DOLLAR, '$', 'third', 625), K_RETURN: Key(K_RETURN, 'Enter', 'third', 692, width = 56, height = 98), K_CAPSLOCK: Key(K_CAPSLOCK, 'CAPS', 'fourth', 0, width = 88, disabled = True), K_q: Key(K_q, 'q', 'fourth', 90), K_s: Key(K_s, 's', 'fourth', 140), K_d: Key(K_d, 'd', 'fourth', 190), K_f: Key(K_f, 'f', 'fourth', 240), K_g: Key(K_g, 'g', 'fourth', 290), K_h: Key(K_h, 'h', 'fourth', 340), K_j: Key(K_j, 'j', 'fourth', 390), K_k: Key(K_k, 'k', 'fourth', 440), K_l: Key(K_l, 'l', 'fourth', 490), K_m: Key(K_m, 'm', 'fourth', 540), 249: Key(249, 'ù', 'fourth', 590), K_ASTERISK: Key(K_ASTERISK, '*', 'fourth', 640), K_LSHIFT: Key(K_LSHIFT, 'LShift', 'fifth', 0, width = 63, disabled = True), K_LESS: Key(K_LESS, '<', 'fifth', 65), K_w: Key(K_w, 'w', 'fifth', 115), K_x: Key(K_x, 'x', 'fifth', 165), K_c: Key(K_c, 'c', 'fifth', 215), K_v: Key(K_v, 'v', 'fifth', 265), K_b: Key(K_b, 'b', 'fifth', 315), K_n: Key(K_n, 'n', 'fifth', 365), K_COMMA: Key(K_COMMA, ',', 'fifth', 415), K_SEMICOLON: Key(K_SEMICOLON, ';', 'fifth', 465), K_COLON: Key(K_COLON, ':', 'fifth', 515), K_EXCLAIM: Key(K_EXCLAIM, '!', 'fifth', 565), K_RSHIFT: Key(K_RSHIFT, 'RShift', 'fifth', 615, width = 133, disabled = True), K_LCTRL: Key(K_LCTRL, 'LCtrl', 'sixth', 0, width = 63, disabled = True), K_LSUPER: Key(K_LSUPER, 'LSuper', 'sixth', 115, disabled = True), K_LALT: Key(K_LALT, 'LAlt', 'sixth', 165, disabled = True), K_SPACE: Key(K_SPACE, 'Espace', 'sixth', 215, width = 248), K_MODE: Key(K_MODE, 'AltGr', 'sixth', 465, disabled = True), 314: Key(314, 'Compose', 'sixth', 515, disabled = True), K_RCTRL: Key(K_RCTRL, 'RCtrl', 'sixth', 565, width = 63, disabled = True), K_INSERT: Key(K_INSERT, 'ins', 'second', 755), K_HOME: Key(K_HOME, 'home', 'second', 805), K_PAGEUP: Key(K_PAGEUP, 'pg_u', 'second', 855), K_DELETE: Key(K_DELETE, 'del', 'third', 755), K_END: Key(K_END, 'end', 'third', 805), K_PAGEDOWN: Key(K_PAGEDOWN, 'pg_d', 'third', 855), K_UP: Key(K_UP, 'up', 'fifth', 805), K_DOWN: Key(K_DOWN, 'down', 'sixth', 805), K_LEFT: Key(K_LEFT, 'left', 'sixth', 755), K_RIGHT: Key(K_RIGHT, 'right', 'sixth', 855), } class MusicFile: def __init__(self, filename): self.filename = filename 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(): import yaml stream = open("config.yml", "r") config = yaml.load(stream) stream.close() aliases = config['aliases'] seen_files = {} for mapped_key in config['keys']: key = Key.find_by_unicode(mapped_key) if key is None: continue for action in config['keys'][mapped_key]: action_name = list(action)[0] action_args = {} if action[action_name] is None: action[action_name] = [] for argument in action[action_name]: if argument == 'include': included = action[action_name]['include'] if isinstance(included, str): action_args.update(aliases[included]) else: for included_ in included: action_args.update(aliases[included_]) elif argument == 'file': filename = action[action_name]['file'] if filename not in seen_files: seen_files[filename] = MusicFile.new(filename) action_args['music'] = seen_files[filename] else: action_args[argument] = action[action_name][argument] key.add_action(action_name, **action_args)