Drawing on a wxPython surface

vegaseat 1 Tallied Votes 892 Views Share

The surface or canvas established with wx.PaintDC() can be used to draw a number of shapes like lines, rectangles and circles. The snippet shows you how to select colors and then draw a few simple shapes. Use help(wx.PaintDC) to get information about the many things you can use this surface for.

# draw lines, a rounded-rectangle and a circle on a wx.PaintDC() surface
# tested with Python24 and wxPython26     vegaseat      06mar2007

import wx 

class MyFrame(wx.Frame): 
    """a frame with a panel"""
    def __init__(self, parent=None, id=-1, title=None): 
        wx.Frame.__init__(self, parent, id, title) 
        self.panel = wx.Panel(self, size=(350, 200)) 
        self.panel.Bind(wx.EVT_PAINT, self.on_paint) 
        self.Fit() 

    def on_paint(self, event):
        # establish the painting surface
        dc = wx.PaintDC(self.panel)
        dc.SetPen(wx.Pen('blue', 4))
        # draw a blue line (thickness = 4)
        dc.DrawLine(50, 20, 300, 20)
        dc.SetPen(wx.Pen('red', 1))
        # draw a red rounded-rectangle
        rect = wx.Rect(50, 50, 100, 100) 
        dc.DrawRoundedRectangleRect(rect, 8)
        # draw a red circle with yellow fill
        dc.SetBrush(wx.Brush('yellow'))
        x = 250
        y = 100
        r = 50
        dc.DrawCircle(x, y, r)


# test it ...
app = wx.PySimpleApp() 
frame1 = MyFrame(title='rounded-rectangle & circle') 
frame1.Center() 
frame1.Show() 
app.MainLoop()

Dani AI

Generated

Nice, compact demo from — it shows the essentials. Two practical additions that make this pattern robust in real programs: use offscreen buffering to avoid flicker, and prefer the graphics API for antialiased drawing and transforms. The sketch below shows the common buffered-paint pattern and when to rebuild the offscreen bitmap.

# common pattern (inside your frame/panel)
self.panel.SetBackgroundStyle(wx.BG_STYLE_PAINT)
self.buffer = wx.Bitmap(1,1)
self.panel.Bind(wx.EVT_SIZE, self.on_size)
self.panel.Bind(wx.EVT_PAINT, self.on_paint)

def on_size(self, event):
    w,h = self.panel.GetClientSize()
    if w > 0 and h > 0:
        self.buffer = wx.Bitmap(w, h)
        self._render_offscreen()
    event.Skip()

def _render_offscreen(self):
    mdc = wx.MemoryDC(self.buffer)
    mdc.Clear()
    # draw static content onto mdc here (pens/brushes/fonts)
    del mdc

def on_paint(self, event):
    wx.BufferedPaintDC(self.panel, self.buffer)   # blit the prepared buffer

Practical tips:

  • Always create a wx.PaintDC (or a buffered variant) only inside an EVT_PAINT handler; for immediate-on-demand drawing use wx.ClientDC but keep persistent state so a later repaint can redraw the same content.
  • Reuse pens/brushes/bitmaps instead of recreating them every paint; rebuild the offscreen bitmap only when the size or the static content changes.
  • For smoother, scalable artwork use wx.GraphicsContext (wx.GraphicsContext.Create(dc)) when available — it gives antialiasing, transforms and device-independent drawing.
  • Keep paint handlers fast: do heavy work on a worker thread and call Refresh/CallAfter to update the buffer on the GUI thread.

These steps preserve the simplicity of the original example while making it suitable for real applications and cross-platform use.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.