"""
L{PyBusyInfo} constructs a busy info window and displays a message in it.
Description
===========
L{PyBusyInfo} constructs a busy info window and displays a message in it.
This class makes it easy to tell your user that the program is temporarily busy.
Just create a L{PyBusyInfo} object, and within the current scope, a message window
will be shown.
For example::
busy = PyBusyInfo("Please wait, working...")
for i in xrange(10000):
DoACalculation()
del busy
It works by creating a window in the constructor, and deleting it in the destructor.
You may also want to call `wx.Yield()` to refresh the window periodically (in case
it had been obscured by other windows, for example).
Usage
=====
Usage example::
import wx
import wx.lib.agw.pybusyinfo as PBI
class MyFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, -1, "PyBusyInfo Demo")
panel = wx.Panel(self)
b = wx.Button(panel, -1, "Test PyBusyInfo ", (50,50))
self.Bind(wx.EVT_BUTTON, self.OnButton, b)
def OnButton(self, event):
message = "Please wait 5 seconds, working..."
busy = PBI.PyBusyInfo(message, parent=self, title="Really Busy")
wx.Yield()
for indx in xrange(5):
wx.MilliSleep(1000)
del busy
# our normal wxApp-derived class, as usual
app = wx.PySimpleApp()
frame = MyFrame(None)
app.SetTopWindow(frame)
frame.Show()
app.MainLoop()
Supported Platforms
===================
L{PyBusyInfo} has been tested on the following platforms:
* Windows (Windows XP).
Window Styles
=============
`No particular window styles are available for this class.`
Events Processing
=================
`No custom events are available for this class.`
License And Version
===================
L{PyBusyInfo} is distributed under the wxPython license.
Latest Revision: Andrea Gavana @ 17 Aug 2011, 15.00 GMT
Version 0.1
"""
import wx
_ = wx.GetTranslation
[docs]class PyInfoFrame(wx.Frame):
""" Base class for L{PyBusyInfo}. """
[docs] def __init__(self, parent, message, title, icon):
"""
Default class constructor.
:param `parent`: the frame parent;
:param `message`: the message to display in the L{PyBusyInfo};
:param `title`: the main L{PyBusyInfo} title;
:param `icon`: an icon to draw as the frame icon, an instance of `wx.Bitmap`.
"""
wx.Frame.__init__(self, parent, wx.ID_ANY, title, wx.DefaultPosition,
wx.DefaultSize, wx.NO_BORDER|wx.FRAME_TOOL_WINDOW|wx.FRAME_SHAPED|wx.STAY_ON_TOP)
panel = wx.Panel(self)
panel.SetCursor(wx.HOURGLASS_CURSOR)
self._message = message
self._title = title
self._icon = icon
dc = wx.ClientDC(self)
textWidth, textHeight, dummy = dc.GetMultiLineTextExtent(self._message)
sizeText = wx.Size(textWidth, textHeight)
self.SetClientSize((max(sizeText.x, 340) + 60, max(sizeText.y, 40) + 60))
# need to size the panel correctly first so that text.Centre() works
panel.SetSize(self.GetClientSize())
# Bind the events to draw ourselves
panel.Bind(wx.EVT_PAINT, self.OnPaint)
panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
self.Centre(wx.BOTH)
# Create a non-rectangular region to set the frame shape
size = self.GetSize()
bmp = wx.EmptyBitmap(size.x, size.y)
dc = wx.BufferedDC(None, bmp)
dc.SetBackground(wx.Brush(wx.Colour(0, 0, 0), wx.SOLID))
dc.Clear()
dc.SetPen(wx.Pen(wx.Colour(0, 0, 0), 1))
dc.DrawRoundedRectangle(0, 0, size.x, size.y, 12)
r = wx.RegionFromBitmapColour(bmp, wx.Colour(0, 0, 0))
# Store the non-rectangular region
self.reg = r
if wx.Platform == "__WXGTK__":
self.Bind(wx.EVT_WINDOW_CREATE, self.SetBusyShape)
else:
self.SetBusyShape()
# Add a custom bitmap at the top (if any)
[docs] def SetBusyShape(self, event=None):
"""
Sets L{PyInfoFrame} shape using the region created from the bitmap.
:param `event`: a `wx.WindowCreateEvent` event (GTK only, as GTK supports setting
the window shape only during window creation).
"""
self.SetShape(self.reg)
if event:
# GTK only
event.Skip()
[docs] def OnPaint(self, event):
"""
Handles the ``wx.EVT_PAINT`` event for L{PyInfoFrame}.
:param `event`: a `wx.PaintEvent` to be processed.
"""
panel = event.GetEventObject()
dc = wx.BufferedPaintDC(panel)
dc.Clear()
# Fill the background with a gradient shading
startColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
endColour = wx.WHITE
rect = panel.GetRect()
dc.GradientFillLinear(rect, startColour, endColour, wx.SOUTH)
# Draw the label
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
dc.SetFont(font)
# Draw the message
rect2 = wx.Rect(*rect)
rect2.height += 20
dc.DrawLabel(self._message, rect2, alignment=wx.ALIGN_CENTER|wx.ALIGN_CENTER)
# Draw the top title
font.SetWeight(wx.BOLD)
dc.SetFont(font)
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_CAPTIONTEXT)))
dc.SetTextForeground(wx.SystemSettings_GetColour(wx.SYS_COLOUR_CAPTIONTEXT))
if self._icon.IsOk():
iconWidth, iconHeight = self._icon.GetWidth(), self._icon.GetHeight()
dummy, textHeight = dc.GetTextExtent(self._title)
textXPos, textYPos = iconWidth + 10, (iconHeight-textHeight)/2
dc.DrawBitmap(self._icon, 5, 5, True)
else:
textXPos, textYPos = 5, 0
dc.DrawText(self._title, textXPos, textYPos+5)
dc.DrawLine(5, 25, rect.width-5, 25)
size = self.GetSize()
dc.SetPen(wx.Pen(startColour, 1))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRoundedRectangle(0, 0, size.x, size.y-1, 12)
[docs] def OnErase(self, event):
"""
Handles the ``wx.EVT_ERASE_BACKGROUND`` event for L{PyInfoFrame}.
:param `event`: a `wx.EraseEvent` event to be processed.
:note: This method is intentionally empty to reduce flicker.
"""
# This is empty on purpose, to avoid flickering
pass
# -------------------------------------------------------------------- #
# The actual PyBusyInfo implementation
# -------------------------------------------------------------------- #
[docs]class PyBusyInfo(object):
"""
Constructs a busy info window as child of parent and displays a message in it.
"""
[docs] def __init__(self, message, parent=None, title=_("Busy"), icon=wx.NullBitmap):
"""
Default class constructor.
:param `parent`: the L{PyBusyInfo} parent;
:param `message`: the message to display in the L{PyBusyInfo};
:param `title`: the main L{PyBusyInfo} title;
:param `icon`: an icon to draw as the frame icon, an instance of `wx.Bitmap`.
:note: If `parent` is not ``None`` you must ensure that it is not closed
while the busy info is shown.
"""
self._infoFrame = PyInfoFrame(parent, message, title, icon)
if parent and parent.HasFlag(wx.STAY_ON_TOP):
# we must have this flag to be in front of our parent if it has it
self._infoFrame.SetWindowStyleFlag(wx.STAY_ON_TOP)
self._infoFrame.Show(True)
self._infoFrame.Refresh()
self._infoFrame.Update()
def __del__(self):
""" Overloaded method, for compatibility with wxWidgets. """
self._infoFrame.Show(False)
self._infoFrame.Destroy()