Introduction

This page contains the current version of the BufferedCanvas widget class.

BufferedCanvas is a fairly simple widget that implements double-buffering and also removes flicker from the rendering. This means you can spend less time trying to find out what the hell a wx.BufferedPaintDC is, and more time writing drawing code.

bufferedcanvas.py

   1 """
   2 BufferedCanvas -- Double-buffered, flicker-free canvas widget
   3 Copyright (C) 2005, 2006 Daniel Keep
   4 
   5 To use this widget, just override or replace the draw method.
   6 This will be called whenever the widget size changes, or when
   7 the update method is explicitly called.
   8 
   9 Please submit any improvements/bugfixes/ideas to the following
  10 url:
  11 
  12   http://wiki.wxpython.org/index.cgi/BufferedCanvas
  13 
  14 2006-04-29: Added bugfix for a crash on Mac provided by Marc Jans.
  15 """
  16 
  17 # Hint: try removing '.sp4msux0rz'
  18 __author__ = 'Daniel Keep <daniel.keep.sp4msux0rz@gmail.com>'
  19 
  20 __license__ = """
  21 This library is free software; you can redistribute it and/or
  22 modify it under the terms of the GNU Lesser General Public License as
  23 published by the Free Software Foundation; either version 2.1 of the
  24 License, or (at your option) any later version.
  25 
  26 As a special exception, the copyright holders of this library 
  27 hereby recind Section 3 of the GNU Lesser General Public License. This
  28 means that you MAY NOT apply the terms of the ordinary GNU General 
  29 Public License instead of this License to any given copy of the
  30 Library. This has been done to prevent users of the Library from being
  31 denied access or the ability to use future improvements.
  32 
  33 This library is distributed in the hope that it will be useful, but
  34 WITHOUT ANY WARRANTY; without even the implied warranty of
  35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
  36 General Public License for more details.
  37 
  38 You should have received a copy of the GNU Lesser General Public
  39 License along with this library; if not, write to the Free Software
  40 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  41 """
  42 
  43 __all__ = ['BufferedCanvas']
  44 
  45 import wx
  46 
  47 class BufferedCanvas(wx.Panel):
  48     """
  49     Implements a double-buffered, flicker-free canvas widget.
  50 
  51     Standard usage is to subclass this class, and override the
  52     draw method.  The draw method is passed a device context, which
  53     should be used to do your drawing.
  54 
  55     Also, you should NOT call dc.BeginDrawing() and dc.EndDrawing() --
  56     these methods are automatically called for you, although you still
  57     need to manually clear the device context.
  58 
  59     If you want to force a redraw (for whatever reason), you should
  60     call the update method.  This is because the draw method is never
  61     called as a result of an EVT_PAINT event.
  62     """
  63 
  64     # These are our two buffers.  Just be aware that when the buffers
  65     # are flipped, the REFERENCES are swapped.  So I wouldn't want to
  66     # try holding onto explicit references to one or the other ;)
  67     buffer = None
  68     backbuffer = None
  69 
  70     def __init__(self,
  71                  parent,
  72                  ID=-1,
  73                  pos=wx.DefaultPosition,
  74                  size=wx.DefaultSize,
  75                  style=wx.NO_FULL_REPAINT_ON_RESIZE):
  76         wx.Panel.__init__(self,parent,ID,pos,size,style)
  77 
  78         # Bind events
  79         self.Bind(wx.EVT_PAINT, self.onPaint)
  80         self.Bind(wx.EVT_SIZE, self.onSize)
  81 
  82         # Disable background erasing (flicker-licious)
  83         def disable_event(*pargs,**kwargs):
  84             pass # the sauce, please
  85         self.Bind(wx.EVT_ERASE_BACKGROUND, disable_event)
  86 
  87         # Ensure that the buffers are setup correctly
  88         self.onSize(None)
  89 
  90     ##
  91     ## General methods
  92     ##
  93 
  94     def draw(self,dc):
  95         """
  96         Stub: called when the canvas needs to be re-drawn.
  97         """
  98         pass
  99     
 100 
 101     def flip(self):
 102         """
 103         Flips the front and back buffers.
 104         """
 105         self.buffer,self.backbuffer = self.backbuffer,self.buffer
 106         self.Refresh()
 107 
 108 
 109     def update(self):
 110         """
 111         Causes the canvas to be updated.
 112         """
 113         dc = wx.MemoryDC()
 114         dc.SelectObject(self.backbuffer)
 115         dc.BeginDrawing()
 116         self.draw(dc)
 117         dc.EndDrawing()
 118         self.flip()
 119 
 120     ##
 121     ## Event handlers
 122     ##
 123 
 124     def onPaint(self, event):
 125         # Blit the front buffer to the screen
 126         dc = wx.BufferedPaintDC(self, self.buffer)
 127 
 128 
 129     def onSize(self, event):
 130         # Here we need to create a new off-screen buffer to hold
 131         # the in-progress drawings on.
 132         width,height = self.GetClientSizeTuple()
 133         if width == 0:
 134             width = 1
 135         if height == 0:
 136             height = 1
 137         self.buffer = wx.EmptyBitmap(width,height)
 138         self.backbuffer = wx.EmptyBitmap(width,height)
 139 
 140         # Now update the screen
 141         self.update()

Example

import wx
from bufferedcanvas import *


class TestCanvas(BufferedCanvas):

    def __init__(self,parent,ID=-1):
        BufferedCanvas.__init__(self,parent,ID)


    def draw(self, dc):
        dc.SetBackground(wx.Brush("Black"))
        dc.Clear()

        dc.SetBrush(wx.BLUE_BRUSH)
        dc.SetPen(wx.Pen('Red', 4))
        dc.DrawRectangle(20,20,300,200)


class TestFrame(wx.Frame):

    def __init__(self,
                 parent=None,
                 ID=-1,
                 title="BufferedCanvas Test",
                 pos=wx.DefaultPosition,
                 size=wx.DefaultSize,
                 style=wx.DEFAULT_FRAME_STYLE):
        wx.Frame.__init__(self,parent,ID,title,pos,size,style)
        self.canvas = TestCanvas(self)
        self.Bind(wx.EVT_CLOSE, self.onClose)

    def onClose(self,event):
        self.Show(False)
        self.Destroy()


def main():
    app = wx.PySimpleApp()
    frame = TestFrame()
    frame.Show(True)
    app.MainLoop()

if __name__ == '__main__':
    main()

Comments

If you have any suggestions, improvements, or bug-fixes (there are bugs?!), then please let me know via my cunningly mangled email address (it's in the source code). Please don't go modifying the code without letting me know first.

BufferedCanvas (last edited 2008-03-11 10:50:39 by localhost)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.