PageRenderTime 33ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/wxPython-src-2.8.12.0/wxPython/wx/lib/throbber.py

#
Python | 322 lines | 286 code | 8 blank | 28 comment | 0 complexity | 7a90192e66fd759ca15b4892d160676d MD5 | raw file
Possible License(s): BSD-2-Clause, GPL-2.0, LGPL-2.0, LGPL-3.0, AGPL-3.0, CC-BY-SA-3.0, BSD-3-Clause
  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. Throbbers utilize a wxTimer so that normal processing
  7. can continue unencumbered.
  8. """
  9. #
  10. # throbber.py - Cliff Wells <clifford.wells@comcast.net>
  11. #
  12. # Thanks to Harald Massa <harald.massa@suedvers.de> for
  13. # suggestions and sample code.
  14. #
  15. # $Id: throbber.py 36607 2005-12-30 23:02:03Z RD $
  16. #
  17. # 12/12/2003 - Jeff Grimmett (grimmtooth@softhome.net)
  18. #
  19. # o 2.5 compatability update.
  20. #
  21. import os
  22. import wx
  23. # ------------------------------------------------------------------------------
  24. THROBBER_EVENT = wx.NewEventType()
  25. EVT_UPDATE_THROBBER = wx.PyEventBinder(THROBBER_EVENT, 0)
  26. class UpdateThrobberEvent(wx.PyEvent):
  27. def __init__(self):
  28. wx.PyEvent.__init__(self)
  29. self.SetEventType(THROBBER_EVENT)
  30. # ------------------------------------------------------------------------------
  31. class Throbber(wx.PyPanel):
  32. """
  33. The first argument is either the name of a file that will be split into frames
  34. (a composite image) or a list of strings of image names that will be treated
  35. as individual frames. If a single (composite) image is given, then additional
  36. information must be provided: the number of frames in the image and the width
  37. of each frame. The first frame is treated as the "at rest" frame (it is not
  38. shown during animation, but only when Throbber.Rest() is called.
  39. A second, single image may be optionally specified to overlay on top of the
  40. animation. A label may also be specified to show on top of the animation.
  41. """
  42. def __init__(self, parent, id,
  43. bitmap, # single (composite) bitmap or list of bitmaps
  44. pos = wx.DefaultPosition,
  45. size = wx.DefaultSize,
  46. frameDelay = 0.1,# time between frames
  47. frames = 0, # number of frames (only necessary for composite image)
  48. frameWidth = 0, # width of each frame (only necessary for composite image)
  49. label = None, # optional text to be displayed
  50. overlay = None, # optional image to overlay on animation
  51. reverse = 0, # reverse direction at end of animation
  52. style = 0, # window style
  53. name = "throbber",
  54. rest = 0,
  55. current = 0,
  56. direction = 1,
  57. sequence = None
  58. ):
  59. wx.PyPanel.__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. # 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. # double check it
  79. assert width != -1 and height != -1, "Unable to guess size"
  80. if label:
  81. extentX, extentY = self.GetTextExtent(label)
  82. self.labelX = (width - extentX)/2
  83. self.labelY = (height - extentY)/2
  84. self.frameDelay = frameDelay
  85. self.rest = rest
  86. self.current = current
  87. self.direction = direction
  88. self.autoReverse = reverse
  89. self.overlay = overlay
  90. if overlay is not None:
  91. self.overlay = overlay
  92. self.overlayX = (width - self.overlay.GetWidth()) / 2
  93. self.overlayY = (height - self.overlay.GetHeight()) / 2
  94. self.showOverlay = overlay is not None
  95. self.showLabel = label is not None
  96. # do we have a sequence of images?
  97. if type(bitmap) in _seqTypes:
  98. self.submaps = bitmap
  99. self.frames = len(self.submaps)
  100. # or a composite image that needs to be split?
  101. else:
  102. self.frames = frames
  103. self.submaps = []
  104. for chunk in range(frames):
  105. rect = (chunk * frameWidth, 0, width, height)
  106. self.submaps.append(bitmap.GetSubBitmap(rect))
  107. # self.sequence can be changed, but it's not recommended doing it
  108. # while the throbber is running. self.sequence[0] should always
  109. # refer to whatever frame is to be shown when 'resting' and be sure
  110. # that no item in self.sequence >= self.frames or < 0!!!
  111. self.SetSequence(sequence)
  112. self.SetClientSize((width, height))
  113. timerID = wx.NewId()
  114. self.timer = wx.Timer(self, timerID)
  115. self.Bind(EVT_UPDATE_THROBBER, self.Update)
  116. self.Bind(wx.EVT_PAINT, self.OnPaint)
  117. self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
  118. self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroyWindow)
  119. def DoGetBestSize(self):
  120. return (self.width, self.height)
  121. def OnTimer(self, event):
  122. wx.PostEvent(self, UpdateThrobberEvent())
  123. def OnDestroyWindow(self, event):
  124. self.Stop()
  125. event.Skip()
  126. def Draw(self, dc):
  127. dc.DrawBitmap(self.submaps[self.sequence[self.current]], 0, 0, True)
  128. if self.overlay and self.showOverlay:
  129. dc.DrawBitmap(self.overlay, self.overlayX, self.overlayY, True)
  130. if self.label and self.showLabel:
  131. dc.DrawText(self.label, self.labelX, self.labelY)
  132. dc.SetTextForeground(wx.WHITE)
  133. dc.DrawText(self.label, self.labelX-1, self.labelY-1)
  134. def OnPaint(self, event):
  135. self.Draw(wx.PaintDC(self))
  136. event.Skip()
  137. def Update(self, event):
  138. self.Next()
  139. def Wrap(self):
  140. if self.current >= len(self.sequence):
  141. if self.autoReverse:
  142. self.Reverse()
  143. self.current = len(self.sequence) - 1
  144. else:
  145. self.current = 0
  146. if self.current < 0:
  147. if self.autoReverse:
  148. self.Reverse()
  149. self.current = 0
  150. else:
  151. self.current = len(self.sequence) - 1
  152. self.Draw(wx.ClientDC(self))
  153. # --------- public methods ---------
  154. def SetFont(self, font):
  155. """Set the font for the label"""
  156. wx.Panel.SetFont(self, font)
  157. self.SetLabel(self.label)
  158. self.Draw(wx.ClientDC(self))
  159. def Rest(self):
  160. """Stop the animation and return to frame 0"""
  161. self.Stop()
  162. self.current = self.rest
  163. self.Draw(wx.ClientDC(self))
  164. def Reverse(self):
  165. """Change the direction of the animation"""
  166. self.direction = -self.direction
  167. def Running(self):
  168. """Returns True if the animation is running"""
  169. return self.running
  170. def Start(self):
  171. """Start the animation"""
  172. if not self.running:
  173. self.running = not self.running
  174. self.timer.Start(int(self.frameDelay * 1000))
  175. def Stop(self):
  176. """Stop the animation"""
  177. if self.running:
  178. self.timer.Stop()
  179. self.running = not self.running
  180. def SetCurrent(self, current):
  181. """Set current image"""
  182. running = self.Running()
  183. if not running:
  184. #FIXME: need to make sure value is within range!!!
  185. self.current = current
  186. self.Draw(wx.ClientDC(self))
  187. def SetRest(self, rest):
  188. """Set rest image"""
  189. self.rest = rest
  190. def SetSequence(self, sequence = None):
  191. """Order to display images"""
  192. # self.sequence can be changed, but it's not recommended doing it
  193. # while the throbber is running. self.sequence[0] should always
  194. # refer to whatever frame is to be shown when 'resting' and be sure
  195. # that no item in self.sequence >= self.frames or < 0!!!
  196. running = self.Running()
  197. self.Stop()
  198. if sequence is not None:
  199. #FIXME: need to make sure values are within range!!!
  200. self.sequence = sequence
  201. else:
  202. self.sequence = range(self.frames)
  203. if running:
  204. self.Start()
  205. def Increment(self):
  206. """Display next image in sequence"""
  207. self.current += 1
  208. self.Wrap()
  209. def Decrement(self):
  210. """Display previous image in sequence"""
  211. self.current -= 1
  212. self.Wrap()
  213. def Next(self):
  214. """Display next image in sequence according to direction"""
  215. self.current += self.direction
  216. self.Wrap()
  217. def Previous(self):
  218. """Display previous image in sequence according to direction"""
  219. self.current -= self.direction
  220. self.Wrap()
  221. def SetFrameDelay(self, frameDelay = 0.05):
  222. """Delay between each frame"""
  223. self.frameDelay = frameDelay
  224. if self.running:
  225. self.Stop()
  226. self.Start()
  227. def ToggleOverlay(self, state = None):
  228. """Toggle the overlay image"""
  229. if state is None:
  230. self.showOverlay = not self.showOverlay
  231. else:
  232. self.showOverlay = state
  233. self.Draw(wx.ClientDC(self))
  234. def ToggleLabel(self, state = None):
  235. """Toggle the label"""
  236. if state is None:
  237. self.showLabel = not self.showLabel
  238. else:
  239. self.showLabel = state
  240. self.Draw(wx.ClientDC(self))
  241. def SetLabel(self, label):
  242. """Change the text of the label"""
  243. self.label = label
  244. if label:
  245. extentX, extentY = self.GetTextExtent(label)
  246. self.labelX = (self.width - extentX)/2
  247. self.labelY = (self.height - extentY)/2
  248. self.Draw(wx.ClientDC(self))
  249. # ------------------------------------------------------------------------------