~ubuntu-dev/wxwidgets2.6/upstream-debian

« back to all changes in this revision

Viewing changes to wxPython/wx/lib/throbber.py

  • Committer: Daniel T Chen
  • Date: 2006-06-26 10:15:11 UTC
  • Revision ID: crimsun@ubuntu.com-20060626101511-a4436cec4c6d9b35
ImportĀ DebianĀ 2.6.3.2.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
A throbber displays an animated image that can be
 
3
started, stopped, reversed, etc.  Useful for showing
 
4
an ongoing process (like most web browsers use) or
 
5
simply for adding eye-candy to an application.
 
6
 
 
7
Throbbers utilize a wxTimer so that normal processing
 
8
can continue unencumbered.
 
9
"""
 
10
 
 
11
#
 
12
# throbber.py - Cliff Wells <clifford.wells@comcast.net>
 
13
#
 
14
# Thanks to Harald Massa <harald.massa@suedvers.de> for
 
15
# suggestions and sample code.
 
16
#
 
17
# $Id: throbber.py,v 1.8.2.1 2005/12/19 23:13:52 RD Exp $
 
18
#
 
19
# 12/12/2003 - Jeff Grimmett (grimmtooth@softhome.net)
 
20
#
 
21
# o 2.5 compatability update.
 
22
#
 
23
 
 
24
 
 
25
import os
 
26
import wx
 
27
 
 
28
# ------------------------------------------------------------------------------
 
29
 
 
30
THROBBER_EVENT = wx.NewEventType()
 
31
EVT_UPDATE_THROBBER = wx.PyEventBinder(THROBBER_EVENT, 0)
 
32
 
 
33
class UpdateThrobberEvent(wx.PyEvent):
 
34
    def __init__(self):
 
35
        wx.PyEvent.__init__(self)
 
36
        self.SetEventType(THROBBER_EVENT)
 
37
 
 
38
# ------------------------------------------------------------------------------
 
39
 
 
40
class Throbber(wx.PyPanel):
 
41
    """
 
42
    The first argument is either the name of a file that will be split into frames
 
43
    (a composite image) or a list of  strings of image names that will be treated
 
44
    as individual frames.  If a single (composite) image is given, then additional
 
45
    information must be provided: the number of frames in the image and the width
 
46
    of each frame.  The first frame is treated as the "at rest" frame (it is not
 
47
    shown during animation, but only when Throbber.Rest() is called.
 
48
    A second, single image may be optionally specified to overlay on top of the
 
49
    animation. A label may also be specified to show on top of the animation.
 
50
    """
 
51
    def __init__(self, parent, id,
 
52
                 bitmap,          # single (composite) bitmap or list of bitmaps
 
53
                 pos = wx.DefaultPosition,
 
54
                 size = wx.DefaultSize,
 
55
                 frameDelay = 0.1,# time between frames
 
56
                 frames = 0,      # number of frames (only necessary for composite image)
 
57
                 frameWidth = 0,  # width of each frame (only necessary for composite image)
 
58
                 label = None,    # optional text to be displayed
 
59
                 overlay = None,  # optional image to overlay on animation
 
60
                 reverse = 0,     # reverse direction at end of animation
 
61
                 style = 0,       # window style
 
62
                 name = "throbber",
 
63
                 rest = 0,
 
64
                 current = 0,
 
65
                 direction = 1,
 
66
                 sequence = None
 
67
                 ):
 
68
        wx.PyPanel.__init__(self, parent, id, pos, size, style, name)
 
69
        self.name = name
 
70
        self.label = label
 
71
        self.running = (1 != 1)
 
72
        _seqTypes = (type([]), type(()))
 
73
 
 
74
        # set size, guessing if necessary
 
75
        width, height = size
 
76
        if width == -1:
 
77
            if type(bitmap) in _seqTypes:
 
78
                width = bitmap[0].GetWidth()
 
79
            else:
 
80
                if frameWidth:
 
81
                    width = frameWidth
 
82
        if height == -1:
 
83
            if type(bitmap) in _seqTypes:
 
84
                height = bitmap[0].GetHeight()
 
85
            else:
 
86
                height = bitmap.GetHeight()
 
87
        self.width, self.height = width, height
 
88
 
 
89
        # double check it
 
90
        assert width != -1 and height != -1, "Unable to guess size"
 
91
 
 
92
        if label:
 
93
            extentX, extentY = self.GetTextExtent(label)
 
94
            self.labelX = (width - extentX)/2
 
95
            self.labelY = (height - extentY)/2
 
96
        self.frameDelay = frameDelay
 
97
        self.rest = rest
 
98
        self.current = current
 
99
        self.direction = direction
 
100
        self.autoReverse = reverse
 
101
        self.overlay = overlay
 
102
        if overlay is not None:
 
103
            self.overlay = overlay
 
104
            self.overlayX = (width - self.overlay.GetWidth()) / 2
 
105
            self.overlayY = (height - self.overlay.GetHeight()) / 2
 
106
        self.showOverlay = overlay is not None
 
107
        self.showLabel = label is not None
 
108
 
 
109
        # do we have a sequence of images?
 
110
        if type(bitmap) in _seqTypes:
 
111
            self.submaps = bitmap
 
112
            self.frames = len(self.submaps)
 
113
        # or a composite image that needs to be split?
 
114
        else:
 
115
            self.frames = frames
 
116
            self.submaps = []
 
117
            for chunk in range(frames):
 
118
                rect = (chunk * frameWidth, 0, width, height)
 
119
                self.submaps.append(bitmap.GetSubBitmap(rect))
 
120
 
 
121
        # self.sequence can be changed, but it's not recommended doing it
 
122
        # while the throbber is running.  self.sequence[0] should always
 
123
        # refer to whatever frame is to be shown when 'resting' and be sure
 
124
        # that no item in self.sequence >= self.frames or < 0!!!
 
125
        self.SetSequence(sequence)
 
126
 
 
127
        self.SetClientSize((width, height))
 
128
 
 
129
        timerID  = wx.NewId()
 
130
        self.timer = wx.Timer(self, timerID)
 
131
 
 
132
        self.Bind(EVT_UPDATE_THROBBER, self.Update)
 
133
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 
134
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
 
135
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroyWindow)
 
136
 
 
137
 
 
138
    def DoGetBestSize(self):
 
139
        return (self.width, self.height)
 
140
    
 
141
 
 
142
    def OnTimer(self, event):
 
143
        wx.PostEvent(self, UpdateThrobberEvent())
 
144
 
 
145
 
 
146
    def OnDestroyWindow(self, event):
 
147
        self.Stop()
 
148
        event.Skip()
 
149
 
 
150
 
 
151
    def Draw(self, dc):
 
152
        dc.DrawBitmap(self.submaps[self.sequence[self.current]], 0, 0, True)
 
153
        if self.overlay and self.showOverlay:
 
154
            dc.DrawBitmap(self.overlay, self.overlayX, self.overlayY, True)
 
155
        if self.label and self.showLabel:
 
156
            dc.DrawText(self.label, self.labelX, self.labelY)
 
157
            dc.SetTextForeground(wx.WHITE)
 
158
            dc.DrawText(self.label, self.labelX-1, self.labelY-1)
 
159
 
 
160
 
 
161
    def OnPaint(self, event):
 
162
        self.Draw(wx.PaintDC(self))
 
163
        event.Skip()
 
164
 
 
165
 
 
166
    def Update(self, event):
 
167
        self.Next()
 
168
 
 
169
 
 
170
    def Wrap(self):
 
171
        if self.current >= len(self.sequence):
 
172
            if self.autoReverse:
 
173
                self.Reverse()
 
174
                self.current = len(self.sequence) - 1
 
175
            else:
 
176
                self.current = 0
 
177
        if self.current < 0:
 
178
            if self.autoReverse:
 
179
                self.Reverse()
 
180
                self.current = 0
 
181
            else:
 
182
                self.current = len(self.sequence) - 1
 
183
        self.Draw(wx.ClientDC(self))
 
184
 
 
185
 
 
186
    # --------- public methods ---------
 
187
    def SetFont(self, font):
 
188
        """Set the font for the label"""
 
189
        wx.Panel.SetFont(self, font)
 
190
        self.SetLabel(self.label)
 
191
        self.Draw(wx.ClientDC(self))
 
192
 
 
193
 
 
194
    def Rest(self):
 
195
        """Stop the animation and return to frame 0"""
 
196
        self.Stop()
 
197
        self.current = self.rest
 
198
        self.Draw(wx.ClientDC(self))
 
199
 
 
200
 
 
201
    def Reverse(self):
 
202
        """Change the direction of the animation"""
 
203
        self.direction = -self.direction
 
204
 
 
205
 
 
206
    def Running(self):
 
207
        """Returns True if the animation is running"""
 
208
        return self.running
 
209
 
 
210
 
 
211
    def Start(self):
 
212
        """Start the animation"""
 
213
        if not self.running:
 
214
            self.running = not self.running
 
215
            self.timer.Start(int(self.frameDelay * 1000))
 
216
 
 
217
 
 
218
    def Stop(self):
 
219
        """Stop the animation"""
 
220
        if self.running:
 
221
            self.timer.Stop()
 
222
            self.running = not self.running
 
223
 
 
224
 
 
225
    def SetCurrent(self, current):
 
226
        """Set current image"""
 
227
        running = self.Running()
 
228
        if not running:
 
229
            #FIXME: need to make sure value is within range!!!
 
230
            self.current = current
 
231
            self.Draw(wx.ClientDC(self))
 
232
 
 
233
 
 
234
    def SetRest(self, rest):
 
235
        """Set rest image"""
 
236
        self.rest = rest
 
237
 
 
238
 
 
239
    def SetSequence(self, sequence = None):
 
240
        """Order to display images"""
 
241
 
 
242
        # self.sequence can be changed, but it's not recommended doing it
 
243
        # while the throbber is running.  self.sequence[0] should always
 
244
        # refer to whatever frame is to be shown when 'resting' and be sure
 
245
        # that no item in self.sequence >= self.frames or < 0!!!
 
246
 
 
247
        running = self.Running()
 
248
        self.Stop()
 
249
 
 
250
        if sequence is not None:
 
251
            #FIXME: need to make sure values are within range!!!
 
252
            self.sequence = sequence
 
253
        else:
 
254
            self.sequence = range(self.frames)
 
255
 
 
256
        if running:
 
257
            self.Start()
 
258
            
 
259
 
 
260
    def Increment(self):
 
261
        """Display next image in sequence"""
 
262
        self.current += 1
 
263
        self.Wrap()
 
264
 
 
265
 
 
266
    def Decrement(self):
 
267
        """Display previous image in sequence"""
 
268
        self.current -= 1
 
269
        self.Wrap()
 
270
 
 
271
 
 
272
    def Next(self):
 
273
        """Display next image in sequence according to direction"""
 
274
        self.current += self.direction
 
275
        self.Wrap()
 
276
 
 
277
 
 
278
    def Previous(self):
 
279
        """Display previous image in sequence according to direction"""
 
280
        self.current -= self.direction
 
281
        self.Wrap()
 
282
 
 
283
 
 
284
    def SetFrameDelay(self, frameDelay = 0.05):
 
285
        """Delay between each frame"""
 
286
        self.frameDelay = frameDelay
 
287
        if self.running:
 
288
            self.Stop()
 
289
            self.Start()
 
290
 
 
291
 
 
292
    def ToggleOverlay(self, state = None):
 
293
        """Toggle the overlay image"""
 
294
        if state is None:
 
295
            self.showOverlay = not self.showOverlay
 
296
        else:
 
297
            self.showOverlay = state
 
298
        self.Draw(wx.ClientDC(self))
 
299
 
 
300
 
 
301
    def ToggleLabel(self, state = None):
 
302
        """Toggle the label"""
 
303
        if state is None:
 
304
            self.showLabel = not self.showLabel
 
305
        else:
 
306
            self.showLabel = state
 
307
        self.Draw(wx.ClientDC(self))
 
308
 
 
309
 
 
310
    def SetLabel(self, label):
 
311
        """Change the text of the label"""
 
312
        self.label = label
 
313
        if label:
 
314
            extentX, extentY = self.GetTextExtent(label)
 
315
            self.labelX = (self.width - extentX)/2
 
316
            self.labelY = (self.height - extentY)/2
 
317
        self.Draw(wx.ClientDC(self))
 
318
 
 
319
 
 
320
 
 
321
# ------------------------------------------------------------------------------
 
322