diff options
-rw-r--r-- | helpers/__init__.py | 149 | ||||
-rw-r--r-- | helpers/font.py | 10 | ||||
-rw-r--r-- | helpers/key.py | 202 | ||||
-rw-r--r-- | helpers/mapping.py | 281 | ||||
-rw-r--r-- | helpers/rounded_rect.py | 62 | ||||
-rw-r--r-- | keyboard.py | 222 | ||||
-rw-r--r-- | music_sampler.py | 123 | ||||
-rw-r--r-- | musicsampler.kv | 7 |
8 files changed, 243 insertions, 813 deletions
diff --git a/helpers/__init__.py b/helpers/__init__.py index eb948f2..40a96af 100644 --- a/helpers/__init__.py +++ b/helpers/__init__.py | |||
@@ -1,150 +1 @@ | |||
1 | # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | |
2 | from .music_file import * | ||
3 | from .mapping import * | ||
4 | from .lock import * | ||
5 | from .font import * | ||
6 | import yaml | ||
7 | |||
8 | def parse_config2(): | ||
9 | stream = open("config.yml", "r") | ||
10 | config = yaml.load(stream) | ||
11 | stream.close() | ||
12 | |||
13 | aliases = config['aliases'] | ||
14 | seen_files = {} | ||
15 | |||
16 | file_lock = Lock("file") | ||
17 | |||
18 | channel_id = 0 | ||
19 | |||
20 | key_properties = {} | ||
21 | |||
22 | for key in config['key_properties']: | ||
23 | if key not in key_properties: | ||
24 | key_properties[key] = { | ||
25 | "actions": [], | ||
26 | "properties": config['key_properties'][key], | ||
27 | "files": [] | ||
28 | } | ||
29 | |||
30 | for mapped_key in config['keys']: | ||
31 | if mapped_key not in key_properties: | ||
32 | key_properties[mapped_key] = { | ||
33 | "actions": [], | ||
34 | "properties": {}, | ||
35 | "files": [] | ||
36 | } | ||
37 | for action in config['keys'][mapped_key]: | ||
38 | action_name = list(action)[0] | ||
39 | action_args = {} | ||
40 | if action[action_name] is None: | ||
41 | action[action_name] = [] | ||
42 | |||
43 | if 'include' in action[action_name]: | ||
44 | included = action[action_name]['include'] | ||
45 | del(action[action_name]['include']) | ||
46 | |||
47 | if isinstance(included, str): | ||
48 | action[action_name].update(aliases[included], **action[action_name]) | ||
49 | else: | ||
50 | for included_ in included: | ||
51 | action[action_name].update(aliases[included_], **action[action_name]) | ||
52 | |||
53 | for argument in action[action_name]: | ||
54 | if argument == 'file': | ||
55 | filename = action[action_name]['file'] | ||
56 | if filename not in seen_files: | ||
57 | if filename in config['music_properties']: | ||
58 | seen_files[filename] = MusicFile( | ||
59 | filename, | ||
60 | file_lock, | ||
61 | channel_id, | ||
62 | **config['music_properties'][filename]) | ||
63 | else: | ||
64 | seen_files[filename] = MusicFile( | ||
65 | filename, | ||
66 | file_lock, | ||
67 | channel_id) | ||
68 | channel_id = channel_id + 1 | ||
69 | |||
70 | if filename not in key_properties[mapped_key]['files']: | ||
71 | key_properties[mapped_key]['files'].append(seen_files[filename]) | ||
72 | |||
73 | action_args['music'] = seen_files[filename] | ||
74 | |||
75 | else: | ||
76 | action_args[argument] = action[action_name][argument] | ||
77 | |||
78 | key_properties[mapped_key]['actions'].append([action_name, action_args]) | ||
79 | |||
80 | return (key_properties, channel_id + 1, seen_files) | ||
81 | |||
82 | def parse_config(mapping): | ||
83 | stream = open("config.yml", "r") | ||
84 | config = yaml.load(stream) | ||
85 | stream.close() | ||
86 | |||
87 | aliases = config['aliases'] | ||
88 | seen_files = {} | ||
89 | |||
90 | file_lock = Lock("file") | ||
91 | |||
92 | channel_id = 0 | ||
93 | |||
94 | for mapped_key in config['keys']: | ||
95 | key = mapping.find_by_unicode(mapped_key) | ||
96 | if key is None: | ||
97 | continue | ||
98 | |||
99 | for action in config['keys'][mapped_key]: | ||
100 | action_name = list(action)[0] | ||
101 | action_args = {} | ||
102 | if action[action_name] is None: | ||
103 | action[action_name] = [] | ||
104 | |||
105 | if 'include' in action[action_name]: | ||
106 | included = action[action_name]['include'] | ||
107 | del(action[action_name]['include']) | ||
108 | |||
109 | if isinstance(included, str): | ||
110 | action[action_name].update(aliases[included], **action[action_name]) | ||
111 | else: | ||
112 | for included_ in included: | ||
113 | action[action_name].update(aliases[included_], **action[action_name]) | ||
114 | |||
115 | for argument in action[action_name]: | ||
116 | if argument == 'file': | ||
117 | filename = action[action_name]['file'] | ||
118 | if filename not in seen_files: | ||
119 | if filename in config['music_properties']: | ||
120 | seen_files[filename] = MusicFile( | ||
121 | filename, | ||
122 | file_lock, | ||
123 | channel_id, | ||
124 | **config['music_properties'][filename]) | ||
125 | else: | ||
126 | seen_files[filename] = MusicFile( | ||
127 | filename, | ||
128 | file_lock, | ||
129 | channel_id) | ||
130 | channel_id = channel_id + 1 | ||
131 | |||
132 | action_args['music'] = seen_files[filename] | ||
133 | |||
134 | else: | ||
135 | action_args[argument] = action[action_name][argument] | ||
136 | |||
137 | key.add_action(action_name, **action_args) | ||
138 | |||
139 | for key_property in config['key_properties']: | ||
140 | key = mapping.find_by_unicode(key_property) | ||
141 | if key is None: | ||
142 | continue | ||
143 | |||
144 | if 'description' in config['key_properties'][key_property]: | ||
145 | key.set_description(config['key_properties'][key_property]['description']) | ||
146 | if 'color' in config['key_properties'][key_property]: | ||
147 | key.set_color(config['key_properties'][key_property]['color']) | ||
148 | |||
149 | # Return the number of channels reserved | ||
150 | return (channel_id + 1, seen_files) | ||
diff --git a/helpers/font.py b/helpers/font.py deleted file mode 100644 index 83999ef..0000000 --- a/helpers/font.py +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | import os | ||
2 | import pygame | ||
3 | import sys | ||
4 | |||
5 | def font(size, font = "Ubuntu-Regular"): | ||
6 | if getattr(sys, 'frozen', False): | ||
7 | return pygame.font.Font(sys._MEIPASS + "/fonts/" + font + ".ttf", size) | ||
8 | else: | ||
9 | path = os.path.dirname(os.path.realpath(__file__)) | ||
10 | return pygame.font.Font(path + "/../fonts/" + font + ".ttf", size) | ||
diff --git a/helpers/key.py b/helpers/key.py index d923b1d..658c17f 100644 --- a/helpers/key.py +++ b/helpers/key.py | |||
@@ -1,169 +1,101 @@ | |||
1 | from .rounded_rect import * | 1 | from kivy.uix.widget import Widget |
2 | from kivy.properties import AliasProperty, BooleanProperty, ListProperty, StringProperty | ||
3 | from kivy.clock import Clock | ||
4 | from kivy.uix.behaviors import ButtonBehavior | ||
5 | |||
2 | from .action import * | 6 | from .action import * |
3 | from .font import font | ||
4 | import time | 7 | import time |
5 | import sys | 8 | |
6 | import pygame | 9 | class Key(ButtonBehavior, Widget): |
7 | 10 | key_sym = StringProperty(None) | |
8 | class Key: | 11 | custom_color = ListProperty([0, 1, 0, 1]) |
9 | default_outer_color = (120, 120, 120) | 12 | custom_unready_color = ListProperty([0, 1, 0, 100/255]) |
10 | lighter_outer_color = (200, 200, 200) | 13 | description_title = StringProperty("") |
11 | default_inner_color = (255, 255, 255) | 14 | description = ListProperty([]) |
12 | mapped_inner_color = ( 0, 255, 0) | 15 | is_key_ready = BooleanProperty(True) |
13 | mapped_unready_inner_color = ( 0, 255, 0, 100) | 16 | |
14 | 17 | def get_color(self): | |
15 | def __init__(self, mapping, draw_lock, key_name, key_sym, top, left, width = 48, height = 48, disabled = False): | 18 | if not self.has_actions: |
16 | self.draw_lock = draw_lock | 19 | return [1, 1, 1, 1] |
17 | self.mapping = mapping | 20 | elif self.all_actions_ready: |
18 | self.key_name = key_name | 21 | return self.custom_color |
19 | self.key_sym = key_sym | ||
20 | |||
21 | self.top = top | ||
22 | self.left = left | ||
23 | self.width = width | ||
24 | self.height = height | ||
25 | |||
26 | self.bottom = self.top + self.height | ||
27 | self.right = self.left + self.width | ||
28 | |||
29 | self.rect = (self.left, self.top, self.right, self.bottom) | ||
30 | self.position = (self.left, self.top) | ||
31 | self.disabled = disabled | ||
32 | |||
33 | if disabled: | ||
34 | self.outer_color = self.lighter_outer_color | ||
35 | self.linewidth = 1 | ||
36 | else: | 22 | else: |
37 | self.outer_color = self.default_outer_color | 23 | return self.custom_unready_color |
38 | self.linewidth = 3 | 24 | def set_color(self): |
25 | pass | ||
26 | |||
27 | color = AliasProperty(get_color, set_color, bind=['is_key_ready']) | ||
39 | 28 | ||
40 | self.inner_color = self.default_inner_color | 29 | def __init__(self, **kwargs): |
30 | super(Key, self).__init__(**kwargs) | ||
41 | self.actions = [] | 31 | self.actions = [] |
42 | self.description = [] | ||
43 | self.custom_color = self.mapped_inner_color | ||
44 | self.custom_unready_color = self.mapped_unready_inner_color | ||
45 | |||
46 | def square(self, all_actions_ready): | ||
47 | if self.has_actions(): | ||
48 | if all_actions_ready: | ||
49 | self.inner_color = self.custom_color | ||
50 | else: | ||
51 | self.inner_color = self.custom_unready_color | ||
52 | 32 | ||
53 | return RoundedRect((0, 0, self.width, self.height), | 33 | def on_key_sym(self, key, key_sym): |
54 | self.outer_color, self.inner_color, self.linewidth) | 34 | if key_sym in self.parent.key_config: |
35 | self.is_key_ready = False | ||
36 | |||
37 | self.config = self.parent.key_config[key_sym] | ||
55 | 38 | ||
56 | def collidepoint(self, position): | 39 | self.actions = [] |
57 | return self.surface.get_rect().collidepoint( | 40 | for key_action in self.config['actions']: |
58 | position[0] - self.position[0], | 41 | self.add_action(key_action[0], **key_action[1]) |
59 | position[1] - self.position[1] | 42 | |
60 | ) | 43 | if 'description' in self.config['properties']: |
44 | key.set_description(self.config['properties']['description']) | ||
45 | if 'color' in self.config['properties']: | ||
46 | key.set_color(self.config['properties']['color']) | ||
47 | |||
48 | Clock.schedule_interval(self.check_all_active, 1) | ||
49 | |||
50 | def check_all_active(self, dt): | ||
51 | if self.all_actions_ready: | ||
52 | self.is_key_ready = True | ||
53 | return False | ||
61 | 54 | ||
62 | def set_description(self, description): | 55 | def set_description(self, description): |
63 | for desc in description: | 56 | if description[0] is not None: |
57 | self.description_title = str(description[0]) | ||
58 | for desc in description[1:]: | ||
64 | if desc is None: | 59 | if desc is None: |
65 | self.description.append("") | 60 | self.description.append("") |
66 | else: | 61 | else: |
67 | self.description.append(str(desc)) | 62 | self.description.append(str(desc).replace(" ", " ")) |
68 | 63 | ||
69 | def set_color(self, color): | 64 | def set_color(self, color): |
70 | self.custom_color = tuple(color) | 65 | color = [x / 255 for x in color] |
71 | color.append(100) | 66 | color.append(1) |
67 | self.custom_color = color | ||
68 | color[3] = 100 / 255 | ||
72 | self.custom_unready_color = tuple(color) | 69 | self.custom_unready_color = tuple(color) |
73 | 70 | ||
74 | def draw(self, background_surface): | 71 | @property |
75 | self.draw_lock.acquire() | ||
76 | all_actions_ready = self.all_actions_ready() | ||
77 | |||
78 | self.surface = self.square(all_actions_ready).surface() | ||
79 | |||
80 | police = font(14) | ||
81 | text_police = font(10) | ||
82 | |||
83 | police.set_bold(True) | ||
84 | text = police.render(self.key_sym, True, (0,0,0)) | ||
85 | self.surface.blit(text, (5,5)) | ||
86 | |||
87 | is_first_line = True | ||
88 | offset = 11 + text_police.get_linesize() - 4 | ||
89 | first_line_offset = 18 | ||
90 | for description in self.description: | ||
91 | text = text_police.render(description, True, (0,0,0)) | ||
92 | if is_first_line: | ||
93 | self.surface.blit(text, (first_line_offset, 9)) | ||
94 | is_first_line = False | ||
95 | else: | ||
96 | self.surface.blit(text, (3, offset)) | ||
97 | offset += text_police.get_linesize() - 4 | ||
98 | |||
99 | background_surface.blit(self.surface, self.position) | ||
100 | self.draw_lock.release() | ||
101 | |||
102 | return not all_actions_ready | ||
103 | |||
104 | def poll_redraw(self, background): | ||
105 | while True: | ||
106 | time.sleep(1) | ||
107 | if self.all_actions_ready(): | ||
108 | self.draw(background) | ||
109 | self.mapping.blit() | ||
110 | break | ||
111 | |||
112 | def has_actions(self): | 72 | def has_actions(self): |
113 | return len(self.actions) > 0 | 73 | return len(self.actions) > 0 |
114 | 74 | ||
75 | @property | ||
115 | def all_actions_ready(self): | 76 | def all_actions_ready(self): |
116 | return all(action.ready() for action in self.actions) | 77 | return all(action.ready() for action in self.actions) |
117 | 78 | ||
118 | def add_action(self, action_name, **arguments): | 79 | def add_action(self, action_name, **arguments): |
119 | self.actions.append(Action(action_name, self, **arguments)) | 80 | self.actions.append(Action(action_name, self, **arguments)) |
120 | 81 | ||
121 | def do_actions(self, screen): | 82 | def do_actions(self): |
122 | print("running actions for {}".format(self.key_sym)) | 83 | print("running actions for {}".format(self.key_sym)) |
123 | start_time = time.time() | 84 | start_time = time.time() |
124 | self.mapping.start_running(self, start_time) | 85 | self.parent.start_running(self, start_time) |
125 | action_number = 0 | 86 | action_number = 0 |
126 | for action in self.actions: | 87 | for action in self.actions: |
127 | if self.mapping.keep_running(self, start_time): | 88 | if self.parent.keep_running(self, start_time): |
128 | self.list_actions(screen, action_number = action_number + 0.5) | 89 | self.list_actions(action_number = action_number + 0.5) |
129 | action.run() | 90 | action.run() |
130 | action_number += 1 | 91 | action_number += 1 |
131 | self.list_actions(screen, action_number = action_number) | 92 | self.list_actions(action_number = action_number) |
132 | |||
133 | self.mapping.finished_running(self, start_time) | ||
134 | |||
135 | def list_actions(self, screen, action_number = 0): | ||
136 | action_descriptions = [action.description() for action in self.actions] | ||
137 | #print("actions linked to key {}:".format(self.key_sym)) | ||
138 | #print("\t" + "\n\t".join(action_descriptions)) | ||
139 | self.draw_lock.acquire() | ||
140 | surface = pygame.Surface((690, 250)).convert() | ||
141 | surface.fill((250, 250, 250)) | ||
142 | police = font(14) | ||
143 | |||
144 | offset = 0 | ||
145 | police.set_bold(True) | ||
146 | text = police.render("actions linked to key {}:".format(self.key_sym), True, (0,0,0)) | ||
147 | surface.blit(text, (0, offset)) | ||
148 | offset += police.get_linesize() | ||
149 | |||
150 | police.set_bold(False) | ||
151 | icon_police = font(14, font = "Symbola") | ||
152 | for index, description in enumerate(action_descriptions): | ||
153 | if index < int(action_number): | ||
154 | icon = icon_police.render("✓", True, (0,0,0)) | ||
155 | elif index + 0.5 == action_number: | ||
156 | icon = icon_police.render("✅", True, (0,0,0)) | ||
157 | else: | ||
158 | icon = icon_police.render(" ", True, (0,0,0)) | ||
159 | |||
160 | text = police.render(description, True, (0,0,0)) | ||
161 | surface.blit(icon, (0, offset)) | ||
162 | surface.blit(text, (10, offset)) | ||
163 | offset += police.get_linesize() | ||
164 | 93 | ||
165 | screen.blit(surface, (5, 308)) | 94 | self.parent.finished_running(self, start_time) |
166 | pygame.display.flip() | ||
167 | self.draw_lock.release() | ||
168 | 95 | ||
96 | def list_actions(self, action_number = 0): | ||
97 | self.parent.parent.ids['ActionList'].update_list(self, action_number) | ||
169 | 98 | ||
99 | def on_press(self): | ||
100 | self.list_actions() | ||
101 | pass | ||
diff --git a/helpers/mapping.py b/helpers/mapping.py index 58aaf3d..4a1cd98 100644 --- a/helpers/mapping.py +++ b/helpers/mapping.py | |||
@@ -1,175 +1,51 @@ | |||
1 | from kivy.uix.relativelayout import RelativeLayout | ||
2 | from kivy.properties import NumericProperty | ||
3 | from kivy.core.window import Window | ||
4 | |||
1 | import threading | 5 | import threading |
2 | import pygame | 6 | import pygame |
3 | from .key import * | 7 | import yaml |
4 | 8 | ||
5 | class Mapping: | 9 | from .lock import * |
6 | WIDTH = 903 | 10 | from .music_file import * |
7 | HEIGHT = 298 | 11 | |
8 | SIZE = WIDTH, HEIGHT | 12 | class Mapping(RelativeLayout): |
9 | 13 | expected_keys = NumericProperty(0) | |
10 | ROW_POSITIONS = { | 14 | |
11 | 'first': 0, | 15 | def __init__(self, **kwargs): |
12 | 'second': 50, | 16 | self.key_config, self.channel_number, self.open_files = self.parse_config() |
13 | 'third': 100, | 17 | super(Mapping, self).__init__(**kwargs) |
14 | 'fourth': 150, | 18 | self._keyboard = Window.request_keyboard(self._keyboard_closed, self) |
15 | 'fifth': 200, | 19 | self._keyboard.bind(on_key_down=self._on_keyboard_down) |
16 | 'sixth': 250, | ||
17 | } | ||
18 | |||
19 | KEYS = [ | ||
20 | (pygame.K_ESCAPE, 'ESC', 'first', 0, {}), | ||
21 | |||
22 | (pygame.K_F1, 'F1', 'first', 100, {}), | ||
23 | (pygame.K_F2, 'F2', 'first', 150, {}), | ||
24 | (pygame.K_F3, 'F3', 'first', 200, {}), | ||
25 | (pygame.K_F4, 'F4', 'first', 250, {}), | ||
26 | |||
27 | (pygame.K_F5, 'F5', 'first', 325, {}), | ||
28 | (pygame.K_F6, 'F6', 'first', 375, {}), | ||
29 | (pygame.K_F7, 'F7', 'first', 425, {}), | ||
30 | (pygame.K_F8, 'F8', 'first', 475, {}), | ||
31 | |||
32 | (pygame.K_F9, 'F9', 'first', 550, {}), | ||
33 | (pygame.K_F10, 'F10', 'first', 600, {}), | ||
34 | (pygame.K_F11, 'F11', 'first', 650, {}), | ||
35 | (pygame.K_F12, 'F12', 'first', 700, {}), | ||
36 | |||
37 | |||
38 | (178, '²', 'second', 0, {}), | ||
39 | (pygame.K_AMPERSAND, '&', 'second', 50, {}), | ||
40 | (233, 'é', 'second', 100, {}), | ||
41 | (pygame.K_QUOTEDBL, '"', 'second', 150, {}), | ||
42 | (pygame.K_QUOTE, "'", 'second', 200, {}), | ||
43 | (pygame.K_LEFTPAREN, '(', 'second', 250, {}), | ||
44 | (pygame.K_MINUS, '-', 'second', 300, {}), | ||
45 | (232, 'è', 'second', 350, {}), | ||
46 | (pygame.K_UNDERSCORE, '_', 'second', 400, {}), | ||
47 | (231, 'ç', 'second', 450, {}), | ||
48 | (224, 'à', 'second', 500, {}), | ||
49 | (pygame.K_RIGHTPAREN, ')', 'second', 550, {}), | ||
50 | (pygame.K_EQUALS, '=', 'second', 600, {}), | ||
51 | |||
52 | (pygame.K_BACKSPACE, '<-', 'second', 650, { 'width': 98 }), | ||
53 | |||
54 | |||
55 | (pygame.K_TAB, 'tab', 'third', 0, { 'width' : 73 }), | ||
56 | (pygame.K_a, 'a', 'third', 75, {}), | ||
57 | (pygame.K_z, 'z', 'third', 125, {}), | ||
58 | (pygame.K_e, 'e', 'third', 175, {}), | ||
59 | (pygame.K_r, 'r', 'third', 225, {}), | ||
60 | (pygame.K_t, 't', 'third', 275, {}), | ||
61 | (pygame.K_y, 'y', 'third', 325, {}), | ||
62 | (pygame.K_u, 'u', 'third', 375, {}), | ||
63 | (pygame.K_i, 'i', 'third', 425, {}), | ||
64 | (pygame.K_o, 'o', 'third', 475, {}), | ||
65 | (pygame.K_p, 'p', 'third', 525, {}), | ||
66 | (pygame.K_CARET, '^', 'third', 575, {}), | ||
67 | (pygame.K_DOLLAR, '$', 'third', 625, {}), | ||
68 | |||
69 | (pygame.K_RETURN, 'Enter', 'third', 692, { 'width': 56, 'height': 98 }), | ||
70 | |||
71 | (pygame.K_CAPSLOCK, 'CAPS', 'fourth', 0, { 'width': 88, 'disabled': True }), | ||
72 | |||
73 | (pygame.K_q, 'q', 'fourth', 90, {}), | ||
74 | (pygame.K_s, 's', 'fourth', 140, {}), | ||
75 | (pygame.K_d, 'd', 'fourth', 190, {}), | ||
76 | (pygame.K_f, 'f', 'fourth', 240, {}), | ||
77 | (pygame.K_g, 'g', 'fourth', 290, {}), | ||
78 | (pygame.K_h, 'h', 'fourth', 340, {}), | ||
79 | (pygame.K_j, 'j', 'fourth', 390, {}), | ||
80 | (pygame.K_k, 'k', 'fourth', 440, {}), | ||
81 | (pygame.K_l, 'l', 'fourth', 490, {}), | ||
82 | (pygame.K_m, 'm', 'fourth', 540, {}), | ||
83 | (249, 'ù', 'fourth', 590, {}), | ||
84 | (pygame.K_ASTERISK, '*', 'fourth', 640, {}), | ||
85 | |||
86 | |||
87 | (pygame.K_LSHIFT, 'LShift', 'fifth', 0, { 'width': 63, 'disabled': True }), | ||
88 | |||
89 | (pygame.K_LESS, '<', 'fifth', 65, {}), | ||
90 | (pygame.K_w, 'w', 'fifth', 115, {}), | ||
91 | (pygame.K_x, 'x', 'fifth', 165, {}), | ||
92 | (pygame.K_c, 'c', 'fifth', 215, {}), | ||
93 | (pygame.K_v, 'v', 'fifth', 265, {}), | ||
94 | (pygame.K_b, 'b', 'fifth', 315, {}), | ||
95 | (pygame.K_n, 'n', 'fifth', 365, {}), | ||
96 | (pygame.K_COMMA, ',', 'fifth', 415, {}), | ||
97 | (pygame.K_SEMICOLON, ';', 'fifth', 465, {}), | ||
98 | (pygame.K_COLON, ':', 'fifth', 515, {}), | ||
99 | (pygame.K_EXCLAIM, '!', 'fifth', 565, {}), | ||
100 | |||
101 | (pygame.K_RSHIFT, 'RShift', 'fifth', 615, { 'width': 133, 'disabled': True }), | ||
102 | |||
103 | (pygame.K_LCTRL, 'LCtrl', 'sixth', 0, { 'width': 63, 'disabled': True }), | ||
104 | (pygame.K_LSUPER, 'LSuper', 'sixth', 115, { 'disabled': True }), | ||
105 | (pygame.K_LALT, 'LAlt', 'sixth', 165, { 'disabled': True }), | ||
106 | (pygame.K_SPACE, 'Espace', 'sixth', 215, { 'width': 248 }), | ||
107 | (pygame.K_MODE, 'AltGr', 'sixth', 465, { 'disabled': True }), | ||
108 | (314, 'Compose', 'sixth', 515, { 'disabled': True }), | ||
109 | (pygame.K_RCTRL, 'RCtrl', 'sixth', 565, { 'width': 63, 'disabled': True }), | ||
110 | |||
111 | |||
112 | (pygame.K_INSERT, 'ins', 'second', 755, {}), | ||
113 | (pygame.K_HOME, 'home', 'second', 805, {}), | ||
114 | (pygame.K_PAGEUP, 'pg_u', 'second', 855, {}), | ||
115 | (pygame.K_DELETE, 'del', 'third', 755, {}), | ||
116 | (pygame.K_END, 'end', 'third', 805, {}), | ||
117 | (pygame.K_PAGEDOWN, 'pg_d', 'third', 855, {}), | ||
118 | |||
119 | |||
120 | (pygame.K_UP, 'up', 'fifth', 805, {}), | ||
121 | (pygame.K_DOWN, 'down', 'sixth', 805, {}), | ||
122 | (pygame.K_LEFT, 'left', 'sixth', 755, {}), | ||
123 | (pygame.K_RIGHT, 'right', 'sixth', 855, {}), | ||
124 | ] | ||
125 | |||
126 | def __init__(self, screen, draw_lock): | ||
127 | self.draw_lock = draw_lock | ||
128 | self.screen = screen | ||
129 | self.background = pygame.Surface(self.SIZE).convert() | ||
130 | self.background.fill((250, 250, 250)) | ||
131 | self.keys = {} | ||
132 | self.running = [] | 20 | self.running = [] |
133 | for key in self.KEYS: | ||
134 | if key[2] in self.ROW_POSITIONS: | ||
135 | position = self.ROW_POSITIONS[key[2]] | ||
136 | else: | ||
137 | position = key[2] | ||
138 | self.keys[key[0]] = Key(self, | ||
139 | self.draw_lock, | ||
140 | key[0], key[1], position, key[3], | ||
141 | **key[4]) | ||
142 | |||
143 | def draw(self): | ||
144 | for key_name in self.keys: | ||
145 | key = self.keys[key_name] | ||
146 | should_redraw_key = key.draw(self.background) | ||
147 | |||
148 | if should_redraw_key: | ||
149 | threading.Thread(name = "MSPollRedraw", target = key.poll_redraw, args = [self.background]).start() | ||
150 | self.blit() | ||
151 | |||
152 | def blit(self): | ||
153 | self.draw_lock.acquire() | ||
154 | self.screen.blit(self.background, (5, 5)) | ||
155 | pygame.display.flip() | ||
156 | self.draw_lock.release() | ||
157 | |||
158 | def find_by_key_num(self, key_num): | ||
159 | if key_num in self.keys: | ||
160 | return self.keys[key_num] | ||
161 | return None | ||
162 | 21 | ||
163 | def find_by_collidepoint(self, position): | 22 | |
164 | for key in self.keys: | 23 | pygame.mixer.init(frequency = 44100) |
165 | if self.keys[key].collidepoint(position): | 24 | pygame.mixer.set_num_channels(self.channel_number) |
166 | return self.keys[key] | 25 | |
26 | def _keyboard_closed(self): | ||
27 | self._keyboard.unbind(on_key_down=self._on_keyboard_down) | ||
28 | self._keyboard = None | ||
29 | |||
30 | def _on_keyboard_down(self, keyboard, keycode, text, modifiers): | ||
31 | key = self.find_by_key_code(keycode) | ||
32 | if key is not None: | ||
33 | threading.Thread(name = "MSKeyAction", target=key.do_actions).start() | ||
34 | return True | ||
35 | |||
36 | def find_by_key_code(self, key_code): | ||
37 | if "Key_" + str(key_code[0]) in self.ids: | ||
38 | return self.ids["Key_" + str(key_code[0])] | ||
167 | return None | 39 | return None |
168 | 40 | ||
169 | def find_by_unicode(self, key_sym): | 41 | def find_by_unicode(self, key_sym): |
170 | for key in self.keys: | 42 | for key in self.children: |
171 | if self.keys[key].key_sym == key_sym: | 43 | if not type(key).__name__ == "Key": |
172 | return self.keys[key] | 44 | continue |
45 | print(key.key_sym, key_sym) | ||
46 | if key.key_sym == key_sym: | ||
47 | print("found") | ||
48 | return key | ||
173 | return None | 49 | return None |
174 | 50 | ||
175 | def stop_all_running(self): | 51 | def stop_all_running(self): |
@@ -185,3 +61,78 @@ class Mapping: | |||
185 | if (key, start_time) in self.running: | 61 | if (key, start_time) in self.running: |
186 | self.running.remove((key, start_time)) | 62 | self.running.remove((key, start_time)) |
187 | 63 | ||
64 | def parse_config(self): | ||
65 | stream = open("config.yml", "r") | ||
66 | config = yaml.load(stream) | ||
67 | stream.close() | ||
68 | |||
69 | aliases = config['aliases'] | ||
70 | seen_files = {} | ||
71 | |||
72 | file_lock = Lock("file") | ||
73 | |||
74 | channel_id = 0 | ||
75 | |||
76 | key_properties = {} | ||
77 | |||
78 | for key in config['key_properties']: | ||
79 | if key not in key_properties: | ||
80 | key_properties[key] = { | ||
81 | "actions": [], | ||
82 | "properties": config['key_properties'][key], | ||
83 | "files": [] | ||
84 | } | ||
85 | |||
86 | for mapped_key in config['keys']: | ||
87 | if mapped_key not in key_properties: | ||
88 | key_properties[mapped_key] = { | ||
89 | "actions": [], | ||
90 | "properties": {}, | ||
91 | "files": [] | ||
92 | } | ||
93 | for action in config['keys'][mapped_key]: | ||
94 | action_name = list(action)[0] | ||
95 | action_args = {} | ||
96 | if action[action_name] is None: | ||
97 | action[action_name] = [] | ||
98 | |||
99 | if 'include' in action[action_name]: | ||
100 | included = action[action_name]['include'] | ||
101 | del(action[action_name]['include']) | ||
102 | |||
103 | if isinstance(included, str): | ||
104 | action[action_name].update(aliases[included], **action[action_name]) | ||
105 | else: | ||
106 | for included_ in included: | ||
107 | action[action_name].update(aliases[included_], **action[action_name]) | ||
108 | |||
109 | for argument in action[action_name]: | ||
110 | if argument == 'file': | ||
111 | filename = action[action_name]['file'] | ||
112 | if filename not in seen_files: | ||
113 | if filename in config['music_properties']: | ||
114 | seen_files[filename] = MusicFile( | ||
115 | filename, | ||
116 | file_lock, | ||
117 | channel_id, | ||
118 | **config['music_properties'][filename]) | ||
119 | else: | ||
120 | seen_files[filename] = MusicFile( | ||
121 | filename, | ||
122 | file_lock, | ||
123 | channel_id) | ||
124 | channel_id = channel_id + 1 | ||
125 | |||
126 | if filename not in key_properties[mapped_key]['files']: | ||
127 | key_properties[mapped_key]['files'].append(seen_files[filename]) | ||
128 | |||
129 | action_args['music'] = seen_files[filename] | ||
130 | |||
131 | else: | ||
132 | action_args[argument] = action[action_name][argument] | ||
133 | |||
134 | key_properties[mapped_key]['actions'].append([action_name, action_args]) | ||
135 | |||
136 | return (key_properties, channel_id + 1, seen_files) | ||
137 | |||
138 | |||
diff --git a/helpers/rounded_rect.py b/helpers/rounded_rect.py deleted file mode 100644 index f168e0e..0000000 --- a/helpers/rounded_rect.py +++ /dev/null | |||
@@ -1,62 +0,0 @@ | |||
1 | import pygame | ||
2 | |||
3 | class RoundedRect: | ||
4 | def __init__(self, rect, outer_color, inner_color, linewidth = 2, radius = 0.4): | ||
5 | self.rect = pygame.Rect(rect) | ||
6 | self.outer_color = pygame.Color(*outer_color) | ||
7 | self.inner_color = pygame.Color(*inner_color) | ||
8 | self.linewidth = linewidth | ||
9 | self.radius = radius | ||
10 | |||
11 | def surface(self): | ||
12 | rectangle = self.filledRoundedRect(self.rect, self.outer_color, self.radius) | ||
13 | |||
14 | inner_rect = pygame.Rect(( | ||
15 | self.rect.left + 2 * self.linewidth, | ||
16 | self.rect.top + 2 * self.linewidth, | ||
17 | self.rect.right - 2 * self.linewidth, | ||
18 | self.rect.bottom - 2 * self.linewidth | ||
19 | )) | ||
20 | |||
21 | inner_rectangle = self.filledRoundedRect(inner_rect, self.inner_color, self.radius) | ||
22 | |||
23 | rectangle.blit(inner_rectangle, (self.linewidth, self.linewidth)) | ||
24 | |||
25 | return rectangle | ||
26 | |||
27 | def filledRoundedRect(self, rect, color, radius=0.4): | ||
28 | """ | ||
29 | filledRoundedRect(rect,color,radius=0.4) | ||
30 | |||
31 | rect : rectangle | ||
32 | color : rgb or rgba | ||
33 | radius : 0 <= radius <= 1 | ||
34 | """ | ||
35 | |||
36 | alpha = color.a | ||
37 | color.a = 0 | ||
38 | pos = rect.topleft | ||
39 | rect.topleft = 0,0 | ||
40 | rectangle = pygame.Surface(rect.size,pygame.SRCALPHA) | ||
41 | |||
42 | circle = pygame.Surface([min(rect.size)*3]*2,pygame.SRCALPHA) | ||
43 | pygame.draw.ellipse(circle,(0,0,0),circle.get_rect(),0) | ||
44 | circle = pygame.transform.smoothscale(circle,[int(min(rect.size)*radius)]*2) | ||
45 | |||
46 | radius = rectangle.blit(circle,(0,0)) | ||
47 | radius.bottomright = rect.bottomright | ||
48 | rectangle.blit(circle,radius) | ||
49 | radius.topright = rect.topright | ||
50 | rectangle.blit(circle,radius) | ||
51 | radius.bottomleft = rect.bottomleft | ||
52 | rectangle.blit(circle,radius) | ||
53 | |||
54 | rectangle.fill((0,0,0),rect.inflate(-radius.w,0)) | ||
55 | rectangle.fill((0,0,0),rect.inflate(0,-radius.h)) | ||
56 | |||
57 | rectangle.fill(color,special_flags=pygame.BLEND_RGBA_MAX) | ||
58 | rectangle.fill((255,255,255,alpha),special_flags=pygame.BLEND_RGBA_MIN) | ||
59 | |||
60 | return rectangle | ||
61 | |||
62 | |||
diff --git a/keyboard.py b/keyboard.py deleted file mode 100644 index 0c1115f..0000000 --- a/keyboard.py +++ /dev/null | |||
@@ -1,222 +0,0 @@ | |||
1 | from kivy.app import App | ||
2 | from kivy.uix.widget import Widget | ||
3 | from kivy.uix.floatlayout import FloatLayout | ||
4 | from kivy.uix.relativelayout import RelativeLayout | ||
5 | from kivy.properties import AliasProperty, ReferenceListProperty, BooleanProperty, NumericProperty, ListProperty, StringProperty, ObjectProperty | ||
6 | from kivy.vector import Vector | ||
7 | from kivy.clock import Clock | ||
8 | from kivy.uix.behaviors import ButtonBehavior | ||
9 | from kivy.uix.label import Label | ||
10 | from kivy.core.window import Window | ||
11 | |||
12 | from helpers.action import * | ||
13 | import time | ||
14 | import sys | ||
15 | import math | ||
16 | import pygame | ||
17 | import helpers | ||
18 | import threading | ||
19 | |||
20 | class KeyDescription(Label): | ||
21 | pass | ||
22 | |||
23 | class Key(ButtonBehavior, Widget): | ||
24 | key_sym = StringProperty(None) | ||
25 | custom_color = ListProperty([0, 1, 0, 1]) | ||
26 | custom_unready_color = ListProperty([0, 1, 0, 100/255]) | ||
27 | description_title = StringProperty("") | ||
28 | description = ListProperty([]) | ||
29 | is_key_ready = BooleanProperty(True) | ||
30 | |||
31 | def get_color(self): | ||
32 | if not self.has_actions: | ||
33 | return [1, 1, 1, 1] | ||
34 | elif self.all_actions_ready: | ||
35 | return self.custom_color | ||
36 | else: | ||
37 | return self.custom_unready_color | ||
38 | def set_color(self): | ||
39 | pass | ||
40 | |||
41 | color = AliasProperty(get_color, set_color, bind=['is_key_ready']) | ||
42 | |||
43 | def __init__(self, **kwargs): | ||
44 | super(Key, self).__init__(**kwargs) | ||
45 | self.actions = [] | ||
46 | |||
47 | def on_key_sym(self, key, key_sym): | ||
48 | if key_sym in self.parent.key_config: | ||
49 | self.is_key_ready = False | ||
50 | |||
51 | self.config = self.parent.key_config[key_sym] | ||
52 | |||
53 | self.actions = [] | ||
54 | for key_action in self.config['actions']: | ||
55 | self.add_action(key_action[0], **key_action[1]) | ||
56 | |||
57 | if 'description' in self.config['properties']: | ||
58 | key.set_description(self.config['properties']['description']) | ||
59 | if 'color' in self.config['properties']: | ||
60 | key.set_color(self.config['properties']['color']) | ||
61 | |||
62 | Clock.schedule_interval(self.check_all_active, 1) | ||
63 | |||
64 | def check_all_active(self, dt): | ||
65 | if self.all_actions_ready: | ||
66 | self.is_key_ready = True | ||
67 | return False | ||
68 | |||
69 | def set_description(self, description): | ||
70 | if description[0] is not None: | ||
71 | self.description_title = str(description[0]) | ||
72 | for desc in description[1:]: | ||
73 | if desc is None: | ||
74 | self.description.append("") | ||
75 | else: | ||
76 | self.description.append(str(desc).replace(" ", " ")) | ||
77 | |||
78 | def set_color(self, color): | ||
79 | color = [x / 255 for x in color] | ||
80 | color.append(1) | ||
81 | self.custom_color = color | ||
82 | color[3] = 100 / 255 | ||
83 | self.custom_unready_color = tuple(color) | ||
84 | |||
85 | @property | ||
86 | def has_actions(self): | ||
87 | return len(self.actions) > 0 | ||
88 | |||
89 | @property | ||
90 | def all_actions_ready(self): | ||
91 | return all(action.ready() for action in self.actions) | ||
92 | |||
93 | def add_action(self, action_name, **arguments): | ||
94 | self.actions.append(Action(action_name, self, **arguments)) | ||
95 | |||
96 | def do_actions(self): | ||
97 | print("running actions for {}".format(self.key_sym)) | ||
98 | start_time = time.time() | ||
99 | self.parent.start_running(self, start_time) | ||
100 | action_number = 0 | ||
101 | for action in self.actions: | ||
102 | if self.parent.keep_running(self, start_time): | ||
103 | self.list_actions(action_number = action_number + 0.5) | ||
104 | action.run() | ||
105 | action_number += 1 | ||
106 | self.list_actions(action_number = action_number) | ||
107 | |||
108 | self.parent.finished_running(self, start_time) | ||
109 | |||
110 | def list_actions(self, action_number = 0): | ||
111 | self.parent.parent.ids['ActionList'].update_list(self, action_number) | ||
112 | |||
113 | def on_press(self): | ||
114 | self.list_actions() | ||
115 | pass | ||
116 | |||
117 | class PlayList(RelativeLayout): | ||
118 | playlist = ListProperty([]) | ||
119 | |||
120 | def __init__(self, **kwargs): | ||
121 | super(PlayList, self).__init__(**kwargs) | ||
122 | Clock.schedule_interval(self.update_playlist, 0.5) | ||
123 | |||
124 | def update_playlist(self, dt): | ||
125 | if self.parent is None or 'Mapping' not in self.parent.ids: | ||
126 | return True | ||
127 | |||
128 | open_files = self.parent.ids['Mapping'].open_files | ||
129 | self.playlist = [] | ||
130 | for music_file in open_files.values(): | ||
131 | if not music_file.is_playing(): | ||
132 | continue | ||
133 | if music_file.is_paused(): | ||
134 | self.playlist.append(["⏸", music_file.name, False]) | ||
135 | else: | ||
136 | self.playlist.append(["⏵", music_file.name, True]) | ||
137 | |||
138 | |||
139 | class ActionList(RelativeLayout): | ||
140 | action_title = StringProperty("") | ||
141 | action_list = ListProperty([]) | ||
142 | |||
143 | def update_list(self, key, action_number = 0): | ||
144 | self.action_title = "actions linked to key {}:".format(key.key_sym) | ||
145 | self.action_list = [] | ||
146 | |||
147 | action_descriptions = [action.description() for action in key.actions] | ||
148 | |||
149 | for index, description in enumerate(action_descriptions): | ||
150 | if index < int(action_number): | ||
151 | icon = "✓" | ||
152 | elif index + 0.5 == action_number: | ||
153 | icon = "✅" | ||
154 | else: | ||
155 | icon = " " | ||
156 | |||
157 | self.action_list.append([icon, description]) | ||
158 | |||
159 | class Mapping(RelativeLayout): | ||
160 | expected_keys = NumericProperty(0) | ||
161 | |||
162 | def __init__(self, **kwargs): | ||
163 | self.key_config, self.channel_number, self.open_files = helpers.parse_config2() | ||
164 | super(Mapping, self).__init__(**kwargs) | ||
165 | self._keyboard = Window.request_keyboard(self._keyboard_closed, self) | ||
166 | self._keyboard.bind(on_key_down=self._on_keyboard_down) | ||
167 | self.running = [] | ||
168 | |||
169 | |||
170 | pygame.mixer.init(frequency = 44100) | ||
171 | pygame.mixer.set_num_channels(self.channel_number) | ||
172 | |||
173 | def _keyboard_closed(self): | ||
174 | self._keyboard.unbind(on_key_down=self._on_keyboard_down) | ||
175 | self._keyboard = None | ||
176 | |||
177 | def _on_keyboard_down(self, keyboard, keycode, text, modifiers): | ||
178 | key = self.find_by_key_code(keycode) | ||
179 | if key is not None: | ||
180 | threading.Thread(name = "MSKeyAction", target=key.do_actions).start() | ||
181 | return True | ||
182 | |||
183 | def find_by_key_code(self, key_code): | ||
184 | if "Key_" + str(key_code[0]) in self.ids: | ||
185 | return self.ids["Key_" + str(key_code[0])] | ||
186 | return None | ||
187 | |||
188 | def find_by_unicode(self, key_sym): | ||
189 | for key in self.children: | ||
190 | if not type(key).__name__ == "Key": | ||
191 | continue | ||
192 | print(key.key_sym, key_sym) | ||
193 | if key.key_sym == key_sym: | ||
194 | print("found") | ||
195 | return key | ||
196 | return None | ||
197 | |||
198 | def stop_all_running(self): | ||
199 | self.running = [] | ||
200 | |||
201 | def start_running(self, key, start_time): | ||
202 | self.running.append((key, start_time)) | ||
203 | |||
204 | def keep_running(self, key, start_time): | ||
205 | return (key, start_time) in self.running | ||
206 | |||
207 | def finished_running(self, key, start_time): | ||
208 | if (key, start_time) in self.running: | ||
209 | self.running.remove((key, start_time)) | ||
210 | |||
211 | |||
212 | class Screen(FloatLayout): | ||
213 | pass | ||
214 | |||
215 | class MusicSamplerApp(App): | ||
216 | def build(self): | ||
217 | Window.size = (913, 563) | ||
218 | |||
219 | return Screen() | ||
220 | |||
221 | if __name__ == '__main__': | ||
222 | MusicSamplerApp().run() | ||
diff --git a/music_sampler.py b/music_sampler.py index 42c01e3..0d9a7a9 100644 --- a/music_sampler.py +++ b/music_sampler.py | |||
@@ -1,80 +1,63 @@ | |||
1 | import sys | 1 | from kivy.app import App |
2 | import pygame | 2 | from kivy.uix.floatlayout import FloatLayout |
3 | import helpers | 3 | from kivy.uix.relativelayout import RelativeLayout |
4 | import threading | 4 | from kivy.properties import ListProperty, StringProperty |
5 | 5 | from kivy.clock import Clock | |
6 | pygame.mixer.pre_init(frequency = 44100) | 6 | from kivy.core.window import Window |
7 | pygame.init() | 7 | |
8 | 8 | from helpers.key import Key | |
9 | size = width, height = 913, 563 | 9 | from helpers.mapping import Mapping |
10 | screen = pygame.display.set_mode(size) | 10 | |
11 | screen.fill((229, 228, 226)) | 11 | class PlayList(RelativeLayout): |
12 | 12 | playlist = ListProperty([]) | |
13 | draw_lock = helpers.Lock("draw") | 13 | |
14 | 14 | def __init__(self, **kwargs): | |
15 | mapping = helpers.Mapping(screen, draw_lock) | 15 | super(PlayList, self).__init__(**kwargs) |
16 | channel_number, open_files = helpers.parse_config(mapping) | 16 | Clock.schedule_interval(self.update_playlist, 0.5) |
17 | pygame.mixer.set_num_channels(channel_number) | 17 | |
18 | 18 | def update_playlist(self, dt): | |
19 | mapping.draw() | 19 | if self.parent is None or 'Mapping' not in self.parent.ids: |
20 | return True | ||
21 | |||
22 | open_files = self.parent.ids['Mapping'].open_files | ||
23 | self.playlist = [] | ||
24 | for music_file in open_files.values(): | ||
25 | if not music_file.is_playing(): | ||
26 | continue | ||
27 | if music_file.is_paused(): | ||
28 | self.playlist.append(["⏸", music_file.name, False]) | ||
29 | else: | ||
30 | self.playlist.append(["⏵", music_file.name, True]) | ||
20 | 31 | ||
21 | draw_lock.acquire() | ||
22 | pygame.display.flip() | ||
23 | draw_lock.release() | ||
24 | 32 | ||
25 | contexts = [ | 33 | class ActionList(RelativeLayout): |
26 | 'normal' | 34 | action_title = StringProperty("") |
27 | ] | 35 | action_list = ListProperty([]) |
28 | 36 | ||
29 | context = 'normal' | 37 | def update_list(self, key, action_number = 0): |
38 | self.action_title = "actions linked to key {}:".format(key.key_sym) | ||
39 | self.action_list = [] | ||
30 | 40 | ||
31 | #### Normal workflow #### | 41 | action_descriptions = [action.description() for action in key.actions] |
32 | while 1: | ||
33 | event = pygame.event.wait() | ||
34 | 42 | ||
35 | if event.type == pygame.QUIT or ( | 43 | for index, description in enumerate(action_descriptions): |
36 | event.type == pygame.KEYDOWN and | 44 | if index < int(action_number): |
37 | event.mod == 4160 and | 45 | icon = "✓" |
38 | event.key == pygame.K_c): | 46 | elif index + 0.5 == action_number: |
39 | for thread in threading.enumerate(): | 47 | icon = "✅" |
40 | if thread.getName()[0:2] != "MS": | 48 | else: |
41 | continue | 49 | icon = " " |
42 | thread.join() | ||
43 | 50 | ||
44 | pygame.quit() | 51 | self.action_list.append([icon, description]) |
45 | sys.exit() | ||
46 | 52 | ||
47 | if context == 'normal': | 53 | class Screen(FloatLayout): |
48 | if event.type == pygame.KEYDOWN: | 54 | pass |
49 | key = mapping.find_by_key_num(event.key) | ||
50 | if key is not None and not key.disabled: | ||
51 | threading.Thread(name = "MSKeyAction", target=key.do_actions, args = [screen]).start() | ||
52 | threading.Thread(name = "MSClic", target=key.list_actions, args = [screen]).start() | ||
53 | elif event.type == pygame.MOUSEBUTTONUP: | ||
54 | key = mapping.find_by_collidepoint(pygame.mouse.get_pos()) | ||
55 | if key is not None: | ||
56 | threading.Thread(name = "MSClic", target=key.list_actions, args = [screen]).start() | ||
57 | 55 | ||
58 | draw_lock.acquire() | 56 | class MusicSamplerApp(App): |
59 | police = helpers.font(14) | 57 | def build(self): |
60 | icon_police = helpers.font(14, font = "Symbola") | 58 | Window.size = (913, 563) |
61 | 59 | ||
62 | surface = pygame.Surface((208, 250)).convert() | 60 | return Screen() |
63 | surface.fill((250, 250, 250)) | ||
64 | offset = 0 | ||
65 | for music_file in open_files.values(): | ||
66 | police.set_bold(False) | ||
67 | if music_file.is_playing(): | ||
68 | if music_file.is_paused(): | ||
69 | icon = icon_police.render("⏸", True, (0,0,0)) | ||
70 | else: | ||
71 | icon = icon_police.render("⏵", True, (0,0,0)) | ||
72 | police.set_bold(True) | ||
73 | text = police.render(music_file.name, True, (0,0,0)) | ||
74 | surface.blit(icon, (0, offset)) | ||
75 | surface.blit(text, (20, offset)) | ||
76 | offset += police.get_linesize() | ||
77 | screen.blit(surface, (700, 308)) | ||
78 | 61 | ||
79 | pygame.display.flip() | 62 | if __name__ == '__main__': |
80 | draw_lock.release() | 63 | MusicSamplerApp().run() |
diff --git a/musicsampler.kv b/musicsampler.kv index 3c1964b..17be1f5 100644 --- a/musicsampler.kv +++ b/musicsampler.kv | |||
@@ -67,6 +67,13 @@ | |||
67 | size: 2 * self.parent.width - 2 * self.parent.line_width, self.parent.height - key_label.font_size | 67 | size: 2 * self.parent.width - 2 * self.parent.line_width, self.parent.height - key_label.font_size |
68 | 68 | ||
69 | <Screen>: | 69 | <Screen>: |
70 | canvas: | ||
71 | Color: | ||
72 | rgba: 229/255, 228/255, 226/255, 1 | ||
73 | Rectangle: | ||
74 | pos: 0, 0 | ||
75 | size: self.width, self.height | ||
76 | |||
70 | key_size: int( (3 * self.width - 16) / 56) | 77 | key_size: int( (3 * self.width - 16) / 56) |
71 | key_sep: int( self.key_size / 24) | 78 | key_sep: int( self.key_size / 24) |
72 | key_pad_sep: int( self.key_size / 7) + 1 | 79 | key_pad_sep: int( self.key_size / 7) + 1 |