055ed71c642ebca680ca4ecd894ae412032a6aba
[minwii.git] / src / app / widgets / playingscreen.py
1 # -*- coding: utf-8 -*-
2 """
3 Écran de jeu MinWii :
4 bandes arc-en-ciel représentant un clavier.
5
6 $Id$
7 $URL$
8 """
9 import pygame
10 from colorsys import hls_to_rgb
11 from gradients import gradients
12 from math import floor
13 import types
14 # TODO : positionner cette constance en fonction de la résolution d'affichage
15 # externaliser la conf.
16 BORDER = 5 # 5px
17 FIRST_HUE = 0.6
18 OFF_LUMINANCE = 0.2
19 OFF_SATURATION = 1
20 ON_TOP_LUMINANCE = 0.6
21 ON_BOTTOM_LUMINANCE = 0.9
22 ON_SATURATION = 1
23 ON_COLUMN_OVERSIZING = 1.5
24
25
26 def event_handler(eventType) :
27 def doRename(m) :
28 m.__name__ = 'eventHandler%s' % eventType
29 return m
30 return doRename
31
32
33 class MetaRenamer(type):
34 def __new__(mcs, name, bases, dict) :
35 for k, v in dict.items() :
36 if isinstance(v, types.FunctionType) :
37 if k != v.__name__ :
38 print 'renommage de %s en %s' % (k, v.__name__)
39 dict[v.__name__] = v
40 del dict[k]
41 return type.__new__(mcs, name, bases, dict)
42
43
44 class _PlayingScreenBase(pygame.sprite.OrderedUpdates) :
45
46 __metaclass__ = MetaRenamer
47
48 def __init__(self, distinctNotes=[]) :
49 """
50 distinctNotes : notes disctinctes présentes dans la chanson
51 triées du plus grave au plus aigu.
52 """
53 super(_PlayingScreenBase, self).__init__()
54 self.distinctNotes = distinctNotes
55 self.keyboardLength = 0
56 self.keyboardRects = []
57 self._initRects()
58 self._initColumns()
59 self._running = False
60
61
62
63 def _initRects(self) :
64 """ création des espaces réservés pour
65 afficher les colonnes.
66 """
67 ambitus = self.distinctNotes[-1].midi - self.distinctNotes[0].midi
68 if ambitus <= 12 :
69 self.keyboardLength = 8
70 else :
71 self.keyboardLength = 11
72
73 screen = pygame.display.get_surface()
74
75 # taille de la zone d'affichage utile (bordure autour)
76 dispWidth = screen.get_width() - 2 * BORDER
77 dispHeight = screen.get_height() - 2 * BORDER
78
79 columnWidth = int(round(float(dispWidth) / self.keyboardLength))
80
81 rects = []
82 for i in range(self.keyboardLength) :
83 upperLeftCorner = (i*columnWidth + BORDER, BORDER)
84 rect = pygame.Rect(upperLeftCorner, (columnWidth, dispHeight))
85 rects.append(rect)
86
87 self.keyboardRects = rects
88
89 def _initColumns(self) :
90
91 hueStep = FIRST_HUE / (self.keyboardLength - 1)
92 for i, rect in enumerate(self.keyboardRects) :
93 hue = FIRST_HUE - hueStep * i
94 print hue
95 c = Column(hue, rect)
96 self.add(c)
97
98
99 def highlightColumn(self, index) :
100 for i, sprite in enumerate(self.sprites()) :
101 sprite.update(i==index)
102 self.draw(pygame.display.get_surface())
103
104 def run(self):
105 self._running = True
106 while self._running :
107 pygame.display.flip()
108 events = pygame.event.get()
109 for event in events:
110 self.input(event)
111
112 def input(self, event) :
113 handler = getattr(self, 'eventHandler%s' % event.type, lambda e:None)
114 handler(event)
115
116 @event_handler(pygame.KEYDOWN)
117 def handleKeyDown(self, event) :
118 if event.key == pygame.K_q:
119 self._running = False
120 uni = event.unicode
121
122 if uni.isdigit() and int(uni) <=8 :
123 self.highlightColumn(int(uni))
124
125 @event_handler(pygame.MOUSEMOTION)
126 def handleMouseMotion(self, event) :
127
128
129
130
131 class SongPlayingScreen(_PlayingScreenBase) :
132
133 def __init__(self, song) :
134 super(SongPlayingScreen, self).__init__(song.distinctNotes)
135 self.song = song
136
137 class SongPlayingScreenTest(_PlayingScreenBase) :
138 def __init__(self) :
139 class C:pass
140 o = C()
141 o.midi=1
142 super(SongPlayingScreenTest, self).__init__([o])
143
144
145 class Column(pygame.sprite.Sprite) :
146
147 def __init__(self, hue, rect) :
148 pygame.sprite.Sprite.__init__(self)
149 sur = pygame.surface.Surface(rect.size)
150 rgba = hls_to_rgba_8bits(hue, OFF_LUMINANCE, OFF_SATURATION)
151 sur.fill(rgba)
152 self.stateOff = sur
153 self.rectOff = rect
154
155 topRgba = hls_to_rgba_8bits(hue, ON_TOP_LUMINANCE, ON_SATURATION)
156 bottomRgba = hls_to_rgba_8bits(hue, ON_BOTTOM_LUMINANCE, ON_SATURATION)
157 rectOn = rect.inflate(ON_COLUMN_OVERSIZING * rect.width, 0)
158 self.stateOn = gradients.vertical(rectOn.size, topRgba, bottomRgba)
159 self.rectOn = rectOn
160
161 self.image = self.stateOff
162 self.rect = rect
163
164 def update(self, state) :
165 if state :
166 self.image = self.stateOn
167 self.rect = self.rectOn
168 else :
169 self.image = self.stateOff
170 self.rect = self.rectOff
171
172 def hls_to_rgba_8bits(h, l, s) :
173 #convert to rgb ranging from 0 to 255
174 rgba = [floor(255 * i) for i in hls_to_rgb(h, l, s) + (1,)]
175 return tuple(rgba)