pygameui.Application   F
last analyzed

Complexity

Total Complexity 103

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 275
dl 0
loc 369
rs 2
c 0
b 0
f 0
wmc 103

30 Methods

Rating   Name   Duplication   Size   Complexity  
A Application.getApp() 0 2 1
A Application.__init__() 0 34 2
B Application._processTimerEvent() 0 23 7
A Application.performFullUpdate() 0 3 2
A Application._processKeyDown() 0 8 4
A Application.hideWindow() 0 17 4
C Application.processEvent() 0 19 9
A Application.moveWindowToFront() 0 6 2
A Application.focusWindowAt() 0 10 4
A Application._processMouseButtonUp() 0 8 5
A Application.needsUpdate() 0 2 1
C Application.draw() 0 29 10
A Application.registerWindow() 0 3 1
A Application.unregisterWindow() 0 5 2
A Application.drawCursor() 0 2 1
A Application.focusWindow() 0 7 3
A Application.setFocus() 0 7 4
A Application._processMouseMotion() 0 8 3
A Application.processAction() 0 3 1
A Application._processMouseWheel() 0 13 4
A Application.exitLocal() 0 4 1
C Application._processMouseButtonDown() 0 29 10
A Application.drawOpenGL() 0 16 3
A Application.update() 0 3 2
A Application._processKeyUp() 0 7 4
A Application.setMouseOver() 0 17 4
A Application.setTempStatus() 0 6 3
A Application.redraw() 0 2 1
A Application.exit() 0 2 1
A Application.setStatus() 0 5 3

1 Function

Rating   Name   Duplication   Size   Complexity  
A noop() 0 2 1

How to fix   Complexity   

Complexity

Complex classes like pygameui.Application often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Pygame.UI.
5
#
6
#  Pygame.UI is free software; you can redistribute it and/or modify
7
#  it under the terms of the Lesser GNU General Public License as published by
8
#  the Free Software Foundation; either version 2.1 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Pygame.UI is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  Lesser GNU General Public License for more details.
15
#
16
#  You should have received a copy of the Lesser GNU General Public License
17
#  along with Pygame.UI; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
import pygame
22
import pygame, pygame.time, pygame.mouse
23
24
import SkinableTheme
25
from Tooltip import Tooltip
26
import Const
27
28
# enable only if you want OpenGL support
29
#try:
30
#    from OpenGL.GL import *
31
#    from OpenGL.GLU import *
32
#except ImportError:
33
#    pass
34
35
def noop():
36
    pass
37
38
class Application:
39
40
    def __init__(self, update = noop, theme = SkinableTheme):
41
        self.theme = theme
42
        self.theme.init()
43
        self.updateFunc = update
44
        self.showBackground = True
45
        self.background = None
46
        self.redrawWidgets = {}
47
        self.cursorPos = (0, 0)
48
        self.windowSurfaceFlags = 0
49
        # status bar widget
50
        self.statusBar = None
51
        self.statusBarText = None
52
        # internal properties
53
        self.locale = 'en'
54
        self.windows = []
55
        self.focusedWindow = None
56
        self.activeWidget = None
57
        self.mouseOverWidget = None
58
        self.mouseOverCount = 0
59
        self.mouseOverThreshold = 3
60
        self.mouseLMBDouble = 0
61
        self.mouseRMBDouble = 0
62
        self.keyEvt = None
63
        self.keyCount = 0
64
        self.tooltip = Tooltip(self)
65
        self.focusedWidget = None
66
        self.cursorOn = 0
67
        self.cursorCount = 0
68
        self._fullUpdate = False
69
        # setup timer
70
        try:
71
             pygame.time.set_timer(Const.TIMEREVENT, 80)
72
        except pygame.error:
73
            pass
74
75
    def getApp(self):
76
        return self
77
78
    def _processTimerEvent(self, evt):
79
        # tooltips
80
        self.mouseOverCount += 1
81
        if self.mouseOverCount == self.mouseOverThreshold:
82
            # show tooltip
83
            if self.mouseOverWidget:
84
                self.tooltip.title = self.mouseOverWidget.tooltipTitle
85
                self.tooltip.text = self.mouseOverWidget.tooltip
86
                self.tooltip.rect = pygame.Rect(pygame.mouse.get_pos(), (100, 100))
87
        # cursor
88
        self.cursorCount += 1
89
        if self.cursorCount == 5:
90
            self.cursorOn = not self.cursorOn
91
            self.cursorCount = 0
92
            if self.focusedWidget:
93
                self.focusedWidget.onCursorChanged()
94
        # keyboard repeat
95
        if self.keyEvt:
96
            self.keyCount += 1
97
            if self.keyCount == 6:
98
                self.processEvent(self.keyEvt)
99
                self.keyCount = 4
100
        return Const.NoEvent
101
102
    def _processMouseWheel(self, evt):
103
        assert evt.button in (4, 5)
104
        # TODO find window to deliver mouse wheel events to
105
        if self.focusedWindow:
106
            if self.focusedWindow.rect.collidepoint(evt.pos):
107
                if evt.button == 4:
108
                    return self.focusedWindow.processMWUp(evt)
109
                else:
110
                    return self.focusedWindow.processMWDown(evt)
111
            else:
112
                return Const.NoEvent
113
        else:
114
            return evt
115
116
    def _processMouseButtonDown(self, evt):
117
        # mouse wheel
118
        if evt.button in (4, 5):
119
            return self._processMouseWheel(evt)
120
        # TODO double click
121
        # check if focused window is top level one
122
        if self.focusedWindow != self.windows[-1]:
123
            window = self.focusedWindow
124
            self.focusWindowAt(evt)
125
            # consume event, when focus has been changed
126
            if self.focusedWindow != window:
127
                return Const.NoEvent
128
        # left and right mouse button
129
        if self.focusedWindow:
130
            if self.focusedWindow.rect.collidepoint(evt.pos):
131
                if evt.button == 1:
132
                    return self.focusedWindow.processMB1Down(evt)
133
                elif evt.button == 3:
134
                    return self.focusedWindow.processMB3Down(evt)
135
            elif self.focusedWindow.modal:
136
                return Const.NoEvent
137
            elif self.focusedWindow.looseFocusClose:
138
                self.focusedWindow.hide()
139
                return self.focusWindowAt(evt)
140
            else:
141
                return self.focusWindowAt(evt)
142
        else:
143
            return self.focusWindowAt(evt)
144
        return evt
145
146
    def _processMouseButtonUp(self, evt):
147
        # left and right mouse button
148
        if self.focusedWindow and self.focusedWindow.rect.collidepoint(evt.pos):
149
            if evt.button == 1:
150
                return self.focusedWindow.processMB1Up(evt)
151
            elif evt.button == 3:
152
                return self.focusedWindow.processMB3Up(evt)
153
        return evt
154
155
    def _processMouseMotion(self, evt):
156
        if self.mouseOverCount < self.mouseOverThreshold:
157
            # just moving across widget does not trigger tooltip
158
            self.mouseOverCount = 0
159
        self.cursorPos = evt.pos
160
        if self.focusedWindow:
161
            return self.focusedWindow.processMMotion(evt)
162
        return evt
163
164
    def _processKeyDown(self, evt):
165
        self.keyEvt = evt
166
        self.keyCount = 0
167
        if self.focusedWidget:
168
            evt = self.focusedWidget.processKeyDown(evt)
169
        if evt != Const.NoEvent and self.focusedWindow:
170
            evt = self.focusedWindow.processKeyDown(evt)
171
        return evt
172
173
    def _processKeyUp(self, evt):
174
        self.keyEvt = None
175
        if self.focusedWidget:
176
            evt = self.focusedWidget.processKeyUp(evt)
177
        if evt != Const.NoEvent and self.focusedWindow:
178
            evt = self.focusedWindow.processKeyUp(evt)
179
        return evt
180
181
    def processEvent(self, evt):
182
        if evt.type == pygame.VIDEOEXPOSE:
183
            self.performFullUpdate()
184
        if not pygame.key.get_focused():
185
            return Const.NoEvent
186
        elif evt.type == Const.TIMEREVENT:
187
            self._processTimerEvent(evt)
188
        elif evt.type == pygame.MOUSEBUTTONDOWN:
189
            self._processMouseButtonDown(evt)
190
        elif evt.type == pygame.MOUSEBUTTONUP:
191
            self._processMouseButtonUp(evt)
192
        elif evt.type == pygame.MOUSEMOTION:
193
            self._processMouseMotion(evt)
194
        elif evt.type == pygame.KEYDOWN:
195
            self._processKeyDown(evt)
196
        elif evt.type == pygame.KEYUP:
197
            self._processKeyUp(evt)
198
        else:
199
            return evt
200
201
    def registerWindow(self, window):
202
        self.windows.append(window)
203
        self._fullUpdate = True
204
205
    def unregisterWindow(self, window):
206
        if window == self.focusedWindow:
207
            self.focusedWindow.hide()
208
        self.windows.remove(window)
209
        self._fullUpdate = True
210
211
    def moveWindowToFront(self, window):
212
        self.focusWindow(window)
213
        if window in self.windows:
214
            self.windows.remove(window)
215
        self.windows.append(window)
216
        self.performFullUpdate()
217
218
    def focusWindow(self, window):
219
        if self.focusedWindow:
220
            self.focusedWindow.focused = 0
221
        self.focusedWindow = window
222
        self.setFocus(None)
223
        if self.focusedWindow:
224
            self.focusedWindow.focused = 1
225
226
    def hideWindow(self, window):
227
        window.visible = 0
228
        self.performFullUpdate()
229
        self._fullUpdate = True
230
        if self.focusedWindow == window:
231
            self.focusedWindow.focused = 0
232
            self.focusedWindow = None
233
            # also unfocus widget
234
            self.setFocus(None)
235
            # find new window to focus
236
            index = len(self.windows) - 1
237
            while index >= 0:
238
                window = self.windows[index]
239
                if window.visible:
240
                    window.toFront()
241
                    return
242
                index -= 1
243
244
    def focusWindowAt(self, evt):
245
        # find window which has been clicked in
246
        index = len(self.windows) - 1
247
        while index >= 0:
248
            window = self.windows[index]
249
            if window.visible and window.rect.collidepoint(evt.pos):
250
                window.toFront()
251
                return Const.NoEvent
252
            index -= 1
253
        return evt
254
255
    def setFocus(self, widget):
256
        if self.focusedWidget != widget:
257
            if self.focusedWidget:
258
                self.focusedWidget.onFocusLost()
259
            self.focusedWidget = widget
260
            if widget:
261
                widget.onFocusGained()
262
263
    def setMouseOver(self, widget):
264
        if self.mouseOverWidget != widget:
265
            if self.mouseOverWidget:
266
                self.mouseOverWidget.onMouseOut()
267
                self.tooltip.text = None
268
                self.tooltip.title = None
269
                self.performFullUpdate()
270
            self.mouseOverWidget = widget
271
            self.mouseOverCount = 0
272
            if widget:
273
                widget.onMouseOver()
274
                self.tooltip.text = None
275
                self.tooltip.title = None
276
                self.performFullUpdate()
277
                widget.parent.setTempStatus(widget.statustip)
278
                return
279
            self.setTempStatus(None)
280
281
    def setStatus(self, text):
282
        self.statusBarText = text
283
        if self.statusBar and self.statusBar.text != text:
284
            self.statusBar.text = text
285
            self.redraw(self.statusBar)
286
287
    def setTempStatus(self, text):
288
        if self.statusBar:
289
            if text:
290
                self.statusBar.text = text
291
            else:
292
                self.statusBar.text = self.statusBarText
293
294
    def draw(self, surface):
295
        """Draw all windows onto supplied surface."""
296
        if self.showBackground:
297
            surface.blit(self.background, (0, 0))
298
        changed = []
299
        #@print "App Draw"
300
        for window in self.windows:
301
            if window.visible:
302
                if self.showBackground:
303
                    window._fullUpdate = True
304
                rect = window.draw(surface)
305
                #@print " ", window, rect
306
                if rect: changed.append(rect)
307
                window.__dict__['_changeReported'] = 0
308
            else:
309
                #@print " ", window, "invisible"
310
                pass
311
        if self.tooltip.title or self.tooltip.text:
312
            title, body = self.theme.drawTooltip(surface, self.tooltip)
313
            changed.append(title)
314
            changed.append(body)
315
        self.tooltip.__dict__['_changeReported'] = 0
316
        self.redrawWidgets = {}
317
        #@print "CHANGED", changed
318
        if self._fullUpdate or self.showBackground:
319
            #@print "FULL UPDATE"
320
            self._fullUpdate = False
321
            return [pygame.display.get_surface().get_rect()]
322
        return changed
323
324
    def drawOpenGL(self):
325
        for window in self.windows:
326
            if window.visible:
327
                window.draw(None)
328
                bitmap = pygame.image.tostring(window.surface, "RGBA", 1)
329
                width, height = window.surface.get_size()
330
                scrW, scrH = pygame.display.get_surface().get_size()
331
                x, y = window.rect.bottomleft
332
                glRasterPos2i(x, scrH - y)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glRasterPos2i does not seem to be defined.
Loading history...
333
                glDrawPixels(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glDrawPixels does not seem to be defined.
Loading history...
334
                    width, height,
335
                    GL_RGBA,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable GL_RGBA does not seem to be defined.
Loading history...
336
                    GL_UNSIGNED_BYTE,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable GL_UNSIGNED_BYTE does not seem to be defined.
Loading history...
337
                    bitmap,
338
                )
339
        glFlush()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable glFlush does not seem to be defined.
Loading history...
340
341
    def performFullUpdate(self):
342
        for window in self.windows:
343
            window._fullUpdate = True
344
345
    def drawCursor(self, surface):
346
        self.theme.drawCursor(surface, self.cursorPos)
347
348
    def update(self):
349
        if self.updateFunc:
350
            self.updateFunc()
351
352
    def redraw(self, widget, redrawParent = 0):
353
        self.redrawWidgets[widget] = None
354
355
    def needsUpdate(self):
356
        return len(self.redrawWidgets) > 0
357
358
    def exitLocal(self):
359
        evt = pygame.event.Event(Const.USEREVENT)
360
        evt.action = "localExit"
361
        pygame.event.post(evt)
362
363
    def exit(self):
364
        pygame.event.post(pygame.event.Event(pygame.QUIT))
365
366
    def processAction(self, actionName, data = None, widget = None):
367
        """ There are no application wide actions supported yet."""
368
        return
369