]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/throbber.py
Version number
[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#
19
20import os
21import wx
22
23# ------------------------------------------------------------------------------
24THROBBER_EVENT = wx.NewEventType()
25def EVT_UPDATE_THROBBER(win, func):
26 win.Connect(-1, -1, THROBBER_EVENT, func)
27
28class UpdateThrobberEvent(wx.PyEvent):
29 def __init__(self):
30 wx.PyEvent.__init__(self)
31 self.SetEventType(THROBBER_EVENT)
32
33# ------------------------------------------------------------------------------
34
35class Throbber(wx.Panel):
36 """
37 The first argument is either the name of a file that will be split into frames
38 (a composite image) or a list of strings of image names that will be treated
39 as individual frames. If a single (composite) image is given, then additional
40 information must be provided: the number of frames in the image and the width
41 of each frame. The first frame is treated as the "at rest" frame (it is not
42 shown during animation, but only when Throbber.Rest() is called.
43 A second, single image may be optionally specified to overlay on top of the
44 animation. A label may also be specified to show on top of the animation.
45 """
46 def __init__(self, parent, id,
47 bitmap, # single (composite) bitmap or list of bitmaps
48 pos = wx.DefaultPosition,
49 size = wx.DefaultSize,
50 frameDelay = 0.1,# time between frames
51 frames = 0, # number of frames (only necessary for composite image)
52 frameWidth = 0, # width of each frame (only necessary for composite image)
53 label = None, # optional text to be displayed
54 overlay = None, # optional image to overlay on animation
55 reverse = 0, # reverse direction at end of animation
56 style = 0, # window style
57 name = "throbber"):
58 wx.Panel.__init__(self, parent, id, pos, size, style, name)
59 self.name = name
60 self.label = label
61 self.running = (1 != 1)
62 _seqTypes = (type([]), type(()))
63
64 # set size, guessing if necessary
65 width, height = size
66 if width == -1:
67 if type(bitmap) in _seqTypes:
68 width = bitmap[0].GetWidth()
69 else:
70 if frameWidth:
71 width = frameWidth
72 if height == -1:
73 if type(bitmap) in _seqTypes:
74 height = bitmap[0].GetHeight()
75 else:
76 height = bitmap.GetHeight()
77 self.width, self.height = width, height
78
79 # double check it
80 assert width != -1 and height != -1, "Unable to guess size"
81
82 if label:
83 extentX, extentY = self.GetTextExtent(label)
84 self.labelX = (width - extentX)/2
85 self.labelY = (height - extentY)/2
86 self.frameDelay = frameDelay
87 self.current = 0
88 self.direction = 1
89 self.autoReverse = reverse
90 self.overlay = overlay
91 if overlay is not None:
92 self.overlay = overlay
93 self.overlayX = (width - self.overlay.GetWidth()) / 2
94 self.overlayY = (height - self.overlay.GetHeight()) / 2
95 self.showOverlay = overlay is not None
96 self.showLabel = label is not None
97
98 # do we have a sequence of images?
99 if type(bitmap) in _seqTypes:
100 self.submaps = bitmap
101 self.frames = len(self.submaps)
102 # or a composite image that needs to be split?
103 else:
104 self.frames = frames
105 self.submaps = []
106 for chunk in range(frames):
107 rect = (chunk * frameWidth, 0, width, height)
108 self.submaps.append(bitmap.GetSubBitmap(rect))
109
110 # self.sequence can be changed, but it's not recommended doing it
111 # while the throbber is running. self.sequence[0] should always
112 # refer to whatever frame is to be shown when 'resting' and be sure
113 # that no item in self.sequence >= self.frames or < 0!!!
114 self.sequence = range(self.frames)
115
116 self.SetClientSize((width, height))
117
118 timerID = wx.NewId()
119 self.timer = wx.Timer(self, timerID)
120
121 EVT_UPDATE_THROBBER(self, self.Rotate)
122 wx.EVT_PAINT(self, self.OnPaint)
123 wx.EVT_TIMER(self, timerID, self.OnTimer)
124 wx.EVT_WINDOW_DESTROY(self, self.OnDestroyWindow)
125
126
127 def OnTimer(self, event):
128 wx.PostEvent(self, UpdateThrobberEvent())
129
130
131 def OnDestroyWindow(self, event):
132 self.Stop()
133 event.Skip()
134
135
136 def Draw(self, dc):
137 dc.DrawBitmap(self.submaps[self.sequence[self.current]], 0, 0, True)
138 if self.overlay and self.showOverlay:
139 dc.DrawBitmap(self.overlay, self.overlayX, self.overlayY, True)
140 if self.label and self.showLabel:
141 dc.DrawText(self.label, self.labelX, self.labelY)
142 dc.SetTextForeground(wx.WHITE)
143 dc.DrawText(self.label, self.labelX-1, self.labelY-1)
144
145
146 def OnPaint(self, event):
147 self.Draw(wx.PaintDC(self))
148 event.Skip()
149
150
151 def Rotate(self, event):
152 self.current += self.direction
153 if self.current >= len(self.sequence):
154 if self.autoReverse:
155 self.Reverse()
156 self.current = len(self.sequence) - 1
157 else:
158 self.current = 1
159 if self.current < 1:
160 if self.autoReverse:
161 self.Reverse()
162 self.current = 1
163 else:
164 self.current = len(self.sequence) - 1
165 self.Draw(wx.ClientDC(self))
166
167
168 # --------- public methods ---------
169 def SetFont(self, font):
170 """Set the font for the label"""
171 wx.Panel.SetFont(self, font)
172 self.SetLabel(self.label)
173 self.Draw(wx.ClientDC(self))
174
175
176 def Rest(self):
177 """Stop the animation and return to frame 0"""
178 self.Stop()
179 self.current = 0
180 self.Draw(wx.ClientDC(self))
181
182
183 def Reverse(self):
184 """Change the direction of the animation"""
185 self.direction = -self.direction
186
187
188 def Running(self):
189 """Returns True if the animation is running"""
190 return self.running
191
192
193 def Start(self):
194 """Start the animation"""
195 if not self.running:
196 self.running = not self.running
197 self.timer.Start(self.frameDelay * 1000)
198
199
200 def Stop(self):
201 """Stop the animation"""
202 if self.running:
203 self.timer.Stop()
204 self.running = not self.running
205
206
207 def SetFrameDelay(self, frameDelay = 0.05):
208 """Delay between each frame"""
209 self.frameDelay = frameDelay
210 if self.running:
211 self.Stop()
212 self.Start()
213
214
215 def ToggleOverlay(self, state = None):
216 """Toggle the overlay image"""
217 if state is None:
218 self.showOverlay = not self.showOverlay
219 else:
220 self.showOverlay = state
221 self.Draw(wx.ClientDC(self))
222
223
224 def ToggleLabel(self, state = None):
225 """Toggle the label"""
226 if state is None:
227 self.showLabel = not self.showLabel
228 else:
229 self.showLabel = state
230 self.Draw(wx.ClientDC(self))
231
232
233 def SetLabel(self, label):
234 """Change the text of the label"""
235 self.label = label
236 if label:
237 extentX, extentY = self.GetTextExtent(label)
238 self.labelX = (self.width - extentX)/2
239 self.labelY = (self.height - extentY)/2
240 self.Draw(wx.ClientDC(self))
241
242
243
244# ------------------------------------------------------------------------------
1fded56b 245