wxPython / wx / lib / agw /

Full commit
L{PyBusyInfo} constructs a busy info window and displays a message in it.


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):

    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 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")

            for indx in xrange(5):

            del busy

    # our normal wxApp-derived class, as usual

    app = wx.PySimpleApp()

    frame = MyFrame(None)


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

class PyInfoFrame(wx.Frame):
    """ Base class for L{PyBusyInfo}. """

    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)

        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

        # Bind the events to draw ourselves
        panel.Bind(wx.EVT_PAINT, self.OnPaint)
        panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)

        # 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.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)

        # Add a custom bitmap at the top (if any)

    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).

        if event:
            # GTK only

    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)

        # 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)

        # 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

        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)
            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.DrawRoundedRectangle(0, 0, size.x, size.y-1, 12)

    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

# -------------------------------------------------------------------- #
# The actual PyBusyInfo implementation
# -------------------------------------------------------------------- #

class PyBusyInfo(object):
    Constructs a busy info window as child of parent and displays a message in it.

    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

    def __del__(self):
        """ Overloaded method, for compatibility with wxWidgets. """