]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/throbber.py
3211ce699e7e0798e33ebc2521d8fa33be6efb0d
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.
7 Throbbers run in a separate thread so normal application
8 processing can continue unencumbered.
12 # throbber.py - Cliff Wells <clifford.wells@attbi.com>
14 # Thanks to Harald Massa <harald.massa@suedvers.de> for
15 # suggestions and sample code.
21 from wxPython
.wx
import *
23 # ------------------------------------------------------------------------------
25 wxEVT_UPDATE_THROBBER
= wxNewEventType()
26 def EVT_UPDATE_THROBBER(win
, func
):
27 win
.Connect(-1, -1, wxEVT_UPDATE_THROBBER
, func
)
29 class UpdateThrobberEvent(wxPyEvent
):
31 wxPyEvent
.__init
__(self
)
32 self
.SetEventType(wxEVT_UPDATE_THROBBER
)
34 # ------------------------------------------------------------------------------
36 class Throbber(wxPanel
):
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.
47 def __init__(self
, parent
, id,
48 bitmap
, # single (composite) bitmap or list of bitmaps
49 pos
= wxDefaultPosition
,
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
59 wxPanel
.__init
__(self
, parent
, id, pos
, size
, style
, name
)
62 _seqTypes
= (type([]), type(()))
64 # set size, guessing if necessary
67 if type(bitmap
) in _seqTypes
:
68 width
= bitmap
[0].GetWidth()
73 if type(bitmap
) in _seqTypes
:
74 height
= bitmap
[0].GetHeight()
76 height
= bitmap
.GetHeight()
77 self
.width
, self
.height
= width
, height
80 assert width
!= -1 and height
!= -1, "Unable to guess size"
83 extentX
, extentY
= self
.GetTextExtent(label
)
84 self
.labelX
= (width
- extentX
)/2
85 self
.labelY
= (height
- extentY
)/2
86 self
.frameDelay
= frameDelay
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
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?
106 for chunk
in range(frames
):
107 rect
= (chunk
* frameWidth
, 0, width
, height
)
108 self
.submaps
.append(bitmap
.GetSubBitmap(rect
))
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
)
116 self
.SetClientSize((width
, height
))
118 EVT_PAINT(self
, self
.OnPaint
)
119 EVT_UPDATE_THROBBER(self
, self
.Rotate
)
120 EVT_WINDOW_DESTROY(self
, self
.OnDestroyWindow
)
122 self
.event
= threading
.Event()
123 self
.event
.set() # we start out in the "resting" state
126 def OnDestroyWindow(self
, event
):
127 # this is currently broken due to a bug in wxWindows... hopefully
128 # it'll be fixed soon. Meanwhile be sure to explicitly call Stop()
129 # before the throbber is destroyed.
135 dc
.DrawBitmap(self
.submaps
[self
.sequence
[self
.current
]], 0, 0, True)
136 if self
.overlay
and self
.showOverlay
:
137 dc
.DrawBitmap(self
.overlay
, self
.overlayX
, self
.overlayY
, True)
138 if self
.label
and self
.showLabel
:
139 dc
.DrawText(self
.label
, self
.labelX
, self
.labelY
)
140 dc
.SetTextForeground(wxWHITE
)
141 dc
.DrawText(self
.label
, self
.labelX
-1, self
.labelY
-1)
144 def OnPaint(self
, event
):
145 self
.Draw(wxPaintDC(self
))
149 def UpdateThread(self
):
151 while hasattr(self
, 'event') and not self
.event
.isSet():
152 wxPostEvent(self
, UpdateThrobberEvent())
153 self
.event
.wait(self
.frameDelay
)
154 except wxPyDeadObjectError
: # BUG: we were destroyed
158 def Rotate(self
, event
):
159 if self
.event
.isSet():
161 self
.current
+= self
.direction
162 if self
.current
>= len(self
.sequence
):
165 self
.current
= len(self
.sequence
) - 1
173 self
.current
= len(self
.sequence
) - 1
174 self
.Draw(wxClientDC(self
))
177 # --------- public methods ---------
178 def SetFont(self
, font
):
179 """Set the font for the label"""
180 wxPanel
.SetFont(self
, font
)
181 self
.SetLabel(self
.label
)
182 self
.Draw(wxClientDC(self
))
186 """Stop the animation and return to frame 0"""
189 self
.Draw(wxClientDC(self
))
193 """Change the direction of the animation"""
194 self
.direction
= -self
.direction
198 """Returns True if the animation is running"""
199 return not self
.event
.isSet()
203 """Start the animation"""
204 if not self
.Running():
206 thread
= threading
.Thread(target
= self
.UpdateThread
,
207 name
= "%s-thread" % self
.name
)
212 """Stop the animation"""
213 if self
.event
.isSet():
218 def SetFrameDelay(self
, frameDelay
= 0.05):
219 """Delay between each frame"""
220 self
.frameDelay
= frameDelay
223 def ToggleOverlay(self
, state
= None):
224 """Toggle the overlay image"""
226 self
.showOverlay
= not self
.showOverlay
228 self
.showOverlay
= state
229 self
.Draw(wxClientDC(self
))
232 def ToggleLabel(self
, state
= None):
233 """Toggle the label"""
235 self
.showLabel
= not self
.showLabel
237 self
.showLabel
= state
238 self
.Draw(wxClientDC(self
))
241 def SetLabel(self
, label
):
242 """Change the text of the label"""
245 extentX
, extentY
= self
.GetTextExtent(label
)
246 self
.labelX
= (self
.width
- extentX
)/2
247 self
.labelY
= (self
.height
- extentY
)/2
248 self
.Draw(wxClientDC(self
))
252 # ------------------------------------------------------------------------------