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