Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/draw_piano.py
diff options
context:
space:
mode:
Diffstat (limited to 'draw_piano.py')
-rwxr-xr-xdraw_piano.py429
1 files changed, 429 insertions, 0 deletions
diff --git a/draw_piano.py b/draw_piano.py
new file mode 100755
index 0000000..6f045c8
--- /dev/null
+++ b/draw_piano.py
@@ -0,0 +1,429 @@
+#!/usr/bin/env python
+# Copyright (C) 2011, One Laptop Per Child
+# Author, Gonzalo Odiard
+# License: LGPLv2
+#
+# The class PianoKeyboard draw a keybord and interact with the mouse
+# References
+# http://www.josef-k.net/mim/MusicSystem.html
+# http://wiki.laptop.org/images/4/4e/Tamtamhelp2.png
+#
+
+import gobject
+import gtk
+import cairo
+
+
+class PianoKeyboard(gtk.DrawingArea):
+
+ __gsignals__ = {'key_pressed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_INT, gobject.TYPE_INT,
+ gobject.TYPE_STRING])),
+ 'key_released': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([gobject.TYPE_INT, gobject.TYPE_INT,
+ gobject.TYPE_STRING]))}
+
+ def __init__(self, octaves=1, add_c=False, labels=None):
+ self._octaves = octaves
+ self._add_c = add_c
+ self._labels = labels
+ self._pressed_key = None
+ self.font_size = 20
+ super(PianoKeyboard, self).__init__()
+ # info needed to check keys positions
+ self._white_keys = [0, 2, 4, 5, 7, 9, 11]
+ self._l_keys_areas = [0, 3]
+ self._t_keys_areas = [1, 4, 5]
+ self._j_keys_areas = [2, 6]
+ self.connect('expose_event', self.expose)
+ self.connect('button_press_event', self.__button_press_cb)
+ self.connect('button_release_event', self.__button_release_cb)
+ self.connect('motion_notify_event', self.__motion_notify_cb)
+ self.set_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.BUTTON_PRESS_MASK | \
+ gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK | \
+ gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ def calculate_sizes(self, width):
+ self._width = width
+ self._height = self._width / 2
+ cant_keys = 7 * self._octaves
+ if self._add_c:
+ cant_keys += 1
+ self._key_width = self._width / cant_keys
+ self._black_keys_height = self._height * 2 / 3
+ self._octave_width = self._key_width * 7
+
+ def __button_press_cb(self, widget, event):
+ if event.button == 1:
+ x, y = event.x, event.y
+ key_found = self.__get_key_at_position(x, y)
+ if key_found is None:
+ return True
+ else:
+ self._pressed_key = key_found
+ octave_clicked = key_found[0]
+ key_clicked = key_found[1]
+ self.emit('key_pressed', octave_clicked, key_clicked,
+ self.get_label(octave_clicked, key_clicked))
+ self.queue_draw()
+ return True
+
+ def __motion_notify_cb(self, widget, event):
+ # only continue if the button is pressed
+ if self._pressed_key is None:
+ return True
+
+ # if this is a hint, then let's get all the necessary
+ # information, if not it's all we need.
+ if event.is_hint:
+ x, y, state = event.window.get_pointer()
+ else:
+ x = event.x
+ y = event.y
+ state = event.state
+
+ key_found = self.__get_key_at_position(x, y)
+ if key_found != self._pressed_key:
+ self.emit('key_released', self._pressed_key[0],
+ self._pressed_key[1], self.get_label(*self._pressed_key))
+
+ self._pressed_key = key_found
+ if key_found is not None:
+ octave_clicked = key_found[0]
+ key_clicked = key_found[1]
+ self.emit('key_pressed', octave_clicked, key_clicked,
+ self.get_label(octave_clicked, key_clicked))
+ self.queue_draw()
+
+ def __get_key_at_position(self, x, y):
+ if y > self._height:
+ return None
+ octave_found = int(x / self._octave_width)
+ key_area = int((x % self._octave_width) / self._key_width)
+ click_x = int(x % self._key_width)
+ if y > self._black_keys_height or \
+ (self._add_c and x > self._width - self._key_width):
+ key_found = self._white_keys[key_area]
+ else:
+ # check black key at the right
+ key_found = -1
+ if key_area in self._l_keys_areas or \
+ key_area in self._t_keys_areas:
+ if click_x > self._key_width * 2 / 3:
+ key_found = self._white_keys[key_area] + 1
+ # check black key at the left
+ if key_found == -1 and \
+ key_area in self._j_keys_areas or \
+ key_area in self._t_keys_areas:
+ if click_x < self._key_width * 1 / 3:
+ key_found = self._white_keys[key_area] - 1
+ if key_found == -1:
+ key_found = self._white_keys[key_area]
+ return (octave_found, key_found)
+
+ def __button_release_cb(self, widget, event):
+ if self._pressed_key is not None:
+ self.emit('key_released', self._pressed_key[0],
+ self._pressed_key[1], self.get_label(*self._pressed_key))
+ self._pressed_key = None
+ self.queue_draw()
+
+ def get_label(self, octave, key):
+ if self._labels is None:
+ return ""
+ try:
+ return self._labels[octave][key]
+ except:
+ return ""
+
+ def expose(self, widget, event):
+ rect = self.get_allocation()
+ self.calculate_sizes(rect.width)
+
+ ctx = widget.window.cairo_create()
+
+ # set a clip region for the expose event
+ ctx.rectangle(event.area.x, event.area.y, event.area.width,
+ event.area.height)
+ ctx.clip()
+
+ # calculate text height
+ ctx.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
+ cairo.FONT_WEIGHT_NORMAL)
+ ctx.set_font_size(self.font_size)
+ x_bearing, y_bearing, width, height, x_advance, y_advance = \
+ ctx.text_extents('M')
+ self._text_height = height
+
+ self.draw(ctx)
+ return False
+
+ def draw(self, ctx):
+ for n in range(0, self._octaves):
+ self.draw_octave(ctx, n)
+ if self._add_c:
+ self.draw_last_C(ctx, n + 1)
+ if self._pressed_key is not None:
+ octave, key = self._pressed_key
+ if octave == -1 or (octave == self._octaves and key > 0):
+ return
+ if key == 0:
+ if octave < self._octaves:
+ self.draw_C(ctx, octave, True)
+ else:
+ self.draw_last_C(ctx, octave, True)
+ elif key == 1:
+ self.draw_CB(ctx, octave, True)
+ elif key == 2:
+ self.draw_D(ctx, octave, True)
+ elif key == 3:
+ self.draw_DB(ctx, octave, True)
+ elif key == 4:
+ self.draw_E(ctx, octave, True)
+ elif key == 5:
+ self.draw_F(ctx, octave, True)
+ elif key == 6:
+ self.draw_FB(ctx, octave, True)
+ elif key == 7:
+ self.draw_G(ctx, octave, True)
+ elif key == 8:
+ self.draw_GB(ctx, octave, True)
+ elif key == 9:
+ self.draw_A(ctx, octave, True)
+ elif key == 10:
+ self.draw_AB(ctx, octave, True)
+ elif key == 11:
+ self.draw_B(ctx, octave, True)
+
+ def draw_octave(self, ctx, octave_number):
+ self.draw_C(ctx, octave_number)
+ self.draw_CB(ctx, octave_number)
+ self.draw_D(ctx, octave_number)
+ self.draw_DB(ctx, octave_number)
+ self.draw_E(ctx, octave_number)
+ self.draw_F(ctx, octave_number)
+ self.draw_FB(ctx, octave_number)
+ self.draw_G(ctx, octave_number)
+ self.draw_GB(ctx, octave_number)
+ self.draw_A(ctx, octave_number)
+ self.draw_AB(ctx, octave_number)
+ self.draw_B(ctx, octave_number)
+
+ """
+ Draw 5 types of keys: L keys, T keys, J keys and black keys,
+ and if we add a c key is a simple key
+ +--+---+--+---+--+---+
+ | |Bl | |Bl | | S |
+ | | ak| | ak| | i |
+ | +---+ +---+ | m |
+ | | | | p |
+ | L | T | J | l |
+ +----+------+----+---+
+ """
+
+ def draw_C(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * (octave_number * 7)
+ self.draw_key_L(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 0, False, highlighted)
+
+ def draw_CB(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * (octave_number * 7) + self._key_width * 2 / 3
+ self.draw_black(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 1, True, highlighted)
+
+ def draw_D(self, ctx, octave_number, highlighted=False):
+ x = self._key_width + self._key_width * (octave_number * 7)
+ self.draw_key_T(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 2, False, highlighted)
+
+ def draw_DB(self, ctx, octave_number, highlighted=False):
+ x = self._key_width + self._key_width * 2 / 3 + \
+ self._key_width * (octave_number * 7)
+ self.draw_black(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 3, True, highlighted)
+
+ def draw_E(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 2 + self._key_width * (octave_number * 7)
+ self.draw_key_J(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 4, False, highlighted)
+
+ def draw_F(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 3 + self._key_width * (octave_number * 7)
+ self.draw_key_L(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 5, False, highlighted)
+
+ def draw_FB(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 3 + self._key_width * 2 / 3 + \
+ self._key_width * (octave_number * 7)
+ self.draw_black(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 6, True, highlighted)
+
+ def draw_G(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 4 + self._key_width * (octave_number * 7)
+ self.draw_key_T(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 7, False, highlighted)
+
+ def draw_GB(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 4 + self._key_width * 2 / 3 + \
+ self._key_width * (octave_number * 7)
+ self.draw_black(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 8, True, highlighted)
+
+ def draw_A(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 5 + self._key_width * (octave_number * 7)
+ self.draw_key_T(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 9, False, highlighted)
+
+ def draw_AB(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 5 + self._key_width * 2 / 3 + \
+ self._key_width * (octave_number * 7)
+ self.draw_black(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 10, True, highlighted)
+
+ def draw_B(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * 6 + self._key_width * (octave_number * 7)
+ self.draw_key_J(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 11, False, highlighted)
+
+ def draw_last_C(self, ctx, octave_number, highlighted=False):
+ x = self._key_width * (octave_number * 7)
+ self.draw_key_simple(ctx, x, highlighted)
+ self._draw_label(ctx, x, octave_number, 0, False, highlighted)
+
+ def draw_key_L(self, ctx, x, highlighted):
+ ctx.save()
+ ctx.move_to(x, 0)
+ stroke = (0, 0, 0)
+ fill = (1, 1, 1)
+ if highlighted:
+ fill = (1, 1, 0)
+
+ ctx.line_to(x + self._key_width * 2 / 3, 0)
+ ctx.line_to(x + self._key_width * 2 / 3, self._black_keys_height)
+ ctx.line_to(x + self._key_width, self._black_keys_height)
+ ctx.line_to(x + self._key_width, self._height)
+ ctx.line_to(x, self._height)
+ ctx.line_to(x, 0)
+ ctx.close_path()
+ self._fill_and_stroke(ctx, fill, stroke)
+ ctx.restore()
+
+ def draw_key_T(self, ctx, x, highlighted):
+ ctx.save()
+ stroke = (0, 0, 0)
+ fill = (1, 1, 1)
+ if highlighted:
+ fill = (1, 1, 0)
+ ctx.move_to(x + self._key_width * 1 / 3, 0)
+ ctx.line_to(x + self._key_width * 2 / 3, 0)
+ ctx.line_to(x + self._key_width * 2 / 3, self._black_keys_height)
+ ctx.line_to(x + self._key_width, self._black_keys_height)
+ ctx.line_to(x + self._key_width, self._height)
+ ctx.line_to(x, self._height)
+ ctx.line_to(x, self._black_keys_height)
+ ctx.line_to(x + self._key_width * 1 / 3, self._black_keys_height)
+ ctx.close_path()
+ self._fill_and_stroke(ctx, fill, stroke)
+ ctx.restore()
+
+ def draw_key_J(self, ctx, x, highlighted):
+ ctx.save()
+ stroke = (0, 0, 0)
+ fill = (1, 1, 1)
+ if highlighted:
+ fill = (1, 1, 0)
+ ctx.move_to(x + self._key_width * 1 / 3, 0)
+ ctx.line_to(x + self._key_width, 0)
+ ctx.line_to(x + self._key_width, self._height)
+ ctx.line_to(x, self._height)
+ ctx.line_to(x, self._black_keys_height)
+ ctx.line_to(x + self._key_width * 1 / 3, self._black_keys_height)
+ ctx.close_path()
+ self._fill_and_stroke(ctx, fill, stroke)
+ ctx.restore()
+
+ def draw_key_simple(self, ctx, x, highlighted):
+ ctx.save()
+ stroke = (0, 0, 0)
+ fill = (1, 1, 1)
+ if highlighted:
+ fill = (1, 1, 0)
+ ctx.move_to(x, 0)
+ ctx.line_to(x + self._key_width, 0)
+ ctx.line_to(x + self._key_width, self._height)
+ ctx.line_to(x, self._height)
+ ctx.close_path()
+ self._fill_and_stroke(ctx, fill, stroke)
+ ctx.restore()
+
+ def draw_black(self, ctx, x, highlighted):
+ ctx.save()
+ ctx.move_to(x, 0)
+ stroke = (0, 0, 0)
+ fill = (0, 0, 0)
+ if highlighted:
+ fill = (1, 1, 0)
+
+ ctx.line_to(x + self._key_width * 2 / 3, 0)
+ ctx.line_to(x + self._key_width * 2 / 3, self._black_keys_height)
+ ctx.line_to(x, self._black_keys_height)
+ ctx.line_to(x, 0)
+ ctx.close_path()
+ self._fill_and_stroke(ctx, fill, stroke)
+ ctx.restore()
+
+ def _fill_and_stroke(self, ctx, fill, stroke):
+ ctx.set_source_rgb(*fill)
+ ctx.fill_preserve()
+ ctx.set_source_rgb(*stroke)
+ ctx.stroke()
+
+ def _draw_label(self, ctx, x, octave_number, position, black_key,
+ highlighted):
+ #print "Dibujando ",text
+ if self._labels is not None:
+ text = self._labels[octave_number][position]
+ x_bearing, y_bearing, width, height, x_advance, y_advance = \
+ ctx.text_extents(text)
+ if black_key:
+ x_text = x + self._key_width * 1 / 3 - (width / 2 + x_bearing)
+ y_text = self._black_keys_height - (self._text_height * 2)
+ if highlighted:
+ stroke = (0, 0, 0)
+ else:
+ stroke = (1, 1, 1)
+ else:
+ x_text = x + self._key_width / 2 - (width / 2 + x_bearing)
+ y_text = self._height - (self._text_height * 2)
+ stroke = (0, 0, 0)
+ ctx.set_source_rgb(*stroke)
+ ctx.move_to(x_text, y_text)
+ ctx.show_text(text)
+
+
+def print_key_pressed(widget, octave_clicked, key_clicked, letter):
+ print 'Pressed Octave: %d Key: %d Letter: %s' % (octave_clicked,
+ key_clicked, letter)
+
+
+def print_key_released(widget, octave_clicked, key_clicked, letter):
+ print 'Released Octave: %d Key: %d Letter: %s' % (octave_clicked,
+ key_clicked, letter)
+
+
+def main():
+ window = gtk.Window()
+ labels_tamtam = ['Q2W3ER5T6Y7UI', 'ZSXDCVGBHNJM', ',']
+ piano = PianoKeyboard(octaves=2, add_c=True, labels=labels_tamtam)
+ piano.connect('key_pressed', print_key_pressed)
+ piano.connect('key_released', print_key_released)
+
+ window.add(piano)
+ window.connect("destroy", gtk.main_quit)
+ window.show_all()
+ gtk.main()
+
+if __name__ == "__main__":
+ main()