f8814334798b6ebc924f5a2063e04e5fc4f8196d
[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 cursors import WarpingCursor
13 from eventutils import event_handler, EventDispatcher, EventHandlerMixin
14 from math import floor
15 import types
16
17 from config import BORDER
18 from config import FIRST_HUE
19 from config import OFF_LUMINANCE
20 from config import OFF_SATURATION
21 from config import ON_TOP_LUMINANCE
22 from config import ON_BOTTOM_LUMINANCE
23 from config import ON_SATURATION
24 from config import ON_COLUMN_OVERSIZING
25
26
27 class _PlayingScreenBase(pygame.sprite.LayeredUpdates, EventHandlerMixin) :
28
29 def __init__(self, distinctNotes=[]) :
30 """
31 distinctNotes : notes disctinctes présentes dans la chanson
32 triées du plus grave au plus aigu.
33 """
34 super(_PlayingScreenBase, self).__init__()
35 self.distinctNotes = distinctNotes
36 self.keyboardLength = 0
37 self.keyboardRects = []
38 self.cursor = None
39 self._initRects()
40 self._initColumns()
41 self._running = False
42 self.draw(pygame.display.get_surface())
43 self._initCursor()
44
45
46
47 def _initRects(self) :
48 """ création des espaces réservés pour
49 afficher les colonnes.
50 """
51 ambitus = self.distinctNotes[-1].midi - self.distinctNotes[0].midi
52 if ambitus <= 12 :
53 self.keyboardLength = 8
54 else :
55 self.keyboardLength = 11
56
57 screen = pygame.display.get_surface()
58
59 # taille de la zone d'affichage utile (bordure autour)
60 dispWidth = screen.get_width() - 2 * BORDER
61 dispHeight = screen.get_height() - 2 * BORDER
62
63 columnWidth = int(round(float(dispWidth) / self.keyboardLength))
64
65 rects = []
66 for i in range(self.keyboardLength) :
67 upperLeftCorner = (i*columnWidth + BORDER, BORDER)
68 rect = pygame.Rect(upperLeftCorner, (columnWidth, dispHeight))
69 rects.append(rect)
70
71 self.keyboardRects = rects
72
73 def _initColumns(self) :
74
75 hueStep = FIRST_HUE / (self.keyboardLength - 1)
76 for i, rect in enumerate(self.keyboardRects) :
77 hue = FIRST_HUE - hueStep * i
78 c = Column(hue, rect)
79 self.add(c, layer=0)
80
81 def _initCursor(self) :
82 self.cursor = WarpingCursor(blinkMode=True)
83 self.add(self.cursor, layer=2)
84
85
86 def highlightColumn(self, index) :
87 for i, sprite in enumerate(self.sprites()) :
88 sprite.update(i==index)
89 self.draw(pygame.display.get_surface())
90
91 def run(self):
92 self._running = True
93 clock = pygame.time.Clock()
94 pygame.display.flip()
95 while self._running :
96 EventDispatcher.dispatchEvents()
97 dirty = self.draw(pygame.display.get_surface())
98 pygame.display.update(dirty)
99 clock.tick(50)
100
101 @event_handler(pygame.KEYDOWN)
102 def handleKeyDown(self, event) :
103 if event.key == pygame.K_q:
104 self._running = False
105 uni = event.unicode
106
107 if uni.isdigit() and int(uni) <=8 :
108 self.highlightColumn(int(uni))
109
110 @event_handler(pygame.MOUSEMOTION)
111 def handleMouseMotion(self, event) :
112 pass
113
114
115
116
117 class SongPlayingScreen(_PlayingScreenBase) :
118
119 def __init__(self, song) :
120 super(SongPlayingScreen, self).__init__(song.distinctNotes)
121 self.song = song
122
123 class SongPlayingScreenTest(_PlayingScreenBase) :
124 def __init__(self) :
125 class C:pass
126 o = C()
127 o.midi=1
128 super(SongPlayingScreenTest, self).__init__([o])
129
130
131 class Column(pygame.sprite.Sprite, EventHandlerMixin) :
132
133 def __init__(self, hue, rect) :
134 pygame.sprite.Sprite.__init__(self)
135 sur = pygame.surface.Surface(rect.size)
136 rgba = hls_to_rgba_8bits(hue, OFF_LUMINANCE, OFF_SATURATION)
137 sur.fill(rgba)
138 self.stateOff = sur
139 self.rectOff = rect
140
141 topRgba = hls_to_rgba_8bits(hue, ON_TOP_LUMINANCE, ON_SATURATION)
142 bottomRgba = hls_to_rgba_8bits(hue, ON_BOTTOM_LUMINANCE, ON_SATURATION)
143 onWidth = rect.width * ON_COLUMN_OVERSIZING
144 onLeft = rect.centerx - onWidth / 2
145 rectOn = pygame.Rect((onLeft, 0),
146 (onWidth, rect.height))
147 self.stateOn = gradients.vertical(rectOn.size, topRgba, bottomRgba)
148 self.rectOn = rectOn
149
150 self.image = self.stateOff
151 self.rect = rect
152
153 def update(self, state) :
154 group = self.groups()[0]
155 if state :
156 group.change_layer(self, 1)
157 self.image = self.stateOn
158 self.rect = self.rectOn
159 else :
160 group.change_layer(self, 0)
161 self.image = self.stateOff
162 self.rect = self.rectOff
163
164 @event_handler(pygame.MOUSEBUTTONDOWN)
165 def onMouseDown(self, event) :
166 if self.rect.collidepoint(*event.pos) :
167 self.update(True)
168
169 @event_handler(pygame.MOUSEBUTTONUP)
170 def onMouseUp(self, event) :
171 self.update(False)
172
173 def hls_to_rgba_8bits(h, l, s) :
174 #convert to rgb ranging from 0 to 255
175 rgba = [floor(255 * i) for i in hls_to_rgb(h, l, s) + (1,)]
176 return tuple(rgba)