[go: up one dir, main page]

File: musicdisplayer.py

package info (click to toggle)
solfege 3.10.3-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 12,408 kB
  • ctags: 4,270
  • sloc: python: 22,161; xml: 7,536; ansic: 4,442; makefile: 685; sh: 308
file content (157 lines) | stat: -rw-r--r-- 7,225 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# GNU Solfege - free ear training software
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2006, 2007, 2008  Tom Cato Amundsen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import gtk
import gobject
import parser
import engravers
import const
from rat import Rat

class MusicDisplayer(gtk.ScrolledWindow):
    def __init__(self, callback):
        gtk.ScrolledWindow.__init__(self)
        self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
        self.m_callback = callback
        self.m_engravers = [[],]
        self.m_fontsize = 20
        self.m_clickables = []
        self.g_d = gtk.DrawingArea()
        self.g_d.show()

        self.add_with_viewport(self.g_d)
        self.g_d.connect("expose_event", self.on_expose_event)
        self.g_d.add_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.BUTTON_PRESS_MASK|gtk.gdk.POINTER_MOTION_MASK)
        self.g_d.connect("button_press_event", self.on_button_press_event)

        self.g_pixmap = None
        self.m_width = self.m_height = 0
    def clear(self, numstaff=1):
        self.m_engravers = [[],]*numstaff
        self.g_d.queue_draw()
        self.g_pixmap = None
        dim = engravers.dimentions[20]
        self.set_size_request(self.get_size_request()[0], numstaff*dim.staff_spacing+dim.first_staff_ypos)
    def display_range(self, music, fontsize, start, end):
        """Exception handling should be done by the caller."""
        #FIXME this function only handles music where start == Rat(0, 1) correctly
        if start != Rat(0, 1):
            print "warning: musicdisplayer.display_range: start != Rat(0, 1)"
        score = parser.parse_to_score_object(music)
        tv = score.m_timeposdict.keys()
        v = [t for t in tv if t < end]
        v.sort()
        self.m_fontsize = fontsize
        self.m_engravers = score._generate_engravers(v, fontsize)
        self._display()
    def display(self, music, fontsize, what_part=const.ALL):
        """Exception handling should be done by the caller."""
        score = parser.parse_to_score_object(music)
        if what_part == const.ALL:
            self.m_engravers = score.get_engravers(fontsize)
        elif what_part == const.FIRST:
            self.m_engravers = score.get_first_engravers(fontsize)

        self.m_fontsize = fontsize
        self._display()
    def _display(self):
        dim = engravers.dimentions[self.m_fontsize]
        # BeamEngraver is the only engraver without m_xpos
        self.m_width = max([e.m_xpos for e in self.m_engravers[0] if not isinstance(e, (engravers.BeamEngraver, engravers.TupletEngraver))]) + 20
        self.m_height = len(self.m_engravers)*dim.staff_spacing+dim.first_staff_ypos
        self.set_size_request(self.get_size_request()[0], self.m_height)
        self.g_d.set_size_request(self.m_width, self.m_height-4)
        self.g_pixmap = None
        self.g_d.queue_draw()
    def add_clickable_region(self, x, y, w, h, midi_int):
        self.m_clickables.append({'x':x, 'y':y, 'w':w, 'h':h,
                                  'midi_int': midi_int})
    def on_button_press_event(self, arg1, event):
        for r in self.m_clickables:
            if r['x'] < event.x < r['x'] + r['w'] \
               and r['y'] < event.y < r['y'] + r['h']:
                self.m_callback(r['midi_int'])
    def on_expose_event(self, darea, event):
        self.white_gc = self.g_d.get_style().white_gc
        self.black_gc = self.g_d.get_style().black_gc
        dim = engravers.dimentions[self.m_fontsize]
        #if self.g_pixmap is None:
        if 1==1:
            d = self.get_allocation()
            if self.m_width < d[2]:
                self.m_width = d[2]
            if self.m_height < d[3]:
                self.m_height = d[3]
            #self.g_pixmap = pixmap = gtk.create_pixmap(self.g_d,
            #                         self.m_width, self.m_height)
            darea.window.draw_rectangle(self.white_gc, True, 0, 0,
                               self.m_width, self.m_height)
            staff_centrum = dim.first_staff_ypos
            self.m_clickables = []
            for staff in self.m_engravers:
                for e in staff:
                    if isinstance(e, engravers.StemEngraver):
                        continue
                    e.engrave(darea, self.black_gc, staff_centrum)
                # stems has to be drawn after noteheads and accidentals
                for e in staff:
                    if isinstance(e, engravers.StemEngraver):
                        e.engrave(darea, self.black_gc, staff_centrum)
                for y in range(-2, 3):
                    darea.window.draw_line(self.black_gc,
                                 0, staff_centrum+dim.linespacing*y,
                                 self.m_width, staff_centrum+y*dim.linespacing)
                staff_centrum = staff_centrum + dim.staff_spacing
        return##############
        darea.draw_pixmap(self.white_gc, self.g_pixmap, 0, 0, 0, 0, self.m_width, self.m_height)

class ChordEditor(MusicDisplayer):
    def __init__(self):
        MusicDisplayer.__init__(self, None)
        self._yp = None
        self.g_d.connect("button_release_event", self.on_button_release_event)
        self.g_d.connect("event", self.on_event)
        self.m_cursor = None
    def set_cursor(self, cursor):
        self.m_cursor = cursor
    def on_button_release_event(self, arg1, event):
        dim = engravers.dimentions[self.m_fontsize]
        dist = int((event.y - dim.first_staff_ypos) / dim.linespacing * 2)
        self.emit('clicked', dist)
    def on_event(self, drawingarea, event):
        if event.type == gtk.gdk.MOTION_NOTIFY:
            dim = engravers.dimentions[self.m_fontsize]
            dist = int((event.y - dim.first_staff_ypos) / dim.linespacing * 2)
            self._yp = dist
            self.queue_draw()
    def on_expose_event(self, darea, event):
        MusicDisplayer.on_expose_event(self, darea, event)
        dim = engravers.dimentions[self.m_fontsize]
        if self.m_cursor is not None:
            staff_centrum = dim.first_staff_ypos
            if self.m_cursor == 'erase':
                return
            if self.m_cursor == 'notehead':
                eng = engravers.NoteheadEngraver(Rat(0,1), "20-tight", 0, self._yp, 2, 0, 0, 0)
            else:
                eng = engravers.AccidentalsEngraver(Rat(0,1), "20-tight", {self._yp: [int(self.m_cursor)]})
            eng.m_xpos = 50
            eng.engrave(darea, self.black_gc, staff_centrum)

gobject.signal_new('clicked', ChordEditor, gobject.SIGNAL_RUN_FIRST,
    gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,))