X-Git-Url: https://scm.cri.ensmp.fr/git/minwii.git/blobdiff_plain/e7854ed82aa375273bfbf772493739f875817061..46f3ffd7fdde386f41020171b5733e55a8e64a85:/src/minwii/widgets/instrumentselector.py diff --git a/src/minwii/widgets/instrumentselector.py b/src/minwii/widgets/instrumentselector.py new file mode 100755 index 0000000..6861643 --- /dev/null +++ b/src/minwii/widgets/instrumentselector.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +""" +Écran de sélection de l'instrument + +$Id$ +$URL$ +""" +import os.path +import pygame +from eventutils import event_handler, EventDispatcher, EventHandlerMixin +from cursors import WarpingCursor +from config import FRAMERATE +from config import INSTRUMENTS +from globals import BACKGROUND_LAYER +from globals import FOREGROUND_LAYER +from globals import CURSOR_LAYER +from globals import hls_to_rgba_8bits + + +class InstrumentSelector(pygame.sprite.LayeredDirty, EventHandlerMixin) : + + rows = 3 + cols = 3 + instruments = INSTRUMENTS + + def __init__(self) : + super(InstrumentSelector, self).__init__() + #self._initRects() + self._initTiles() + self._initCursor() + self._inflatedTile = None + self.selectedInstrument = None + + def _initTiles(self) : + screen = pygame.display.get_surface() + tileWidth = int(round(float(screen.get_width()) / self.cols)) + tileHeight = int(round(float(screen.get_height()) / self.rows)) + + self.tiles = [] + instrus = list(self.instruments[:]) + for y in range(self.cols) : + for x in range(self.rows) : + upperLeftCorner = (x * tileWidth, y * tileHeight) + rect = pygame.Rect(upperLeftCorner, (tileWidth, tileHeight)) + # !!! s'il y avait plus de 3x3 tuiles !!!, il faudrait alors + # changer le tuple (x,y) qui concerne le point d'application de l'homotétie. + # Cf. InstrumentTile.inflate + tile = InstrumentTile(instrus.pop(0), self, rect, (x,y)) + self.add(tile, layer=BACKGROUND_LAYER) + self.tiles.append(tile) + + def _initCursor(self) : + self.cursor = WarpingCursor(blinkMode=True) + self.add(self.cursor, layer=CURSOR_LAYER) + + + def run(self): + self._running = True + clock = pygame.time.Clock() + pygame.display.flip() + pygame.mouse.set_visible(False) + while self._running : + EventDispatcher.dispatchEvents() + dirty = self.draw(pygame.display.get_surface()) + pygame.display.update(dirty) + clock.tick(FRAMERATE) + + def stop(self) : + self._running = False + pygame.mouse.set_visible(True) + self.cursor._stopBlink() + + @event_handler(pygame.KEYDOWN) + def handleKeyDown(self, event) : + if event.key == pygame.K_q: + self.stop() + + @event_handler(pygame.MOUSEMOTION) + def onMouseMove(self, event) : + for tile in reversed(self.sprites()[:-1]) : + if tile.rect.collidepoint(*event.pos) : + self.raiseTileOver(tile) + break + + def raiseTileOver(self, tile) : + if not tile.inflated : + self.change_layer(tile, FOREGROUND_LAYER) + tile.inflate(tile.coords) + + if self._inflatedTile : + self._inflatedTile.deflate() + self.change_layer(self._inflatedTile, BACKGROUND_LAYER) + + self._inflatedTile = tile + + @event_handler(pygame.MOUSEBUTTONDOWN) + def selectInstrument(self, event) : + for tile in reversed(self.sprites()[:-1]) : + if tile.rect.collidepoint(*event.pos) : + self.selectedInstrument = tile.instrumentDescription + self.stop() + break + + + +class InstrumentTile(pygame.sprite.DirtySprite) : + + @staticmethod + def _get_instrument_image(name) : + imagePath = os.path.abspath(__file__).split(os.path.sep)[:-1] + imagePath.extend(['data', 'instruments']) + name, ext = os.path.splitext(name) + imagePath.append('%s%s' % (name, ext or '.jpg')) + return os.path.sep.join(imagePath) + + BORDER = 10 + INFLATE_ZOOM = 0.4 + + def __init__(self, instrumentDescription, group, rect, coords) : + pygame.sprite.DirtySprite.__init__(self, group) + self.inflated = False + self.instrumentDescription = instrumentDescription + self.rect = rect + self._baseRect = rect.copy() + self.coords = coords + imagePath = InstrumentTile._get_instrument_image(instrumentDescription['name']) + self._img = pygame.image.load(imagePath) + self.update() + + + def update(self) : + innerWidth, innerHeight = [l-self.BORDER*2 for l in self.rect.size] + innerSize = innerWidth, innerHeight + + border = pygame.Surface(self.rect.size) + border.fill((0xdd,0xdd,0xdd,255)) + + bg = pygame.Surface(innerSize) + bg.fill((255,255,255,255)) + bgRect = pygame.Rect((self.BORDER, self.BORDER), innerSize) + + img = self._img + iWidth, iHeight = img.get_size() + imgRatio = float(iWidth) / iHeight + + # adapts dimensions + iw = innerWidth + ih = int(round(innerWidth / imgRatio)) + + if ih > innerHeight: + ih = innerHeight + iw = int(round(innerHeight * imgRatio)) + + imgPosition = ((innerWidth - iw) / 2, (innerHeight - ih) / 2) + imgRect = pygame.Rect(imgPosition, (iw, ih)) + img = pygame.transform.smoothscale(img, (iw, ih)) + + bg.blit(img, imgRect) + border.blit(bg, bgRect) + self.image = border + + + def inflate(self, refPoint) : + self.inflated = True + keep = {} + for name in REF_POINTS[refPoint] : + keep[name] = getattr(self.rect, name) + + self.rect.inflate_ip(*[l*self.INFLATE_ZOOM for l in self.rect.size]) + + for k, v in keep.items() : + setattr(self.rect, k, v) + + self.update() + self.dirty = 1 + + + def deflate(self) : + self.inflated = False + self.rect = self._baseRect.copy() + self.update() + self.dirty = 1 + + + +REF_POINTS = { + (0, 0) : ['top', 'left'], + (1, 0) : ['top'], + (2, 0) : ['top', 'right'], + + (0, 1) : ['left'], + (1, 1) : [], + (2, 1) : ['right'], + + (0, 2) : ['bottom', 'left'], + (1, 2) : ['bottom'], + (2, 2) : ['bottom', 'right'] +} \ No newline at end of file