]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/throbber.py
more informative assert message
[wxWidgets.git] / wxPython / wx / lib / throbber.py
CommitLineData
d14a1e28
RD
1"""
2A throbber displays an animated image that can be
3started, stopped, reversed, etc. Useful for showing
4an ongoing process (like most web browsers use) or
5simply for adding eye-candy to an application.
1fded56b 6
d14a1e28
RD
7Throbbers utilize a wxTimer so that normal processing
8can 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$
18#
b881fc78
RD
19# 12/12/2003 - Jeff Grimmett (grimmtooth@softhome.net)
20#
21# o 2.5 compatability update.
22#
23
d14a1e28
RD
24
25import os
26import wx
27
28# ------------------------------------------------------------------------------
fd3f2efe 29
d14a1e28 30THROBBER_EVENT = wx.NewEventType()
b881fc78 31EVT_UPDATE_THROBBER = wx.PyEventBinder(THROBBER_EVENT, 0)
d14a1e28
RD
32
33class UpdateThrobberEvent(wx.PyEvent):
34 def __init__(self):
35 wx.PyEvent.__init__(self)
36 self.SetEventType(THROBBER_EVENT)
37
38# ------------------------------------------------------------------------------
39
102e2b26 40class Throbber(wx.PyPanel):
d14a1e28
RD
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
02b800ce
RD
62 name = "throbber",
63 rest = 0,
64 current = 0,
65 direction = 1,
66 sequence = None
67 ):
102e2b26 68 wx.PyPanel.__init__(self, parent, id, pos, size, style, name)
d14a1e28
RD
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
02b800ce
RD
97 self.rest = rest
98 self.current = current
99 self.direction = direction
d14a1e28
RD
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!!!
02b800ce 125 self.SetSequence(sequence)
d14a1e28
RD
126
127 self.SetClientSize((width, height))
128
129 timerID = wx.NewId()
130 self.timer = wx.Timer(self, timerID)
131
02b800ce 132 self.Bind(EVT_UPDATE_THROBBER, self.Update)
b881fc78
RD
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)
102e2b26
RD
136
137
138 def DoGetBestSize(self):
139 return (self.width, self.height)
140
d14a1e28
RD
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):
d7403ad2 152 dc.DrawBitmap(self.submaps[self.sequence[self.current]], 0, 0, True)
d14a1e28 153 if self.overlay and self.showOverlay:
d7403ad2 154 dc.DrawBitmap(self.overlay, self.overlayX, self.overlayY, True)
d14a1e28 155 if self.label and self.showLabel:
d7403ad2 156 dc.DrawText(self.label, self.labelX, self.labelY)
d14a1e28 157 dc.SetTextForeground(wx.WHITE)
d7403ad2 158 dc.DrawText(self.label, self.labelX-1, self.labelY-1)
d14a1e28
RD
159
160
161 def OnPaint(self, event):
162 self.Draw(wx.PaintDC(self))
163 event.Skip()
164
165
02b800ce
RD
166 def Update(self, event):
167 self.Next()
168
169
170 def Wrap(self):
d14a1e28
RD
171 if self.current >= len(self.sequence):
172 if self.autoReverse:
173 self.Reverse()
174 self.current = len(self.sequence) - 1
175 else:
02b800ce
RD
176 self.current = 0
177 if self.current < 0:
d14a1e28
RD
178 if self.autoReverse:
179 self.Reverse()
02b800ce 180 self.current = 0
d14a1e28
RD
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()
02b800ce 197 self.current = self.rest
d14a1e28
RD
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
fd3f2efe 215 self.timer.Start(int(self.frameDelay * 1000))
d14a1e28
RD
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
02b800ce
RD
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
d14a1e28
RD
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# ------------------------------------------------------------------------------
1fded56b 322