Commit before breaking everything
[matches/honours.git] / research / transmission_spectroscopy / simulator / pgu-0.18 / build / lib / pgu / gui / app.py
diff --git a/research/transmission_spectroscopy/simulator/pgu-0.18/build/lib/pgu/gui/app.py b/research/transmission_spectroscopy/simulator/pgu-0.18/build/lib/pgu/gui/app.py
new file mode 100644 (file)
index 0000000..dd1f6c0
--- /dev/null
@@ -0,0 +1,312 @@
+"""Defines the top-level application widget"""
+
+import pygame
+import os
+from pygame.locals import *
+
+from . import pguglobals
+from . import container
+from .theme import Theme
+from .const import *
+
+class App(container.Container):
+    """The top-level widget for an application.
+    
+    Example:
+        import pygame
+        from pgu import gui
+
+        widget = gui.Button("Testing")
+
+        app = gui.App()
+        app.init(widget=widget)
+        app.run()
+
+    """
+
+    # The top-level widget in the application
+    widget = None
+    # The pygame display for rendering the GUI. Note this may be a subsurface
+    # of the full surface.
+    screen = None
+    # The region of the (full) pygame display that contains the GUI. If set,
+    # this is used when transforming the mouse position from screen 
+    # coordinates into the subsurface coordinates.
+    appArea = None
+
+    def __init__(self, theme=None, **params):
+        """Create a new application given the (optional) theme instance."""
+        self.set_global_app()
+
+        if (not theme):
+            name = os.getenv("PGU_THEME", "").strip()
+            if (name):
+                # Use the environment variable defined theme
+                self.theme = Theme(name)
+            else:
+                # Default theme
+                self.theme = Theme()
+        else:
+            # Use the user-supplied theme
+            self.theme = theme
+        
+        params['decorate'] = 'app'
+        container.Container.__init__(self,**params)
+        self._quit = False
+        self.widget = None
+        self._chsize = False
+        self._repaint = False
+        
+        self.screen = None
+        self.container = None
+
+    def set_global_app(self):
+        """Registers this app as _the_ global PGU application. You 
+        generally shouldn't need to call this function."""
+        # Keep a global reference to this application instance so that PGU
+        # components can easily find it.
+        pguglobals.app = self
+        # For backwards compatibility we keep a reference in the class 
+        # itself too.
+        App.app = self
+        
+    def resize(self):
+        if self.screen:
+            # The user has explicitly specified a screen surface
+            size = self.screen.get_size()
+
+        elif pygame.display.get_surface():
+            # Use the existing pygame display
+            self.screen = pygame.display.get_surface()
+            size = self.screen.get_size()
+
+        else:
+            # Otherwise we must allocate a new pygame display
+            if self.style.width != 0 and self.style.height != 0:
+                # Create a new screen based on the desired app size
+                size = (self.style.width, self.style.height)
+        
+            else:
+                # Use the size of the top-most widget
+                size = self.widget.rect.size = self.widget.resize()
+            # Create the display
+            self.screen = pygame.display.set_mode(size, SWSURFACE)
+
+        #use screen to set up size of this widget
+        self.style.width,self.style.height = size
+        self.rect.size = size
+        self.rect.topleft = (0, 0)
+        
+        self.widget.rect.topleft = (0, 0)
+        self.widget.rect.size = self.widget.resize(*size)
+
+        for w in self.windows:
+            w.rect.size = w.resize()
+
+        self._chsize = False
+
+    def init(self, widget=None, screen=None, area=None):
+        """Initialize the application.
+
+        Keyword arguments:
+            widget -- the top-level widget in the application
+            screen -- the pygame surface to render to
+            area -- the rectangle (within 'screen') to use for rendering
+        """
+
+        self.set_global_app()
+        
+        if (widget): 
+            # Set the top-level widget
+            self.widget = widget
+        if (screen): 
+            if (area):
+                # Take a subsurface of the given screen
+                self.appArea = area
+                self.screen = screen.subsurface(area)
+            else:
+                # Use the entire screen for the app
+                self.screen = screen
+        
+        self.resize()   
+        
+        w = self.widget     
+        
+        self.widgets = []
+        self.widgets.append(w)
+        w.container = self
+        self.focus(w)
+        
+        pygame.key.set_repeat(500,30)
+        
+        self._repaint = True
+        self._quit = False
+        
+        self.send(INIT)
+    
+    def event(self,ev):
+        """Pass an event to the main widget. If you are managing your own
+        mainloop, this function should be called periodically when you are
+        processing pygame events.
+        """
+        self.set_global_app()
+
+        if (self.appArea and hasattr(ev, "pos")):
+            # Translate into subsurface coordinates
+            pos = (ev.pos[0]-self.appArea.x,
+                   ev.pos[1]-self.appArea.y)
+            args = {"pos" : pos}
+            # Copy over other misc mouse parameters
+            for name in ("buttons", "rel", "button"):
+                if (hasattr(ev, name)):
+                    args[name] = getattr(ev, name)
+            
+            ev = pygame.event.Event(ev.type, args)
+
+        #NOTE: might want to deal with ACTIVEEVENT in the future.
+        self.send(ev.type, ev)
+        container.Container.event(self, ev)
+        if ev.type == MOUSEBUTTONUP:
+            if ev.button not in (4,5): # Ignores the mouse wheel
+                # Also issue a "CLICK" event
+                sub = pygame.event.Event(CLICK,{
+                    'button' : ev.button,
+                    'pos' : ev.pos})
+                self.send(sub.type,sub)
+                container.Container.event(self,sub)
+    
+    def loop(self):
+        """Performs one iteration of the PGU application loop, which
+        processes events and update the pygame display."""
+        self.set_global_app()
+
+        for e in pygame.event.get():
+            if not (e.type == QUIT and self.mywindow):
+                self.event(e)
+        rects = self.update(self.screen)
+        pygame.display.update(rects)
+        
+        
+    def paint(self,screen=None):
+        """Renders the application onto the given pygame surface"""
+        if (screen):
+            self.screen = screen
+
+        if self._chsize:
+            self._chsize = False
+            self.resize()
+
+        if self.background:
+            self.background.paint(self.screen)
+
+        container.Container.paint(self, self.screen)
+
+    def update(self,screen=None):
+        """Update the screen in a semi-efficient manner, and returns
+        a list of pygame rects to be updated."""
+        if (screen):
+            self.screen = screen
+
+        if self._chsize:
+            self.resize()
+            self._chsize = False
+            return None
+
+        if self._repaint:
+            self.paint(self.screen)
+            self._repaint = False
+            rects = [pygame.Rect(0, 0,
+                                 self.screen.get_width(),
+                                 self.screen.get_height())]
+        else:
+            rects = container.Container.update(self,self.screen)
+
+        if (self.appArea):
+            # Translate the rects from subsurface coordinates into
+            # full display coordinates.
+            for r in rects:
+                r.move_ip(self.appArea.topleft)
+
+        return rects
+    
+    def run(self, widget=None, screen=None, delay=10): 
+        """Run an application.
+        
+        Automatically calls App.init and then forever loops while
+        calling App.event and App.update
+
+        Keyword arguments:
+            widget -- the top-level widget to use
+            screen -- the pygame surface to render to
+            delay -- the delay between updates (in milliseconds)
+        """
+        self.init(widget,screen)
+        while not self._quit:
+            self.loop()
+            pygame.time.wait(delay)
+
+    def reupdate(self,w=None): 
+        pass
+
+    def repaint(self,w=None):
+        self._repaint = True
+
+    def repaintall(self): 
+        self._repaint = True
+
+    def chsize(self):
+        if (not self._chsize):
+            self._chsize = True
+            self._repaint = True
+    
+    def quit(self,value=None): 
+        self._quit = True
+
+    def open(self, w, pos=None):
+        """Opens the given PGU window and positions it on the screen"""
+        w.container = self
+        
+        if (w.rect.w == 0 or w.rect.h == 0):
+            w.rect.size = w.resize()
+        
+        if (not pos): 
+            # Auto-center the window
+            w.rect.center = self.rect.center
+        else: 
+            # Show the window in a particular location
+            w.rect.topleft = pos
+        
+        self.windows.append(w)
+        self.mywindow = w
+        self.focus(w)
+        self.repaint(w)
+        w.send(OPEN)
+
+    def close(self, w):
+        """Closes the previously opened PGU window"""
+        if self.myfocus is w: self.blur(w)
+
+        if w not in self.windows: return #no need to remove it twice! happens.
+        
+        self.windows.remove(w)
+        
+        self.mywindow = None
+        if self.windows:
+            self.mywindow = self.windows[-1]
+            self.focus(self.mywindow)
+        
+        if not self.mywindow:
+            self.myfocus = self.widget #HACK: should be done fancier, i think..
+            if not self.myhover:
+                self.enter(self.widget)
+         
+        self.repaintall()
+        w.send(CLOSE)
+
+
+class Desktop(App):
+    """Create an App using the desktop theme class."""
+    def __init__(self,**params):
+        params.setdefault('cls','desktop')
+        App.__init__(self,**params)
+

UCC git Repository :: git.ucc.asn.au