File: app.py

package info (click to toggle)
python-box2d 2.3.2~dfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 3,596 kB
  • ctags: 5,116
  • sloc: python: 14,384; cpp: 13,393; makefile: 9
file content (303 lines) | stat: -rw-r--r-- 9,332 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
"""Defines the top-level application widget"""

import pygame
from pygame.locals import *

from . import pguglobals
from . import container
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 theme == None: 
            from .theme import Theme
            theme = 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)