Introduction

The following sample was sentto the maillist by Jean-Michel Fauth who hopes that it can be useful to others as they learn how to use wxPython.

Code Sample

   1 #-------------------------------------------------------------------
   2 # CommentedDrawing.py
   3 # Development: windows 98se, Python 2.2.3, wxPython 2.4.0.7
   4 # also ok under wxPython 2.4.1.2
   5 # 17 July 2003
   6 # Jean-Michel Fauth, Switzerland
   7 #-------------------------------------------------------------------
   8 
   9 from wxPython.wx import *
  10 from time import asctime
  11 
  12 #-------------------------------------------------------------------
  13 
  14 def jmtime():
  15     return '[' + asctime()[11:19] + '] '
  16 
  17 #-------------------------------------------------------------------
  18 
  19 # The control/widget containing the drawing. It is white and has no border. It
  20 # is not necessary to defined its positon and size, since these parameters are
  21 # set up by the layout constraints mechanism. However, I forced the control
  22 # to have no border.
  23 class MyDrawingArea(wxWindow):
  24 
  25     def __init__(self, parent, id):
  26         sty = wxNO_BORDER
  27         wxWindow.__init__(self, parent, id, style=sty)
  28         self.parent = parent
  29         self.SetBackgroundColour(wxWHITE)
  30         self.SetCursor(wxCROSS_CURSOR)
  31 
  32         # Some initalisation, just to reminds the user that a variable
  33         # called self.BufferBmp exists. See self.OnSize().
  34         self.BufferBmp = None
  35 
  36         EVT_SIZE(self, self.OnSize)
  37         EVT_PAINT(self, self.OnPaint)
  38 
  39 
  40     # OnSize is fired at the application start or when the frame is resized.
  41     # The OnSize event is fired BEFORE the OnPaint event. wxWindows
  42     # handles the events in this order. This is not due to the fact,
  43     # that the code line EVT_SIZE(...) is placed before the line
  44     # EVT_PAINT(...).
  45     # The procedure OnSize() is the right place to define the
  46     # BufferBmp and its size. self.BufferBmp is the picture in memory,
  47     # which contains your drawing. self.BufferBmp is also used as a flag,
  48     # a None value indicates no picture.
  49     #
  50     def OnSize(self, event):
  51         print jmtime() + 'OnSize in MyDrawingArea'
  52         # Get the size of the drawing area in pixels.
  53         self.wi, self.he = self.GetSizeTuple()
  54         # Create BufferBmp and set the same size as the drawing area.
  55         self.BufferBmp = wxEmptyBitmap(self.wi, self.he)
  56         memdc = wxMemoryDC()
  57         memdc.SelectObject(self.BufferBmp)
  58         # Drawing job
  59         ret = self.DoSomeDrawing(memdc)
  60         if not ret:  #error
  61             self.BufferBmp = None
  62             wxMessageBox('Error in drawing', 'CommentedDrawing', wxOK | wxICON_EXCLAMATION)
  63 
  64 
  65     # OnPaint is executed at the app start, when resizing or when
  66     # the application windows becomes active. OnPaint copies the
  67     # buffered picture, instead of preparing (again) the drawing job.
  68     # This is the trick, copying is very fast.
  69     # Note: if you forget to define the dc in this procedure,
  70     # (no dc = ... code line), the application will run in
  71     # an infinite loop. This is a common beginner's error. (I think)
  72     # BeginDrawing() and EndDrawing() are for windows platforms (see doc).
  73     def OnPaint(self, event):
  74         print jmtime() + 'OnPaint in MyDrawingArea'
  75         dc = wxPaintDC(self)
  76         dc.BeginDrawing()
  77         if self.BufferBmp != None:
  78             print jmtime() + '...drawing'
  79             dc.DrawBitmap(self.BufferBmp, 0, 0, True)
  80         else:
  81             print jmtime() + '...nothing to draw'
  82         dc.EndDrawing()
  83 
  84 
  85     # The function defines the drawing job. Everything is drawn on the dc.
  86     # In that application, the dc corresponds to the BufferBmp.
  87     # Three things are drawn, a square with a fixed size, and two
  88     # rectangles with sizes determined by the size of the dc, that
  89     # means the size of the drawing area. Keep in mind, the size
  90     # of the drawing area depends on the size of the main frame,
  91     # which can be resized on the fly with your mouse.
  92     # At this point, I will introduce a small complication, that is
  93     # in fact quite practical. It may happen, the drawing is not
  94     # working correctly. Either there is an error in the drawing job
  95     # or the data you want to plot can not be drawn correctly. A typical
  96     # example is the plotting of 'scientific data'. The data are not (or
  97     # may not be) scaled correctly, that leads to errors, generally integer
  98     # overflow errors.
  99     # To circumvent this, the procedure returns True, if the drawing succeed.
 100     # It returns False, if the drawing fails. The returned value may be used
 101     # later.
 102     def DoSomeDrawing(self, dc):
 103         try:
 104             print jmtime() + 'DoSomeDrawing in MyDrawingArea'
 105 
 106             dc.BeginDrawing()
 107 
 108             #~ raise OverflowError #for test
 109 
 110             # Clear everything
 111             dc.SetBrush(wxBrush(wxWHITE, wxSOLID))
 112             dc.Clear()
 113 
 114             # Draw the square with a fixed size.
 115             dc.SetBrush(wxBrush(wxCYAN, wxSOLID))
 116             dc.SetPen(wxPen(wxBLUE, 1, wxSOLID))
 117             dc.DrawRectangle(10, 10, 200, 200)
 118 
 119             # Draw a transparent rectangle with a red border, proportional to
 120             # the dc size.
 121             dcwi, dche = dc.GetSizeTuple()
 122             dc.SetBrush(wxBrush(wxCYAN, wxTRANSPARENT))
 123             dc.SetPen(wxPen(wxRED, 1, wxSOLID))
 124             dc.DrawRectangle(0, 0, dcwi, dche)
 125 
 126             # Draw one another rectangle, a rectangle with a size proportional
 127             # to the dc size.
 128             gap = 50
 129             dc.SetBrush(wxBrush(wxWHITE, wxTRANSPARENT))
 130             dc.SetPen(wxPen(wxBLACK, 1, wxSOLID))
 131             dc.DrawRectangle(0 + gap, 0 + gap, dcwi - 2 * gap, dche - 2 * gap)
 132 
 133             # These next 2 lines will raise an overflow error.
 134             #~ largeval = 1e10
 135             #~ dc.DrawLine(dcwi // 2, dche // 2, largeval, largeval)
 136 
 137             dc.EndDrawing()
 138             return True
 139 
 140         except:
 141             return False
 142 
 143 #-------------------------------------------------------------------
 144 
 145 # Panel in the main frame. It covers automatically the client area of
 146 # its parent frame. The panel contains a single control (class MyDrawingArea),
 147 # on which the drawing takes place. The position and size of this control is
 148 # set up with layout constraints, so that the user see what happens to the
 149 # drawing when the main frame is resized.
 150 class MyPanel(wxPanel):
 151 
 152     def __init__(self, parent, id):
 153         wxPanel.__init__(self, parent, id, wxDefaultPosition, wxDefaultSize)
 154 
 155         self.drawingarea = MyDrawingArea(self, -1)
 156 
 157         self.SetAutoLayout(True)
 158 
 159         gap = 30 #in pixels
 160         lc = wxLayoutConstraints()
 161         lc.top.SameAs(self, wxTop, gap)
 162         lc.left.SameAs(self, wxLeft, gap)
 163         lc.right.SameAs(self, wxWidth, gap)
 164         lc.bottom.SameAs(self, wxBottom, gap)
 165         self.drawingarea.SetConstraints(lc)
 166 
 167 #-------------------------------------------------------------------
 168 
 169 # Usual frame. Can be resized, maximized and minimized.
 170 # The frame contains one panel.
 171 class MyFrame(wxFrame):
 172 
 173     def __init__(self, parent, id):
 174         wxFrame.__init__(self, parent, id, 'CommentedDrawing', wxPoint(0, 0), wxSize(500, 400))
 175         self.panel = MyPanel(self, -1)
 176 
 177         EVT_CLOSE(self, self.OnCloseWindow)
 178 
 179     def OnCloseWindow(self, event):
 180         print jmtime() + 'OnCloseWindow in MyFrame'
 181         self.Destroy()
 182 
 183 #-------------------------------------------------------------------
 184 
 185 class MyApp(wxApp):
 186 
 187     def OnInit(self):
 188         frame = MyFrame(None, -1)
 189         frame.Show(True)
 190         self.SetTopWindow(frame)
 191         return True
 192 
 193 #-------------------------------------------------------------------
 194 
 195 def main():
 196     print 'main is running...'
 197     app = MyApp(0)
 198     app.MainLoop()
 199 
 200 #-------------------------------------------------------------------
 201 
 202 if __name__ == "__main__" :
 203     main()
 204 
 205 #eof-------------------------------------------------------------------

Comments

YetAnotherDrawingSample (last edited 2008-03-11 10:50:23 by localhost)

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