Clignotement du curseur basé sur un thread indépendant de pygame.
[minwii.git] / src / app / widgets / cursors.py
index 3ac9c43..c8df104 100755 (executable)
@@ -8,12 +8,12 @@ $URL$
 
 import pygame
 import os
 
 import pygame
 import os
+from threading import Thread
+import time
 from eventutils import EventHandlerMixin, event_handler
 from itertools import cycle
 from eventutils import EventHandlerMixin, event_handler
 from itertools import cycle
-from pygame.locals import USEREVENT
-TIMEOUT = USEREVENT + 1
 
 
-class WarpingCursor(pygame.sprite.Sprite, EventHandlerMixin):
+class WarpingCursor(pygame.sprite.DirtySprite, EventHandlerMixin):
     '''
     The class for animating the warping cursor
     '''
     '''
     The class for animating the warping cursor
     '''
@@ -21,16 +21,15 @@ class WarpingCursor(pygame.sprite.Sprite, EventHandlerMixin):
     @staticmethod
     def _get_theme_images(name) :
         basePath = os.path.abspath(__file__).split(os.path.sep)[:-1]
     @staticmethod
     def _get_theme_images(name) :
         basePath = os.path.abspath(__file__).split(os.path.sep)[:-1]
-        basePath.append('data')
+        basePath.extend(['data', 'cursor'])
         basePath.append(name)
         basePath = os.path.sep.join(basePath)
         images = [f for f in os.listdir(basePath) if os.path.splitext(f)[1] == '.png']
         return basePath, images
 
     
         basePath.append(name)
         basePath = os.path.sep.join(basePath)
         images = [f for f in os.listdir(basePath) if os.path.splitext(f)[1] == '.png']
         return basePath, images
 
     
-    def __init__(self, theme='black', duration=75, blink=True):
-        pygame.sprite.Sprite.__init__(self)
-        pygame.mouse.set_visible(False)
+    def __init__(self, theme='black', duration=50, blinkMode=True):
+        pygame.sprite.DirtySprite.__init__(self)
         imagesPath, images = WarpingCursor._get_theme_images(theme)
         flashImage = images.pop(images.index('flash.png'))
         flashImagePath = os.path.sep.join([imagesPath, flashImage]) 
         imagesPath, images = WarpingCursor._get_theme_images(theme)
         flashImage = images.pop(images.index('flash.png'))
         flashImagePath = os.path.sep.join([imagesPath, flashImage]) 
@@ -49,38 +48,83 @@ class WarpingCursor(pygame.sprite.Sprite, EventHandlerMixin):
         self.duration = duration
         
         self.image = self.images[0]
         self.duration = duration
         
         self.image = self.images[0]
-        self.rect = pygame.Rect((0,0), (self.width, self.height))
+        # workarround cursor alignement problem
+        pygame.event.set_blocked(pygame.MOUSEMOTION)
+        pygame.mouse.set_pos(pygame.mouse.get_pos())
+        pygame.event.set_allowed(pygame.MOUSEMOTION)
+        # ---
+        x, y = pygame.mouse.get_pos()
+        left = x - self.width / 2
+        top = y - self.height / 2
+        self.rect = pygame.Rect((left, top), (self.width, self.height))
         
         
-        self.blink = blink
-        if blink :
-            self._startBlink()
-            
+        self.blinkMode = blinkMode
+        self._startBlink()
+    
+    def __del__(self) :
+        if hasattr(self, 'timer') :
+            try : self.timer.stop()
+            except : pass
+    
     def _startBlink(self) :
     def _startBlink(self) :
-        pygame.time.set_timer(TIMEOUT, self.duration)
-        self.iterator = self.iterImages()
+        if self.blinkMode :
+            self._blinking = True
+            self.iterator = self.iterImages()
+            self.timer = ForeverTimer(self.duration, self.loadNext)
+            self.timer.start()
     
     
+    def _stopBlink(self) :
+        if self.blinkMode :
+            self.timer.stop()
+            
     def iterImages(self) :
         for img in cycle(self.images) :
             yield img
     
     def iterImages(self) :
         for img in cycle(self.images) :
             yield img
     
-    @event_handler(TIMEOUT)
-    def loadNext(self, event) :
-        if self.blink :
+    def loadNext(self) :
+        if self._blinking :
+            self.dirty = 1
             self.image = self.iterator.next()
     
     @event_handler(pygame.MOUSEBUTTONDOWN)
     def flashOn(self, event) :
             self.image = self.iterator.next()
     
     @event_handler(pygame.MOUSEBUTTONDOWN)
     def flashOn(self, event) :
-        self.blink=False
+        self.dirty = 1
+        self._blinking = False
         self.image = self.flashImage
 
     @event_handler(pygame.MOUSEBUTTONUP)
     def flashOff(self, event) :
         self.image = self.flashImage
 
     @event_handler(pygame.MOUSEBUTTONUP)
     def flashOff(self, event) :
-        self.blink = True
-        self.loadNext(event)
+        self.dirty = 1
+        if self.blinkMode :
+            self._blinking = True
+        else :
+            self.image = self.images[0]
     
     @event_handler(pygame.MOUSEMOTION)
     def move(self, event) :
     
     @event_handler(pygame.MOUSEMOTION)
     def move(self, event) :
-        x, y = event.rel
-        self.rect.centerx += x
-        self.rect.centery += y
-        #self.rect.move_ip(*rel)
+        self.dirty = 1
+        self.rect.move_ip(event.rel)
+    
+    def setPosition(self, pos) :
+        self.dirty = 1
+        x, y = pos
+        rx, ry = self.rect.centerx, self.rect.centery
+        self.rect.move_ip(x-rx, y-ry)
+
+
+class ForeverTimer(Thread) :
+    def __init__(self, duration, func, *args, **kw) :
+        Thread.__init__(self)
+        self.duration = duration / 1000.
+        self.func = func
+        self.args = args
+        self.kw = kw
+        self.running = True
+
+    def run(self) :
+        while self.running :
+            self.func(*self.args, **self.kw)
+            time.sleep(self.duration)
+    
+    def stop(self) :
+        self.running = False
\ No newline at end of file