]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/doodle/doodle.py
Use wxBufferedDC in the doodle sample
[wxWidgets.git] / wxPython / samples / doodle / doodle.py
1 # doodle.py
2
3 """
4 This module contains the DoodleWindow class which is a window that you
5 can do simple drawings upon.
6 """
7
8
9 from wxPython.wx import *
10
11 #----------------------------------------------------------------------
12
13 class DoodleWindow(wxWindow):
14 menuColours = { 100 : 'Black',
15 101 : 'Yellow',
16 102 : 'Red',
17 103 : 'Green',
18 104 : 'Blue',
19 105 : 'Purple',
20 106 : 'Brown',
21 107 : 'Aquamarine',
22 108 : 'Forest Green',
23 109 : 'Light Blue',
24 110 : 'Goldenrod',
25 111 : 'Cyan',
26 112 : 'Orange',
27 113 : 'Navy',
28 114 : 'Dark Grey',
29 115 : 'Light Grey',
30 }
31 maxThickness = 16
32
33
34 def __init__(self, parent, ID):
35 wxWindow.__init__(self, parent, ID, style=wxNO_FULL_REPAINT_ON_RESIZE)
36 self.SetBackgroundColour("WHITE")
37 self.listeners = []
38 self.thickness = 1
39 self.SetColour("Black")
40 self.lines = []
41 self.x = self.y = 0
42 self.MakeMenu()
43
44 self.InitBuffer()
45
46 # hook some mouse events
47 EVT_LEFT_DOWN(self, self.OnLeftDown)
48 EVT_LEFT_UP(self, self.OnLeftUp)
49 EVT_RIGHT_UP(self, self.OnRightUp)
50 EVT_MOTION(self, self.OnMotion)
51
52 # the window resize event and idle events for managing the buffer
53 EVT_SIZE(self, self.OnSize)
54 EVT_IDLE(self, self.OnIdle)
55
56 # and the refresh event
57 EVT_PAINT(self, self.OnPaint)
58
59
60 def __del__(self):
61 self.Cleanup()
62
63
64 def Cleanup(self):
65 if hasattr(self, "menu"):
66 self.menu.Destroy()
67 del self.menu
68
69
70 def InitBuffer(self):
71 """Initialize the bitmap used for buffering the display."""
72 size = self.GetClientSize()
73 self.buffer = wxEmptyBitmap(size.width, size.height)
74 dc = wxBufferedDC(None, self.buffer)
75 dc.SetBackground(wxBrush(self.GetBackgroundColour()))
76 dc.Clear()
77 self.DrawLines(dc)
78 self.reInitBuffer = false
79
80
81 def SetColour(self, colour):
82 """Set a new colour and make a matching pen"""
83 self.colour = colour
84 self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
85 self.Notify()
86
87
88 def SetThickness(self, num):
89 """Set a new line thickness and make a matching pen"""
90 self.thickness = num
91 self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
92 self.Notify()
93
94
95 def GetLinesData(self):
96 return self.lines[:]
97
98
99 def SetLinesData(self, lines):
100 self.lines = lines[:]
101 self.InitBuffer()
102 self.Refresh()
103
104
105 def MakeMenu(self):
106 """Make a menu that can be popped up later"""
107 menu = wxMenu()
108 keys = self.menuColours.keys()
109 keys.sort()
110 for k in keys:
111 text = self.menuColours[k]
112 menu.Append(k, text, kind=wxITEM_CHECK)
113 EVT_MENU_RANGE(self, 100, 200, self.OnMenuSetColour)
114 EVT_UPDATE_UI_RANGE(self, 100, 200, self.OnCheckMenuColours)
115 menu.Break()
116
117 for x in range(1, self.maxThickness+1):
118 menu.Append(x, str(x), kind=wxITEM_CHECK)
119 EVT_MENU_RANGE(self, 1, self.maxThickness, self.OnMenuSetThickness)
120 EVT_UPDATE_UI_RANGE(self, 1, self.maxThickness, self.OnCheckMenuThickness)
121 self.menu = menu
122
123
124 # These two event handlers are called before the menu is displayed
125 # to determine which items should be checked.
126 def OnCheckMenuColours(self, event):
127 text = self.menuColours[event.GetId()]
128 if text == self.colour:
129 event.Check(true)
130 else:
131 event.Check(false)
132 def OnCheckMenuThickness(self, event):
133 if event.GetId() == self.thickness:
134 event.Check(true)
135 else:
136 event.Check(false)
137
138
139 def OnLeftDown(self, event):
140 """called when the left mouse button is pressed"""
141 self.curLine = []
142 self.x, self.y = event.GetPositionTuple()
143 self.CaptureMouse()
144
145
146 def OnLeftUp(self, event):
147 """called when the left mouse button is released"""
148 if self.HasCapture():
149 self.lines.append( (self.colour, self.thickness, self.curLine) )
150 self.curLine = []
151 self.ReleaseMouse()
152
153
154 def OnRightUp(self, event):
155 """called when the right mouse button is released, will popup the menu"""
156 pt = event.GetPosition()
157 self.PopupMenu(self.menu, pt)
158
159
160
161 def OnMotion(self, event):
162 """
163 Called when the mouse is in motion. If the left button is
164 dragging then draw a line from the last event position to the
165 current one. Save the coordinants for redraws.
166 """
167 if event.Dragging() and event.LeftIsDown():
168 dc = wxBufferedDC(wxClientDC(self), self.buffer)
169 dc.BeginDrawing()
170 dc.SetPen(self.pen)
171 pos = event.GetPositionTuple()
172 coords = (self.x, self.y) + pos
173 self.curLine.append(coords)
174 dc.DrawLine(self.x, self.y, pos[0], pos[1])
175 self.x, self.y = pos
176 dc.EndDrawing()
177
178
179 def OnSize(self, event):
180 """
181 Called when the window is resized. We set a flag so the idle
182 handler will resize the buffer.
183 """
184 self.reInitBuffer = true
185
186
187 def OnIdle(self, event):
188 """
189 If the size was changed then resize the bitmap used for double
190 buffering to match the window size. We do it in Idle time so
191 there is only one refresh after resizing is done, not lots while
192 it is happening.
193 """
194 if self.reInitBuffer:
195 self.InitBuffer()
196 self.Refresh(FALSE)
197
198
199 def OnPaint(self, event):
200 """
201 Called when the window is exposed.
202 """
203 # Create a buffered paint DC. It will create the real
204 # wxPaintDC and then blit the bitmap to it when dc is
205 # deleted. Since we don't need to draw anything else
206 # here that's all there is to it.
207 dc = wxBufferedPaintDC(self, self.buffer)
208
209
210 def DrawLines(self, dc):
211 """
212 Redraws all the lines that have been drawn already.
213 """
214 dc.BeginDrawing()
215 for colour, thickness, line in self.lines:
216 pen = wxPen(wxNamedColour(colour), thickness, wxSOLID)
217 dc.SetPen(pen)
218 for coords in line:
219 apply(dc.DrawLine, coords)
220 dc.EndDrawing()
221
222
223 # Event handlers for the popup menu, uses the event ID to determine
224 # the colour or the thickness to set.
225 def OnMenuSetColour(self, event):
226 self.SetColour(self.menuColours[event.GetId()])
227
228 def OnMenuSetThickness(self, event):
229 self.SetThickness(event.GetId())
230
231
232 # Observer pattern. Listeners are registered and then notified
233 # whenever doodle settings change.
234 def AddListener(self, listener):
235 self.listeners.append(listener)
236
237 def Notify(self):
238 for other in self.listeners:
239 other.Update(self.colour, self.thickness)
240
241
242 #----------------------------------------------------------------------
243
244 class DoodleFrame(wxFrame):
245 def __init__(self, parent):
246 wxFrame.__init__(self, parent, -1, "Doodle Frame", size=(800,600),
247 style=wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE)
248 doodle = DoodleWindow(self, -1)
249
250 #----------------------------------------------------------------------
251
252 if __name__ == '__main__':
253 app = wxPySimpleApp()
254 frame = DoodleFrame(None)
255 frame.Show(true)
256 app.MainLoop()
257