ARGH
[matches/honours.git] / research / transmission_spectroscopy / simulator / pgu-0.18 / build / lib / pgu / gui / app.py
1 """Defines the top-level application widget"""
2
3 import pygame
4 import os
5 from pygame.locals import *
6
7 from . import pguglobals
8 from . import container
9 from .theme import Theme
10 from .const import *
11
12 class App(container.Container):
13     """The top-level widget for an application.
14     
15     Example:
16         import pygame
17         from pgu import gui
18
19         widget = gui.Button("Testing")
20
21         app = gui.App()
22         app.init(widget=widget)
23         app.run()
24
25     """
26
27     # The top-level widget in the application
28     widget = None
29     # The pygame display for rendering the GUI. Note this may be a subsurface
30     # of the full surface.
31     screen = None
32     # The region of the (full) pygame display that contains the GUI. If set,
33     # this is used when transforming the mouse position from screen 
34     # coordinates into the subsurface coordinates.
35     appArea = None
36
37     def __init__(self, theme=None, **params):
38         """Create a new application given the (optional) theme instance."""
39         self.set_global_app()
40
41         if (not theme):
42             name = os.getenv("PGU_THEME", "").strip()
43             if (name):
44                 # Use the environment variable defined theme
45                 self.theme = Theme(name)
46             else:
47                 # Default theme
48                 self.theme = Theme()
49         else:
50             # Use the user-supplied theme
51             self.theme = theme
52         
53         params['decorate'] = 'app'
54         container.Container.__init__(self,**params)
55         self._quit = False
56         self.widget = None
57         self._chsize = False
58         self._repaint = False
59         
60         self.screen = None
61         self.container = None
62
63     def set_global_app(self):
64         """Registers this app as _the_ global PGU application. You 
65         generally shouldn't need to call this function."""
66         # Keep a global reference to this application instance so that PGU
67         # components can easily find it.
68         pguglobals.app = self
69         # For backwards compatibility we keep a reference in the class 
70         # itself too.
71         App.app = self
72         
73     def resize(self):
74         if self.screen:
75             # The user has explicitly specified a screen surface
76             size = self.screen.get_size()
77
78         elif pygame.display.get_surface():
79             # Use the existing pygame display
80             self.screen = pygame.display.get_surface()
81             size = self.screen.get_size()
82
83         else:
84             # Otherwise we must allocate a new pygame display
85             if self.style.width != 0 and self.style.height != 0:
86                 # Create a new screen based on the desired app size
87                 size = (self.style.width, self.style.height)
88         
89             else:
90                 # Use the size of the top-most widget
91                 size = self.widget.rect.size = self.widget.resize()
92             # Create the display
93             self.screen = pygame.display.set_mode(size, SWSURFACE)
94
95         #use screen to set up size of this widget
96         self.style.width,self.style.height = size
97         self.rect.size = size
98         self.rect.topleft = (0, 0)
99         
100         self.widget.rect.topleft = (0, 0)
101         self.widget.rect.size = self.widget.resize(*size)
102
103         for w in self.windows:
104             w.rect.size = w.resize()
105
106         self._chsize = False
107
108     def init(self, widget=None, screen=None, area=None):
109         """Initialize the application.
110
111         Keyword arguments:
112             widget -- the top-level widget in the application
113             screen -- the pygame surface to render to
114             area -- the rectangle (within 'screen') to use for rendering
115         """
116
117         self.set_global_app()
118         
119         if (widget): 
120             # Set the top-level widget
121             self.widget = widget
122         if (screen): 
123             if (area):
124                 # Take a subsurface of the given screen
125                 self.appArea = area
126                 self.screen = screen.subsurface(area)
127             else:
128                 # Use the entire screen for the app
129                 self.screen = screen
130         
131         self.resize()   
132         
133         w = self.widget     
134         
135         self.widgets = []
136         self.widgets.append(w)
137         w.container = self
138         self.focus(w)
139         
140         pygame.key.set_repeat(500,30)
141         
142         self._repaint = True
143         self._quit = False
144         
145         self.send(INIT)
146     
147     def event(self,ev):
148         """Pass an event to the main widget. If you are managing your own
149         mainloop, this function should be called periodically when you are
150         processing pygame events.
151         """
152         self.set_global_app()
153
154         if (self.appArea and hasattr(ev, "pos")):
155             # Translate into subsurface coordinates
156             pos = (ev.pos[0]-self.appArea.x,
157                    ev.pos[1]-self.appArea.y)
158             args = {"pos" : pos}
159             # Copy over other misc mouse parameters
160             for name in ("buttons", "rel", "button"):
161                 if (hasattr(ev, name)):
162                     args[name] = getattr(ev, name)
163             
164             ev = pygame.event.Event(ev.type, args)
165
166         #NOTE: might want to deal with ACTIVEEVENT in the future.
167         self.send(ev.type, ev)
168         container.Container.event(self, ev)
169         if ev.type == MOUSEBUTTONUP:
170             if ev.button not in (4,5): # Ignores the mouse wheel
171                 # Also issue a "CLICK" event
172                 sub = pygame.event.Event(CLICK,{
173                     'button' : ev.button,
174                     'pos' : ev.pos})
175                 self.send(sub.type,sub)
176                 container.Container.event(self,sub)
177     
178     def loop(self):
179         """Performs one iteration of the PGU application loop, which
180         processes events and update the pygame display."""
181         self.set_global_app()
182
183         for e in pygame.event.get():
184             if not (e.type == QUIT and self.mywindow):
185                 self.event(e)
186         rects = self.update(self.screen)
187         pygame.display.update(rects)
188         
189         
190     def paint(self,screen=None):
191         """Renders the application onto the given pygame surface"""
192         if (screen):
193             self.screen = screen
194
195         if self._chsize:
196             self._chsize = False
197             self.resize()
198
199         if self.background:
200             self.background.paint(self.screen)
201
202         container.Container.paint(self, self.screen)
203
204     def update(self,screen=None):
205         """Update the screen in a semi-efficient manner, and returns
206         a list of pygame rects to be updated."""
207         if (screen):
208             self.screen = screen
209
210         if self._chsize:
211             self.resize()
212             self._chsize = False
213             return None
214
215         if self._repaint:
216             self.paint(self.screen)
217             self._repaint = False
218             rects = [pygame.Rect(0, 0,
219                                  self.screen.get_width(),
220                                  self.screen.get_height())]
221         else:
222             rects = container.Container.update(self,self.screen)
223
224         if (self.appArea):
225             # Translate the rects from subsurface coordinates into
226             # full display coordinates.
227             for r in rects:
228                 r.move_ip(self.appArea.topleft)
229
230         return rects
231     
232     def run(self, widget=None, screen=None, delay=10): 
233         """Run an application.
234         
235         Automatically calls App.init and then forever loops while
236         calling App.event and App.update
237
238         Keyword arguments:
239             widget -- the top-level widget to use
240             screen -- the pygame surface to render to
241             delay -- the delay between updates (in milliseconds)
242         """
243         self.init(widget,screen)
244         while not self._quit:
245             self.loop()
246             pygame.time.wait(delay)
247
248     def reupdate(self,w=None): 
249         pass
250
251     def repaint(self,w=None):
252         self._repaint = True
253
254     def repaintall(self): 
255         self._repaint = True
256
257     def chsize(self):
258         if (not self._chsize):
259             self._chsize = True
260             self._repaint = True
261     
262     def quit(self,value=None): 
263         self._quit = True
264
265     def open(self, w, pos=None):
266         """Opens the given PGU window and positions it on the screen"""
267         w.container = self
268         
269         if (w.rect.w == 0 or w.rect.h == 0):
270             w.rect.size = w.resize()
271         
272         if (not pos): 
273             # Auto-center the window
274             w.rect.center = self.rect.center
275         else: 
276             # Show the window in a particular location
277             w.rect.topleft = pos
278         
279         self.windows.append(w)
280         self.mywindow = w
281         self.focus(w)
282         self.repaint(w)
283         w.send(OPEN)
284
285     def close(self, w):
286         """Closes the previously opened PGU window"""
287         if self.myfocus is w: self.blur(w)
288
289         if w not in self.windows: return #no need to remove it twice! happens.
290         
291         self.windows.remove(w)
292         
293         self.mywindow = None
294         if self.windows:
295             self.mywindow = self.windows[-1]
296             self.focus(self.mywindow)
297         
298         if not self.mywindow:
299             self.myfocus = self.widget #HACK: should be done fancier, i think..
300             if not self.myhover:
301                 self.enter(self.widget)
302          
303         self.repaintall()
304         w.send(CLOSE)
305
306
307 class Desktop(App):
308     """Create an App using the desktop theme class."""
309     def __init__(self,**params):
310         params.setdefault('cls','desktop')
311         App.__init__(self,**params)
312

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