]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/throbber.py
Moved config.py to the root because if wxPython hasn't been built yet
[wxWidgets.git] / wxPython / wx / lib / throbber.py
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$
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.Panel):
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 wx.Panel.__init__(self, parent, id, pos, size, style, name)
64 self.name = name
65 self.label = label
66 self.running = (1 != 1)
67 _seqTypes = (type([]), type(()))
68
69 # set size, guessing if necessary
70 width, height = size
71 if width == -1:
72 if type(bitmap) in _seqTypes:
73 width = bitmap[0].GetWidth()
74 else:
75 if frameWidth:
76 width = frameWidth
77 if height == -1:
78 if type(bitmap) in _seqTypes:
79 height = bitmap[0].GetHeight()
80 else:
81 height = bitmap.GetHeight()
82 self.width, self.height = width, height
83
84 # double check it
85 assert width != -1 and height != -1, "Unable to guess size"
86
87 if label:
88 extentX, extentY = self.GetTextExtent(label)
89 self.labelX = (width - extentX)/2
90 self.labelY = (height - extentY)/2
91 self.frameDelay = frameDelay
92 self.current = 0
93 self.direction = 1
94 self.autoReverse = reverse
95 self.overlay = overlay
96 if overlay is not None:
97 self.overlay = overlay
98 self.overlayX = (width - self.overlay.GetWidth()) / 2
99 self.overlayY = (height - self.overlay.GetHeight()) / 2
100 self.showOverlay = overlay is not None
101 self.showLabel = label is not None
102
103 # do we have a sequence of images?
104 if type(bitmap) in _seqTypes:
105 self.submaps = bitmap
106 self.frames = len(self.submaps)
107 # or a composite image that needs to be split?
108 else:
109 self.frames = frames
110 self.submaps = []
111 for chunk in range(frames):
112 rect = (chunk * frameWidth, 0, width, height)
113 self.submaps.append(bitmap.GetSubBitmap(rect))
114
115 # self.sequence can be changed, but it's not recommended doing it
116 # while the throbber is running. self.sequence[0] should always
117 # refer to whatever frame is to be shown when 'resting' and be sure
118 # that no item in self.sequence >= self.frames or < 0!!!
119 self.sequence = range(self.frames)
120
121 self.SetClientSize((width, height))
122
123 timerID = wx.NewId()
124 self.timer = wx.Timer(self, timerID)
125
126 self.Bind(EVT_UPDATE_THROBBER, self.Rotate)
127 self.Bind(wx.EVT_PAINT, self.OnPaint)
128 self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
129 self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroyWindow)
130
131
132 def OnTimer(self, event):
133 wx.PostEvent(self, UpdateThrobberEvent())
134
135
136 def OnDestroyWindow(self, event):
137 self.Stop()
138 event.Skip()
139
140
141 def Draw(self, dc):
142 dc.DrawBitmap(self.submaps[self.sequence[self.current]], (0, 0), True)
143 if self.overlay and self.showOverlay:
144 dc.DrawBitmap(self.overlay, (self.overlayX, self.overlayY), True)
145 if self.label and self.showLabel:
146 dc.DrawText(self.label, (self.labelX, self.labelY))
147 dc.SetTextForeground(wx.WHITE)
148 dc.DrawText(self.label, (self.labelX-1, self.labelY-1))
149
150
151 def OnPaint(self, event):
152 self.Draw(wx.PaintDC(self))
153 event.Skip()
154
155
156 def Rotate(self, event):
157 self.current += self.direction
158 if self.current >= len(self.sequence):
159 if self.autoReverse:
160 self.Reverse()
161 self.current = len(self.sequence) - 1
162 else:
163 self.current = 1
164 if self.current < 1:
165 if self.autoReverse:
166 self.Reverse()
167 self.current = 1
168 else:
169 self.current = len(self.sequence) - 1
170 self.Draw(wx.ClientDC(self))
171
172
173 # --------- public methods ---------
174 def SetFont(self, font):
175 """Set the font for the label"""
176 wx.Panel.SetFont(self, font)
177 self.SetLabel(self.label)
178 self.Draw(wx.ClientDC(self))
179
180
181 def Rest(self):
182 """Stop the animation and return to frame 0"""
183 self.Stop()
184 self.current = 0
185 self.Draw(wx.ClientDC(self))
186
187
188 def Reverse(self):
189 """Change the direction of the animation"""
190 self.direction = -self.direction
191
192
193 def Running(self):
194 """Returns True if the animation is running"""
195 return self.running
196
197
198 def Start(self):
199 """Start the animation"""
200 if not self.running:
201 self.running = not self.running
202 self.timer.Start(int(self.frameDelay * 1000))
203
204
205 def Stop(self):
206 """Stop the animation"""
207 if self.running:
208 self.timer.Stop()
209 self.running = not self.running
210
211
212 def SetFrameDelay(self, frameDelay = 0.05):
213 """Delay between each frame"""
214 self.frameDelay = frameDelay
215 if self.running:
216 self.Stop()
217 self.Start()
218
219
220 def ToggleOverlay(self, state = None):
221 """Toggle the overlay image"""
222 if state is None:
223 self.showOverlay = not self.showOverlay
224 else:
225 self.showOverlay = state
226 self.Draw(wx.ClientDC(self))
227
228
229 def ToggleLabel(self, state = None):
230 """Toggle the label"""
231 if state is None:
232 self.showLabel = not self.showLabel
233 else:
234 self.showLabel = state
235 self.Draw(wx.ClientDC(self))
236
237
238 def SetLabel(self, label):
239 """Change the text of the label"""
240 self.label = label
241 if label:
242 extentX, extentY = self.GetTextExtent(label)
243 self.labelX = (self.width - extentX)/2
244 self.labelY = (self.height - extentY)/2
245 self.Draw(wx.ClientDC(self))
246
247
248
249 # ------------------------------------------------------------------------------
250