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