/wx/lib/agw/pybusyinfo.py

https://bitbucket.org/windwiny/wxpython · Python · 285 lines · 166 code · 47 blank · 72 comment · 7 complexity · 6efb2e370adbf640479af09899988ca4 MD5 · raw file

  1. """
  2. L{PyBusyInfo} constructs a busy info window and displays a message in it.
  3. Description
  4. ===========
  5. L{PyBusyInfo} constructs a busy info window and displays a message in it.
  6. This class makes it easy to tell your user that the program is temporarily busy.
  7. Just create a L{PyBusyInfo} object, and within the current scope, a message window
  8. will be shown.
  9. For example::
  10. busy = PyBusyInfo("Please wait, working...")
  11. for i in xrange(10000):
  12. DoACalculation()
  13. del busy
  14. It works by creating a window in the constructor, and deleting it in the destructor.
  15. You may also want to call `wx.Yield()` to refresh the window periodically (in case
  16. it had been obscured by other windows, for example).
  17. Usage
  18. =====
  19. Usage example::
  20. import wx
  21. import wx.lib.agw.pybusyinfo as PBI
  22. class MyFrame(wx.Frame):
  23. def __init__(self, parent):
  24. wx.Frame.__init__(self, parent, -1, "PyBusyInfo Demo")
  25. panel = wx.Panel(self)
  26. b = wx.Button(panel, -1, "Test PyBusyInfo ", (50,50))
  27. self.Bind(wx.EVT_BUTTON, self.OnButton, b)
  28. def OnButton(self, event):
  29. message = "Please wait 5 seconds, working..."
  30. busy = PBI.PyBusyInfo(message, parent=self, title="Really Busy")
  31. wx.Yield()
  32. for indx in xrange(5):
  33. wx.MilliSleep(1000)
  34. del busy
  35. # our normal wxApp-derived class, as usual
  36. app = wx.PySimpleApp()
  37. frame = MyFrame(None)
  38. app.SetTopWindow(frame)
  39. frame.Show()
  40. app.MainLoop()
  41. Supported Platforms
  42. ===================
  43. L{PyBusyInfo} has been tested on the following platforms:
  44. * Windows (Windows XP).
  45. Window Styles
  46. =============
  47. `No particular window styles are available for this class.`
  48. Events Processing
  49. =================
  50. `No custom events are available for this class.`
  51. License And Version
  52. ===================
  53. L{PyBusyInfo} is distributed under the wxPython license.
  54. Latest Revision: Andrea Gavana @ 17 Aug 2011, 15.00 GMT
  55. Version 0.1
  56. """
  57. import wx
  58. _ = wx.GetTranslation
  59. class PyInfoFrame(wx.Frame):
  60. """ Base class for L{PyBusyInfo}. """
  61. def __init__(self, parent, message, title, icon):
  62. """
  63. Default class constructor.
  64. :param `parent`: the frame parent;
  65. :param `message`: the message to display in the L{PyBusyInfo};
  66. :param `title`: the main L{PyBusyInfo} title;
  67. :param `icon`: an icon to draw as the frame icon, an instance of `wx.Bitmap`.
  68. """
  69. wx.Frame.__init__(self, parent, wx.ID_ANY, title, wx.DefaultPosition,
  70. wx.DefaultSize, wx.NO_BORDER|wx.FRAME_TOOL_WINDOW|wx.FRAME_SHAPED|wx.STAY_ON_TOP)
  71. panel = wx.Panel(self)
  72. panel.SetCursor(wx.HOURGLASS_CURSOR)
  73. self._message = message
  74. self._title = title
  75. self._icon = icon
  76. dc = wx.ClientDC(self)
  77. textWidth, textHeight, dummy = dc.GetMultiLineTextExtent(self._message)
  78. sizeText = wx.Size(textWidth, textHeight)
  79. self.SetClientSize((max(sizeText.x, 340) + 60, max(sizeText.y, 40) + 60))
  80. # need to size the panel correctly first so that text.Centre() works
  81. panel.SetSize(self.GetClientSize())
  82. # Bind the events to draw ourselves
  83. panel.Bind(wx.EVT_PAINT, self.OnPaint)
  84. panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
  85. self.Centre(wx.BOTH)
  86. # Create a non-rectangular region to set the frame shape
  87. size = self.GetSize()
  88. bmp = wx.EmptyBitmap(size.x, size.y)
  89. dc = wx.BufferedDC(None, bmp)
  90. dc.SetBackground(wx.Brush(wx.Colour(0, 0, 0), wx.SOLID))
  91. dc.Clear()
  92. dc.SetPen(wx.Pen(wx.Colour(0, 0, 0), 1))
  93. dc.DrawRoundedRectangle(0, 0, size.x, size.y, 12)
  94. r = wx.RegionFromBitmapColour(bmp, wx.Colour(0, 0, 0))
  95. # Store the non-rectangular region
  96. self.reg = r
  97. if wx.Platform == "__WXGTK__":
  98. self.Bind(wx.EVT_WINDOW_CREATE, self.SetBusyShape)
  99. else:
  100. self.SetBusyShape()
  101. # Add a custom bitmap at the top (if any)
  102. def SetBusyShape(self, event=None):
  103. """
  104. Sets L{PyInfoFrame} shape using the region created from the bitmap.
  105. :param `event`: a `wx.WindowCreateEvent` event (GTK only, as GTK supports setting
  106. the window shape only during window creation).
  107. """
  108. self.SetShape(self.reg)
  109. if event:
  110. # GTK only
  111. event.Skip()
  112. def OnPaint(self, event):
  113. """
  114. Handles the ``wx.EVT_PAINT`` event for L{PyInfoFrame}.
  115. :param `event`: a `wx.PaintEvent` to be processed.
  116. """
  117. panel = event.GetEventObject()
  118. dc = wx.BufferedPaintDC(panel)
  119. dc.Clear()
  120. # Fill the background with a gradient shading
  121. startColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
  122. endColour = wx.WHITE
  123. rect = panel.GetRect()
  124. dc.GradientFillLinear(rect, startColour, endColour, wx.SOUTH)
  125. # Draw the label
  126. font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
  127. dc.SetFont(font)
  128. # Draw the message
  129. rect2 = wx.Rect(*rect)
  130. rect2.height += 20
  131. dc.DrawLabel(self._message, rect2, alignment=wx.ALIGN_CENTER|wx.ALIGN_CENTER)
  132. # Draw the top title
  133. font.SetWeight(wx.BOLD)
  134. dc.SetFont(font)
  135. dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_CAPTIONTEXT)))
  136. dc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_CAPTIONTEXT))
  137. if self._icon.IsOk():
  138. iconWidth, iconHeight = self._icon.GetWidth(), self._icon.GetHeight()
  139. dummy, textHeight = dc.GetTextExtent(self._title)
  140. textXPos, textYPos = iconWidth + 10, (iconHeight-textHeight)/2
  141. dc.DrawBitmap(self._icon, 5, 5, True)
  142. else:
  143. textXPos, textYPos = 5, 0
  144. dc.DrawText(self._title, textXPos, textYPos+5)
  145. dc.DrawLine(5, 25, rect.width-5, 25)
  146. size = self.GetSize()
  147. dc.SetPen(wx.Pen(startColour, 1))
  148. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  149. dc.DrawRoundedRectangle(0, 0, size.x, size.y-1, 12)
  150. def OnErase(self, event):
  151. """
  152. Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyInfoFrame}.
  153. :param `event`: a `wx.EraseEvent` event to be processed.
  154. :note: This method is intentionally empty to reduce flicker.
  155. """
  156. # This is empty on purpose, to avoid flickering
  157. pass
  158. # -------------------------------------------------------------------- #
  159. # The actual PyBusyInfo implementation
  160. # -------------------------------------------------------------------- #
  161. class PyBusyInfo(object):
  162. """
  163. Constructs a busy info window as child of parent and displays a message in it.
  164. """
  165. def __init__(self, message, parent=None, title=_("Busy"), icon=wx.NullBitmap):
  166. """
  167. Default class constructor.
  168. :param `parent`: the L{PyBusyInfo} parent;
  169. :param `message`: the message to display in the L{PyBusyInfo};
  170. :param `title`: the main L{PyBusyInfo} title;
  171. :param `icon`: an icon to draw as the frame icon, an instance of `wx.Bitmap`.
  172. :note: If `parent` is not ``None`` you must ensure that it is not closed
  173. while the busy info is shown.
  174. """
  175. self._infoFrame = PyInfoFrame(parent, message, title, icon)
  176. if parent and parent.HasFlag(wx.STAY_ON_TOP):
  177. # we must have this flag to be in front of our parent if it has it
  178. self._infoFrame.SetWindowStyleFlag(wx.STAY_ON_TOP)
  179. self._infoFrame.Show(True)
  180. self._infoFrame.Refresh()
  181. self._infoFrame.Update()
  182. def __del__(self):
  183. """ Overloaded method, for compatibility with wxWidgets. """
  184. self._infoFrame.Show(False)
  185. self._infoFrame.Destroy()