Implémentation de la méthode de sélection de l'instrument.
[minwii.git] / src / app / widgets / instrumentselector.py
1 # -*- coding: utf-8 -*-
2 """
3 Écran de sélection de l'instrument
4
5 $Id$
6 $URL$
7 """
8 import os.path
9 import pygame
10 from eventutils import event_handler, EventDispatcher, EventHandlerMixin
11 from cursors import WarpingCursor
12 from config import FRAMERATE
13 from globals import BACKGROUND_LAYER
14 from globals import FOREGROUND_LAYER
15 from globals import CURSOR_LAYER
16 from globals import hls_to_rgba_8bits
17
18
19 class InstrumentSelector(pygame.sprite.LayeredDirty, EventHandlerMixin) :
20
21 rows = 3
22 cols = 3
23 instruments = ['accordeon', 'celesta', 'flute', 'guitare', 'orgue', 'piano', 'tuba', 'violon', 'violoncelle']
24
25 def __init__(self) :
26 super(InstrumentSelector, self).__init__()
27 #self._initRects()
28 self._initTiles()
29 self._initCursor()
30 self._inflatedTile = None
31
32 def _initTiles(self) :
33 screen = pygame.display.get_surface()
34 tileWidth = int(round(float(screen.get_width()) / self.cols))
35 tileHeight = int(round(float(screen.get_height()) / self.rows))
36
37 self.tiles = []
38 instrus = self.instruments[:]
39 for y in range(self.cols) :
40 for x in range(self.rows) :
41 upperLeftCorner = (x * tileWidth, y * tileHeight)
42 rect = pygame.Rect(upperLeftCorner, (tileWidth, tileHeight))
43 tile = InstrumentTile(instrus.pop(0), self, rect, (x,y))
44 self.add(tile, layer=BACKGROUND_LAYER)
45 self.tiles.append(tile)
46
47 def _initCursor(self) :
48 self.cursor = WarpingCursor(blinkMode=True)
49 self.add(self.cursor, layer=CURSOR_LAYER)
50
51
52 def run(self):
53 self._running = True
54 clock = pygame.time.Clock()
55 pygame.display.flip()
56 pygame.mouse.set_visible(False)
57 while self._running :
58 EventDispatcher.dispatchEvents()
59 dirty = self.draw(pygame.display.get_surface())
60 pygame.display.update(dirty)
61 clock.tick(FRAMERATE)
62
63 def stop(self) :
64 self._running = False
65 pygame.mouse.set_visible(True)
66 self.cursor._stopBlink()
67
68 @event_handler(pygame.KEYDOWN)
69 def handleKeyDown(self, event) :
70 if event.key == pygame.K_q:
71 self.stop()
72
73 @event_handler(pygame.MOUSEMOTION)
74 #@event_handler(pygame.MOUSEBUTTONDOWN)
75 def onMouseMove(self, event) :
76 for tile in reversed(self.sprites()[:-1]) :
77 if tile.rect.collidepoint(*event.pos) :
78 self.raiseTileOver(tile)
79 break
80
81 def raiseTileOver(self, tile) :
82 if not tile.inflated :
83 self.change_layer(tile, FOREGROUND_LAYER)
84 tile.inflate(tile.coords)
85
86 if self._inflatedTile :
87 self._inflatedTile.deflate()
88 self.change_layer(self._inflatedTile, BACKGROUND_LAYER)
89
90 self._inflatedTile = tile
91
92 @event_handler(pygame.MOUSEBUTTONDOWN)
93 def selectInstrument(self, event) :
94 for tile in reversed(self.sprites()[:-1]) :
95 if tile.rect.collidepoint(*event.pos) :
96 self.selectedInstrument = tile
97 self.stop()
98 break
99
100
101
102 class InstrumentTile(pygame.sprite.DirtySprite) :
103
104 @staticmethod
105 def _get_instrument_image(name) :
106 imagePath = os.path.abspath(__file__).split(os.path.sep)[:-1]
107 imagePath.extend(['data', 'instruments'])
108 name, ext = os.path.splitext(name)
109 imagePath.append('%s%s' % (name, ext or '.jpg'))
110 return os.path.sep.join(imagePath)
111
112 BORDER = 10
113 INFLATE_ZOOM = 0.4
114
115 def __init__(self, name, group, rect, coords) :
116 pygame.sprite.DirtySprite.__init__(self, group)
117 self.inflated = False
118 self.name = name
119 self.rect = rect
120 self._baseRect = rect.copy()
121 self.coords = coords
122 imagePath = InstrumentTile._get_instrument_image(name)
123 self._img = pygame.image.load(imagePath)
124 self.update()
125
126
127 def update(self) :
128 innerWidth, innerHeight = [l-self.BORDER*2 for l in self.rect.size]
129 innerSize = innerWidth, innerHeight
130
131 border = pygame.Surface(self.rect.size)
132 border.fill((0,0,0,255))
133
134 bg = pygame.Surface(innerSize)
135 bg.fill((255,255,255,255))
136 bgRect = pygame.Rect((self.BORDER, self.BORDER), innerSize)
137
138 img = self._img
139 iWidth, iHeight = img.get_size()
140 imgRatio = float(iWidth) / iHeight
141
142 # adapts dimensions
143 iw = innerWidth
144 ih = int(round(innerWidth / imgRatio))
145
146 if ih > innerHeight:
147 ih = innerHeight
148 iw = int(round(innerHeight * imgRatio))
149
150 imgPosition = ((innerWidth - iw) / 2, (innerHeight - ih) / 2)
151 imgRect = pygame.Rect(imgPosition, (iw, ih))
152 img = pygame.transform.smoothscale(img, (iw, ih))
153
154 bg.blit(img, imgRect)
155 border.blit(bg, bgRect)
156 self.image = border
157
158
159 def inflate(self, refPoint) :
160 self.inflated = True
161 keep = {}
162 for name in REF_POINTS[refPoint] :
163 keep[name] = getattr(self.rect, name)
164
165 self.rect.inflate_ip(*[l*self.INFLATE_ZOOM for l in self.rect.size])
166
167 for k, v in keep.items() :
168 setattr(self.rect, k, v)
169
170 self.update()
171 self.dirty = 1
172
173
174 def deflate(self) :
175 self.inflated = False
176 self.rect = self._baseRect.copy()
177 self.update()
178 self.dirty = 1
179
180
181
182 REF_POINTS = {
183 (0, 0) : ['top', 'left'],
184 (1, 0) : ['top'],
185 (2, 0) : ['top', 'right'],
186
187 (0, 1) : ['left'],
188 (1, 1) : [],
189 (2, 1) : ['right'],
190
191 (0, 2) : ['bottom', 'left'],
192 (1, 2) : ['bottom'],
193 (2, 2) : ['bottom', 'right']
194 }