# -*- coding: utf-8 -*-
-from .music_file import *
-from .mapping import *
-from .lock import *
-from .font import *
-import yaml
-
-def parse_config2():
- stream = open("config.yml", "r")
- config = yaml.load(stream)
- stream.close()
-
- aliases = config['aliases']
- seen_files = {}
-
- file_lock = Lock("file")
-
- channel_id = 0
-
- key_properties = {}
-
- for key in config['key_properties']:
- if key not in key_properties:
- key_properties[key] = {
- "actions": [],
- "properties": config['key_properties'][key],
- "files": []
- }
-
- for mapped_key in config['keys']:
- if mapped_key not in key_properties:
- key_properties[mapped_key] = {
- "actions": [],
- "properties": {},
- "files": []
- }
- for action in config['keys'][mapped_key]:
- action_name = list(action)[0]
- action_args = {}
- if action[action_name] is None:
- action[action_name] = []
-
- if 'include' in action[action_name]:
- included = action[action_name]['include']
- del(action[action_name]['include'])
-
- if isinstance(included, str):
- action[action_name].update(aliases[included], **action[action_name])
- else:
- for included_ in included:
- action[action_name].update(aliases[included_], **action[action_name])
-
- for argument in action[action_name]:
- if argument == 'file':
- filename = action[action_name]['file']
- if filename not in seen_files:
- if filename in config['music_properties']:
- seen_files[filename] = MusicFile(
- filename,
- file_lock,
- channel_id,
- **config['music_properties'][filename])
- else:
- seen_files[filename] = MusicFile(
- filename,
- file_lock,
- channel_id)
- channel_id = channel_id + 1
-
- if filename not in key_properties[mapped_key]['files']:
- key_properties[mapped_key]['files'].append(seen_files[filename])
-
- action_args['music'] = seen_files[filename]
-
- else:
- action_args[argument] = action[action_name][argument]
-
- key_properties[mapped_key]['actions'].append([action_name, action_args])
-
- return (key_properties, channel_id + 1, seen_files)
-
-def parse_config(mapping):
- stream = open("config.yml", "r")
- config = yaml.load(stream)
- stream.close()
-
- aliases = config['aliases']
- seen_files = {}
-
- file_lock = Lock("file")
-
- channel_id = 0
-
- for mapped_key in config['keys']:
- key = mapping.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] = []
-
- if 'include' in action[action_name]:
- included = action[action_name]['include']
- del(action[action_name]['include'])
-
- if isinstance(included, str):
- action[action_name].update(aliases[included], **action[action_name])
- else:
- for included_ in included:
- action[action_name].update(aliases[included_], **action[action_name])
-
- for argument in action[action_name]:
- if argument == 'file':
- filename = action[action_name]['file']
- if filename not in seen_files:
- if filename in config['music_properties']:
- seen_files[filename] = MusicFile(
- filename,
- file_lock,
- channel_id,
- **config['music_properties'][filename])
- else:
- seen_files[filename] = MusicFile(
- filename,
- file_lock,
- channel_id)
- channel_id = channel_id + 1
-
- action_args['music'] = seen_files[filename]
-
- else:
- action_args[argument] = action[action_name][argument]
-
- key.add_action(action_name, **action_args)
-
- for key_property in config['key_properties']:
- key = mapping.find_by_unicode(key_property)
- if key is None:
- continue
-
- if 'description' in config['key_properties'][key_property]:
- key.set_description(config['key_properties'][key_property]['description'])
- if 'color' in config['key_properties'][key_property]:
- key.set_color(config['key_properties'][key_property]['color'])
-
- # Return the number of channels reserved
- return (channel_id + 1, seen_files)
+++ /dev/null
-import os
-import pygame
-import sys
-
-def font(size, font = "Ubuntu-Regular"):
- if getattr(sys, 'frozen', False):
- return pygame.font.Font(sys._MEIPASS + "/fonts/" + font + ".ttf", size)
- else:
- path = os.path.dirname(os.path.realpath(__file__))
- return pygame.font.Font(path + "/../fonts/" + font + ".ttf", size)
-from .rounded_rect import *
+from kivy.uix.widget import Widget
+from kivy.properties import AliasProperty, BooleanProperty, ListProperty, StringProperty
+from kivy.clock import Clock
+from kivy.uix.behaviors import ButtonBehavior
+
from .action import *
-from .font import font
import time
-import sys
-import pygame
-
-class Key:
- 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 = ( 0, 255, 0, 100)
-
- 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
-
- 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)
- self.disabled = disabled
-
- if disabled:
- self.outer_color = self.lighter_outer_color
- self.linewidth = 1
+
+class Key(ButtonBehavior, Widget):
+ key_sym = StringProperty(None)
+ custom_color = ListProperty([0, 1, 0, 1])
+ custom_unready_color = ListProperty([0, 1, 0, 100/255])
+ description_title = StringProperty("")
+ description = ListProperty([])
+ is_key_ready = BooleanProperty(True)
+
+ def get_color(self):
+ if not self.has_actions:
+ return [1, 1, 1, 1]
+ elif self.all_actions_ready:
+ return self.custom_color
else:
- self.outer_color = self.default_outer_color
- self.linewidth = 3
+ return self.custom_unready_color
+ def set_color(self):
+ pass
+
+ color = AliasProperty(get_color, set_color, bind=['is_key_ready'])
- self.inner_color = self.default_inner_color
+ def __init__(self, **kwargs):
+ super(Key, self).__init__(**kwargs)
self.actions = []
- self.description = []
- self.custom_color = self.mapped_inner_color
- self.custom_unready_color = self.mapped_unready_inner_color
-
- def square(self, all_actions_ready):
- if self.has_actions():
- if all_actions_ready:
- self.inner_color = self.custom_color
- else:
- self.inner_color = self.custom_unready_color
- return RoundedRect((0, 0, self.width, self.height),
- self.outer_color, self.inner_color, self.linewidth)
+ def on_key_sym(self, key, key_sym):
+ if key_sym in self.parent.key_config:
+ self.is_key_ready = False
+
+ self.config = self.parent.key_config[key_sym]
- def collidepoint(self, position):
- return self.surface.get_rect().collidepoint(
- position[0] - self.position[0],
- position[1] - self.position[1]
- )
+ self.actions = []
+ for key_action in self.config['actions']:
+ self.add_action(key_action[0], **key_action[1])
+
+ if 'description' in self.config['properties']:
+ key.set_description(self.config['properties']['description'])
+ if 'color' in self.config['properties']:
+ key.set_color(self.config['properties']['color'])
+
+ Clock.schedule_interval(self.check_all_active, 1)
+
+ def check_all_active(self, dt):
+ if self.all_actions_ready:
+ self.is_key_ready = True
+ return False
def set_description(self, description):
- for desc in description:
+ if description[0] is not None:
+ self.description_title = str(description[0])
+ for desc in description[1:]:
if desc is None:
self.description.append("")
else:
- self.description.append(str(desc))
+ self.description.append(str(desc).replace(" ", " "))
def set_color(self, color):
- self.custom_color = tuple(color)
- color.append(100)
+ color = [x / 255 for x in color]
+ color.append(1)
+ self.custom_color = color
+ color[3] = 100 / 255
self.custom_unready_color = tuple(color)
- def draw(self, background_surface):
- self.draw_lock.acquire()
- all_actions_ready = self.all_actions_ready()
-
- self.surface = self.square(all_actions_ready).surface()
-
- police = font(14)
- text_police = font(10)
-
- police.set_bold(True)
- text = police.render(self.key_sym, True, (0,0,0))
- self.surface.blit(text, (5,5))
-
- is_first_line = True
- offset = 11 + text_police.get_linesize() - 4
- first_line_offset = 18
- for description in self.description:
- text = text_police.render(description, True, (0,0,0))
- if is_first_line:
- self.surface.blit(text, (first_line_offset, 9))
- is_first_line = False
- else:
- self.surface.blit(text, (3, offset))
- offset += text_police.get_linesize() - 4
-
- 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
-
+ @property
def has_actions(self):
return len(self.actions) > 0
+ @property
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, screen):
+ def do_actions(self):
print("running actions for {}".format(self.key_sym))
start_time = time.time()
- self.mapping.start_running(self, start_time)
+ self.parent.start_running(self, start_time)
action_number = 0
for action in self.actions:
- if self.mapping.keep_running(self, start_time):
- self.list_actions(screen, action_number = action_number + 0.5)
+ if self.parent.keep_running(self, start_time):
+ self.list_actions(action_number = action_number + 0.5)
action.run()
action_number += 1
- self.list_actions(screen, action_number = action_number)
-
- self.mapping.finished_running(self, start_time)
-
- def list_actions(self, screen, action_number = 0):
- action_descriptions = [action.description() for action in self.actions]
- #print("actions linked to key {}:".format(self.key_sym))
- #print("\t" + "\n\t".join(action_descriptions))
- self.draw_lock.acquire()
- surface = pygame.Surface((690, 250)).convert()
- surface.fill((250, 250, 250))
- police = font(14)
-
- offset = 0
- police.set_bold(True)
- text = police.render("actions linked to key {}:".format(self.key_sym), True, (0,0,0))
- surface.blit(text, (0, offset))
- offset += police.get_linesize()
-
- police.set_bold(False)
- icon_police = font(14, font = "Symbola")
- for index, description in enumerate(action_descriptions):
- if index < int(action_number):
- icon = icon_police.render("✓", True, (0,0,0))
- elif index + 0.5 == action_number:
- icon = icon_police.render("✅", True, (0,0,0))
- else:
- icon = icon_police.render(" ", True, (0,0,0))
-
- text = police.render(description, True, (0,0,0))
- surface.blit(icon, (0, offset))
- surface.blit(text, (10, offset))
- offset += police.get_linesize()
+ self.list_actions(action_number = action_number)
- screen.blit(surface, (5, 308))
- pygame.display.flip()
- self.draw_lock.release()
+ self.parent.finished_running(self, start_time)
+ def list_actions(self, action_number = 0):
+ self.parent.parent.ids['ActionList'].update_list(self, action_number)
+ def on_press(self):
+ self.list_actions()
+ pass
+from kivy.uix.relativelayout import RelativeLayout
+from kivy.properties import NumericProperty
+from kivy.core.window import Window
+
import threading
import pygame
-from .key import *
-
-class Mapping:
- WIDTH = 903
- HEIGHT = 298
- SIZE = WIDTH, HEIGHT
-
- ROW_POSITIONS = {
- 'first': 0,
- 'second': 50,
- 'third': 100,
- 'fourth': 150,
- 'fifth': 200,
- 'sixth': 250,
- }
-
- 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 = {}
+import yaml
+
+from .lock import *
+from .music_file import *
+
+class Mapping(RelativeLayout):
+ expected_keys = NumericProperty(0)
+
+ def __init__(self, **kwargs):
+ self.key_config, self.channel_number, self.open_files = self.parse_config()
+ super(Mapping, self).__init__(**kwargs)
+ self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
+ self._keyboard.bind(on_key_down=self._on_keyboard_down)
self.running = []
- for key in self.KEYS:
- if key[2] in self.ROW_POSITIONS:
- position = self.ROW_POSITIONS[key[2]]
- else:
- position = key[2]
- self.keys[key[0]] = Key(self,
- self.draw_lock,
- key[0], key[1], position, key[3],
- **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(name = "MSPollRedraw", 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]
+
+ pygame.mixer.init(frequency = 44100)
+ pygame.mixer.set_num_channels(self.channel_number)
+
+ def _keyboard_closed(self):
+ self._keyboard.unbind(on_key_down=self._on_keyboard_down)
+ self._keyboard = None
+
+ def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
+ key = self.find_by_key_code(keycode)
+ if key is not None:
+ threading.Thread(name = "MSKeyAction", target=key.do_actions).start()
+ return True
+
+ def find_by_key_code(self, key_code):
+ if "Key_" + str(key_code[0]) in self.ids:
+ return self.ids["Key_" + str(key_code[0])]
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]
+ for key in self.children:
+ if not type(key).__name__ == "Key":
+ continue
+ print(key.key_sym, key_sym)
+ if key.key_sym == key_sym:
+ print("found")
+ return key
return None
def stop_all_running(self):
if (key, start_time) in self.running:
self.running.remove((key, start_time))
+ def parse_config(self):
+ stream = open("config.yml", "r")
+ config = yaml.load(stream)
+ stream.close()
+
+ aliases = config['aliases']
+ seen_files = {}
+
+ file_lock = Lock("file")
+
+ channel_id = 0
+
+ key_properties = {}
+
+ for key in config['key_properties']:
+ if key not in key_properties:
+ key_properties[key] = {
+ "actions": [],
+ "properties": config['key_properties'][key],
+ "files": []
+ }
+
+ for mapped_key in config['keys']:
+ if mapped_key not in key_properties:
+ key_properties[mapped_key] = {
+ "actions": [],
+ "properties": {},
+ "files": []
+ }
+ for action in config['keys'][mapped_key]:
+ action_name = list(action)[0]
+ action_args = {}
+ if action[action_name] is None:
+ action[action_name] = []
+
+ if 'include' in action[action_name]:
+ included = action[action_name]['include']
+ del(action[action_name]['include'])
+
+ if isinstance(included, str):
+ action[action_name].update(aliases[included], **action[action_name])
+ else:
+ for included_ in included:
+ action[action_name].update(aliases[included_], **action[action_name])
+
+ for argument in action[action_name]:
+ if argument == 'file':
+ filename = action[action_name]['file']
+ if filename not in seen_files:
+ if filename in config['music_properties']:
+ seen_files[filename] = MusicFile(
+ filename,
+ file_lock,
+ channel_id,
+ **config['music_properties'][filename])
+ else:
+ seen_files[filename] = MusicFile(
+ filename,
+ file_lock,
+ channel_id)
+ channel_id = channel_id + 1
+
+ if filename not in key_properties[mapped_key]['files']:
+ key_properties[mapped_key]['files'].append(seen_files[filename])
+
+ action_args['music'] = seen_files[filename]
+
+ else:
+ action_args[argument] = action[action_name][argument]
+
+ key_properties[mapped_key]['actions'].append([action_name, action_args])
+
+ return (key_properties, channel_id + 1, seen_files)
+
+
+++ /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
-
-
+++ /dev/null
-from kivy.app import App
-from kivy.uix.widget import Widget
-from kivy.uix.floatlayout import FloatLayout
-from kivy.uix.relativelayout import RelativeLayout
-from kivy.properties import AliasProperty, ReferenceListProperty, BooleanProperty, NumericProperty, ListProperty, StringProperty, ObjectProperty
-from kivy.vector import Vector
-from kivy.clock import Clock
-from kivy.uix.behaviors import ButtonBehavior
-from kivy.uix.label import Label
-from kivy.core.window import Window
-
-from helpers.action import *
-import time
-import sys
-import math
-import pygame
-import helpers
-import threading
-
-class KeyDescription(Label):
- pass
-
-class Key(ButtonBehavior, Widget):
- key_sym = StringProperty(None)
- custom_color = ListProperty([0, 1, 0, 1])
- custom_unready_color = ListProperty([0, 1, 0, 100/255])
- description_title = StringProperty("")
- description = ListProperty([])
- is_key_ready = BooleanProperty(True)
-
- def get_color(self):
- if not self.has_actions:
- return [1, 1, 1, 1]
- elif self.all_actions_ready:
- return self.custom_color
- else:
- return self.custom_unready_color
- def set_color(self):
- pass
-
- color = AliasProperty(get_color, set_color, bind=['is_key_ready'])
-
- def __init__(self, **kwargs):
- super(Key, self).__init__(**kwargs)
- self.actions = []
-
- def on_key_sym(self, key, key_sym):
- if key_sym in self.parent.key_config:
- self.is_key_ready = False
-
- self.config = self.parent.key_config[key_sym]
-
- self.actions = []
- for key_action in self.config['actions']:
- self.add_action(key_action[0], **key_action[1])
-
- if 'description' in self.config['properties']:
- key.set_description(self.config['properties']['description'])
- if 'color' in self.config['properties']:
- key.set_color(self.config['properties']['color'])
-
- Clock.schedule_interval(self.check_all_active, 1)
-
- def check_all_active(self, dt):
- if self.all_actions_ready:
- self.is_key_ready = True
- return False
-
- def set_description(self, description):
- if description[0] is not None:
- self.description_title = str(description[0])
- for desc in description[1:]:
- if desc is None:
- self.description.append("")
- else:
- self.description.append(str(desc).replace(" ", " "))
-
- def set_color(self, color):
- color = [x / 255 for x in color]
- color.append(1)
- self.custom_color = color
- color[3] = 100 / 255
- self.custom_unready_color = tuple(color)
-
- @property
- def has_actions(self):
- return len(self.actions) > 0
-
- @property
- 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.parent.start_running(self, start_time)
- action_number = 0
- for action in self.actions:
- if self.parent.keep_running(self, start_time):
- self.list_actions(action_number = action_number + 0.5)
- action.run()
- action_number += 1
- self.list_actions(action_number = action_number)
-
- self.parent.finished_running(self, start_time)
-
- def list_actions(self, action_number = 0):
- self.parent.parent.ids['ActionList'].update_list(self, action_number)
-
- def on_press(self):
- self.list_actions()
- pass
-
-class PlayList(RelativeLayout):
- playlist = ListProperty([])
-
- def __init__(self, **kwargs):
- super(PlayList, self).__init__(**kwargs)
- Clock.schedule_interval(self.update_playlist, 0.5)
-
- def update_playlist(self, dt):
- if self.parent is None or 'Mapping' not in self.parent.ids:
- return True
-
- open_files = self.parent.ids['Mapping'].open_files
- self.playlist = []
- for music_file in open_files.values():
- if not music_file.is_playing():
- continue
- if music_file.is_paused():
- self.playlist.append(["⏸", music_file.name, False])
- else:
- self.playlist.append(["⏵", music_file.name, True])
-
-
-class ActionList(RelativeLayout):
- action_title = StringProperty("")
- action_list = ListProperty([])
-
- def update_list(self, key, action_number = 0):
- self.action_title = "actions linked to key {}:".format(key.key_sym)
- self.action_list = []
-
- action_descriptions = [action.description() for action in key.actions]
-
- for index, description in enumerate(action_descriptions):
- if index < int(action_number):
- icon = "✓"
- elif index + 0.5 == action_number:
- icon = "✅"
- else:
- icon = " "
-
- self.action_list.append([icon, description])
-
-class Mapping(RelativeLayout):
- expected_keys = NumericProperty(0)
-
- def __init__(self, **kwargs):
- self.key_config, self.channel_number, self.open_files = helpers.parse_config2()
- super(Mapping, self).__init__(**kwargs)
- self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
- self._keyboard.bind(on_key_down=self._on_keyboard_down)
- self.running = []
-
-
- pygame.mixer.init(frequency = 44100)
- pygame.mixer.set_num_channels(self.channel_number)
-
- def _keyboard_closed(self):
- self._keyboard.unbind(on_key_down=self._on_keyboard_down)
- self._keyboard = None
-
- def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
- key = self.find_by_key_code(keycode)
- if key is not None:
- threading.Thread(name = "MSKeyAction", target=key.do_actions).start()
- return True
-
- def find_by_key_code(self, key_code):
- if "Key_" + str(key_code[0]) in self.ids:
- return self.ids["Key_" + str(key_code[0])]
- return None
-
- def find_by_unicode(self, key_sym):
- for key in self.children:
- if not type(key).__name__ == "Key":
- continue
- print(key.key_sym, key_sym)
- if key.key_sym == key_sym:
- print("found")
- return 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 Screen(FloatLayout):
- pass
-
-class MusicSamplerApp(App):
- def build(self):
- Window.size = (913, 563)
-
- return Screen()
-
-if __name__ == '__main__':
- MusicSamplerApp().run()
-import sys
-import pygame
-import helpers
-import threading
-
-pygame.mixer.pre_init(frequency = 44100)
-pygame.init()
-
-size = width, height = 913, 563
-screen = pygame.display.set_mode(size)
-screen.fill((229, 228, 226))
-
-draw_lock = helpers.Lock("draw")
-
-mapping = helpers.Mapping(screen, draw_lock)
-channel_number, open_files = helpers.parse_config(mapping)
-pygame.mixer.set_num_channels(channel_number)
-
-mapping.draw()
+from kivy.app import App
+from kivy.uix.floatlayout import FloatLayout
+from kivy.uix.relativelayout import RelativeLayout
+from kivy.properties import ListProperty, StringProperty
+from kivy.clock import Clock
+from kivy.core.window import Window
+
+from helpers.key import Key
+from helpers.mapping import Mapping
+
+class PlayList(RelativeLayout):
+ playlist = ListProperty([])
+
+ def __init__(self, **kwargs):
+ super(PlayList, self).__init__(**kwargs)
+ Clock.schedule_interval(self.update_playlist, 0.5)
+
+ def update_playlist(self, dt):
+ if self.parent is None or 'Mapping' not in self.parent.ids:
+ return True
+
+ open_files = self.parent.ids['Mapping'].open_files
+ self.playlist = []
+ for music_file in open_files.values():
+ if not music_file.is_playing():
+ continue
+ if music_file.is_paused():
+ self.playlist.append(["⏸", music_file.name, False])
+ else:
+ self.playlist.append(["⏵", music_file.name, True])
-draw_lock.acquire()
-pygame.display.flip()
-draw_lock.release()
-contexts = [
- 'normal'
-]
+class ActionList(RelativeLayout):
+ action_title = StringProperty("")
+ action_list = ListProperty([])
-context = 'normal'
+ def update_list(self, key, action_number = 0):
+ self.action_title = "actions linked to key {}:".format(key.key_sym)
+ self.action_list = []
-#### Normal workflow ####
-while 1:
- event = pygame.event.wait()
+ action_descriptions = [action.description() for action in key.actions]
- if event.type == pygame.QUIT or (
- event.type == pygame.KEYDOWN and
- event.mod == 4160 and
- event.key == pygame.K_c):
- for thread in threading.enumerate():
- if thread.getName()[0:2] != "MS":
- continue
- thread.join()
+ for index, description in enumerate(action_descriptions):
+ if index < int(action_number):
+ icon = "✓"
+ elif index + 0.5 == action_number:
+ icon = "✅"
+ else:
+ icon = " "
- pygame.quit()
- sys.exit()
+ self.action_list.append([icon, description])
- if context == 'normal':
- if event.type == pygame.KEYDOWN:
- key = mapping.find_by_key_num(event.key)
- if key is not None and not key.disabled:
- threading.Thread(name = "MSKeyAction", target=key.do_actions, args = [screen]).start()
- threading.Thread(name = "MSClic", target=key.list_actions, args = [screen]).start()
- elif event.type == pygame.MOUSEBUTTONUP:
- key = mapping.find_by_collidepoint(pygame.mouse.get_pos())
- if key is not None:
- threading.Thread(name = "MSClic", target=key.list_actions, args = [screen]).start()
+class Screen(FloatLayout):
+ pass
- draw_lock.acquire()
- police = helpers.font(14)
- icon_police = helpers.font(14, font = "Symbola")
+class MusicSamplerApp(App):
+ def build(self):
+ Window.size = (913, 563)
- surface = pygame.Surface((208, 250)).convert()
- surface.fill((250, 250, 250))
- offset = 0
- for music_file in open_files.values():
- police.set_bold(False)
- if music_file.is_playing():
- if music_file.is_paused():
- icon = icon_police.render("⏸", True, (0,0,0))
- else:
- icon = icon_police.render("⏵", True, (0,0,0))
- police.set_bold(True)
- text = police.render(music_file.name, True, (0,0,0))
- surface.blit(icon, (0, offset))
- surface.blit(text, (20, offset))
- offset += police.get_linesize()
- screen.blit(surface, (700, 308))
+ return Screen()
- pygame.display.flip()
- draw_lock.release()
+if __name__ == '__main__':
+ MusicSamplerApp().run()
size: 2 * self.parent.width - 2 * self.parent.line_width, self.parent.height - key_label.font_size
<Screen>:
+ canvas:
+ Color:
+ rgba: 229/255, 228/255, 226/255, 1
+ Rectangle:
+ pos: 0, 0
+ size: self.width, self.height
+
key_size: int( (3 * self.width - 16) / 56)
key_sep: int( self.key_size / 24)
key_pad_sep: int( self.key_size / 7) + 1