1 """Defines the top-level application widget"""
5 from pygame.locals import *
7 from . import pguglobals
8 from . import container
9 from .theme import Theme
12 class App(container.Container):
13 """The top-level widget for an application.
19 widget = gui.Button("Testing")
22 app.init(widget=widget)
27 # The top-level widget in the application
29 # The pygame display for rendering the GUI. Note this may be a subsurface
30 # of the full surface.
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.
37 def __init__(self, theme=None, **params):
38 """Create a new application given the (optional) theme instance."""
42 name = os.getenv("PGU_THEME", "").strip()
44 # Use the environment variable defined theme
45 self.theme = Theme(name)
50 # Use the user-supplied theme
53 params['decorate'] = 'app'
54 container.Container.__init__(self,**params)
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.
69 # For backwards compatibility we keep a reference in the class
75 # The user has explicitly specified a screen surface
76 size = self.screen.get_size()
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()
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)
90 # Use the size of the top-most widget
91 size = self.widget.rect.size = self.widget.resize()
93 self.screen = pygame.display.set_mode(size, SWSURFACE)
95 #use screen to set up size of this widget
96 self.style.width,self.style.height = size
98 self.rect.topleft = (0, 0)
100 self.widget.rect.topleft = (0, 0)
101 self.widget.rect.size = self.widget.resize(*size)
103 for w in self.windows:
104 w.rect.size = w.resize()
108 def init(self, widget=None, screen=None, area=None):
109 """Initialize the application.
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
117 self.set_global_app()
120 # Set the top-level widget
124 # Take a subsurface of the given screen
126 self.screen = screen.subsurface(area)
128 # Use the entire screen for the app
136 self.widgets.append(w)
140 pygame.key.set_repeat(500,30)
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.
152 self.set_global_app()
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)
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)
164 ev = pygame.event.Event(ev.type, args)
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,
175 self.send(sub.type,sub)
176 container.Container.event(self,sub)
179 """Performs one iteration of the PGU application loop, which
180 processes events and update the pygame display."""
181 self.set_global_app()
183 for e in pygame.event.get():
184 if not (e.type == QUIT and self.mywindow):
186 rects = self.update(self.screen)
187 pygame.display.update(rects)
190 def paint(self,screen=None):
191 """Renders the application onto the given pygame surface"""
200 self.background.paint(self.screen)
202 container.Container.paint(self, self.screen)
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."""
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())]
222 rects = container.Container.update(self,self.screen)
225 # Translate the rects from subsurface coordinates into
226 # full display coordinates.
228 r.move_ip(self.appArea.topleft)
232 def run(self, widget=None, screen=None, delay=10):
233 """Run an application.
235 Automatically calls App.init and then forever loops while
236 calling App.event and App.update
239 widget -- the top-level widget to use
240 screen -- the pygame surface to render to
241 delay -- the delay between updates (in milliseconds)
243 self.init(widget,screen)
244 while not self._quit:
246 pygame.time.wait(delay)
248 def reupdate(self,w=None):
251 def repaint(self,w=None):
254 def repaintall(self):
258 if (not self._chsize):
262 def quit(self,value=None):
265 def open(self, w, pos=None):
266 """Opens the given PGU window and positions it on the screen"""
269 if (w.rect.w == 0 or w.rect.h == 0):
270 w.rect.size = w.resize()
273 # Auto-center the window
274 w.rect.center = self.rect.center
276 # Show the window in a particular location
279 self.windows.append(w)
286 """Closes the previously opened PGU window"""
287 if self.myfocus is w: self.blur(w)
289 if w not in self.windows: return #no need to remove it twice! happens.
291 self.windows.remove(w)
295 self.mywindow = self.windows[-1]
296 self.focus(self.mywindow)
298 if not self.mywindow:
299 self.myfocus = self.widget #HACK: should be done fancier, i think..
301 self.enter(self.widget)
308 """Create an App using the desktop theme class."""
309 def __init__(self,**params):
310 params.setdefault('cls','desktop')
311 App.__init__(self,**params)