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