Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/canvas_piano.py
blob: 6fdae51c4544a878745e4f218e8d61814928b2ba (plain)
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
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#------------------------------------------------------------------------------

# Copyright 2008-2009 : François Sénéquier
# Email : francois.senequier@netcourrier.com

# This file is part of 'Theorie'.
#
# 'Theorie' 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
# any later version.
#
# 'Theorie' 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 'Theorie'.  If not, see <http://www.gnu.org/licenses/>.

#------------------------------------------------------------------------------

import pygtk
import gtk

import modele
import canvas_instrument

#------------------------------------------------------------------------------

class CanvasPiano(canvas_instrument.CanvasInstrument):

	def __positionNoteClavier(self, note):
		"""
		Retourne la note non diesee la plus proche de la note
		passee en parametre (inferieure) i.e une touche blanche
		inferieure ou egale a la note. Cela sert pour calculer
		les position des touches blanches et noires.
		"""
		assert 0 <= note and note < modele.NOMBRE_NOTES_OCTAVE
		if note in [0,1]:
			pos = 0
		elif note in [2,3]:
			pos = 1
  		elif note in [4]:
			pos = 2
		elif note in [5,6]:
			pos = 3
		elif note in [7,8]:
			pos = 4
		elif note in [9,10]:
			pos = 5
		else:
			pos = 6
		return pos
	
	def __initialiserDieses(self, lar, hau):
		"""
		Definition des polygones pour chaque touche differente 
		du clavier : 3 touches blanches et une touche noire.
		"""
		assert lar >= 0 and hau >= 0
		# dimension des touches "diese"
		ltd = lar / 2
		htd = (5 * hau / 8) + 1
		assert ltd >= 0 and htd >= 0
		# position des touches "diese"
		min = (lar - ltd) / 2 + 1
		max = min + ltd - 1
		lar -= 1
		# pour les touches blanches (diese a gauche, a droite puis au centre)
		self.__xyg = [(min,0),(lar,0),(lar,hau),(0,hau),(0,htd),(min,htd),(min,0)]
		self.__xyd = [(0,0),(max,0),(max,htd),(lar,htd),(lar,hau),(0,hau),(0,0)]
		self.__xyc = [(min,0),(max,0),(max,htd),(lar,htd),(lar,hau),(0,hau),(0,htd),(min,htd),(min,0)]
		# pour les touches noires
		min = min+ltd
		max = min+ltd
		htd -= 1
		if max < min:
			min = 0
			max = 0
		self.__xyn = [(min,0),(max,0),(max,htd),(min,htd),(min,0)]
	
	def __afficherNote(self, dr, gc, cm, psx, psy, lar, note, etat):
		son = note % modele.NOMBRE_NOTES_OCTAVE
		ocv = note / modele.NOMBRE_NOTES_OCTAVE
		assert 0 <= ocv
		pos = self.__positionNoteClavier(son) + ocv * 7
		psx += pos * lar
		lis = []
		if son in [4,11]:
			# touches blanches avec touche noire a gauche (mi, si)
			lis = self.decaler_polygone(self.__xyg, (psx, psy))
			gc.set_foreground(self.get_couleur_remplissage(etat))
			dr.draw_polygon(gc, True, lis)
		elif son in [0,5]:
			# touches blanches avec touche noire a droite (do, fa)
			lis = self.decaler_polygone(self.__xyd, (psx, psy))
			gc.set_foreground(self.get_couleur_remplissage(etat))
			dr.draw_polygon(gc, True, lis)
		elif son in [2,7,9]:
			# touches blanches avec touches noires a droite et a gauche (re, sol, la)
			lis = self.decaler_polygone(self.__xyc, (psx, psy))
			gc.set_foreground(self.get_couleur_remplissage(etat))
			dr.draw_polygon(gc, True, lis)
		else:
			# touches noires (do#, re#, fa#, sol#, la#)
			lis = self.decaler_polygone(self.__xyn, (psx, psy))
			gc.set_foreground(self.get_couleur_remplissage(etat))
			dr.draw_polygon(gc, True, lis)
		gc.set_foreground(self.get_couleur_remplissage(self.COUL_NOIR))
		dr.draw_polygon(gc, False, lis)
	
	def __dessiner(self, widget, event):
		# recuperation des dimensions de la zone graphique
		rec, lar, hau, dr, gc, pl, cm = self.get_contexte_graphique(widget)
		# calcul du nombre d'octaves
		vmin, vmax = self.ens.getOctaves()
		dif = vmax-vmin+1
		nbr = max(2, dif)
		# calcul des dimensions des touches : 
		# - fonction de la zone graphique utilisable
		# - fonction du nombre de touches blanches :
		#   nombre d'octaves * 7 touches blanches.
		ltc = int(self.rpx * lar / (nbr * 7))
		htc = int(self.rpy * hau)
		if htc > 2*ltc:
			htc = 2 * ltc
		else:
			ltc = htc/2
		nbr = dif
		# calcul du decalage du clavier pour centrage
		psx = (lar - 7 * nbr * ltc) / 2
		psy = (hau - htc) / 2
		#
		self.__initialiserDieses(ltc, htc)
		liste = self.ens.getListe()
		prs = None
		if len(liste) >= 1:
			prs = modele.Son(liste[0])
		for ind in range(nbr * modele.NOMBRE_NOTES_OCTAVE):
			note = modele.Son(ind)
			if prs != None:
				note.decaler(prs.getOctave() * modele.NOMBRE_NOTES_OCTAVE)
			son = note.getNote()
			if note in liste:
				etat = prs.getConsonnance(note)
			elif son in [0,2,4,5,7,9,11]:
				etat = self.COUL_BLANC
			else:
				etat = self.COUL_NOIR
			self.__afficherNote(dr, gc, cm, psx, psy, ltc, ind, etat)
		return False

	#----------------------------------------------------------------------
	
	def __init__(self, lar = 600, hau = 200, ens = "", rpx = 0.95, rpy = 0.95):
		"""
		@param lar: largeur de la zone graphique
		@param hau: hauteur de la zone graphique
		@param ens: l'ensemble de notes a afficher
		@param rpx: partie de la largeur utilisee
		@param rpy: partie de la hauteur utilisee
		"""
		super(CanvasPiano,self).__init__(lar,hau,ens,rpx,rpy)
		self.connect("expose-event", self.__dessiner)
		self.show()