]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/doodle/doodle.py
Allow for multiple CHM controllers in app (don't leak dynamic libs)
[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 # When the window is destroyed, clean up resources.
60 EVT_WINDOW_DESTROY(self, self.Cleanup)
61
62
63 def Cleanup(self, evt):
64 if hasattr(self, "menu"):
65 self.menu.Destroy()
66 del self.menu
67
68
69 def InitBuffer(self):
70 """Initialize the bitmap used for buffering the display."""
71 size = self.GetClientSize()
72 self.buffer = wxEmptyBitmap(size.width, size.height)
73 dc = wxBufferedDC(None, self.buffer)
74 dc.SetBackground(wxBrush(self.GetBackgroundColour()))
75 dc.Clear()
76 self.DrawLines(dc)
77 self.reInitBuffer = False
78
79
80 def SetColour(self, colour):
81 """Set a new colour and make a matching pen"""
82 self.colour = colour
83 self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
84 self.Notify()
85
86
87 def SetThickness(self, num):
88 """Set a new line thickness and make a matching pen"""
89 self.thickness = num
90 self.pen = wxPen(wxNamedColour(self.colour), self.thickness, wxSOLID)
91 self.Notify()
92
93
94 def GetLinesData(self):
95 return self.lines[:]
96
97
98 def SetLinesData(self, lines):
99 self.lines = lines[:]
100 self.InitBuffer()
101 self.Refresh()
102
103
104 def MakeMenu(self):
105 """Make a menu that can be popped up later"""
106 menu = wxMenu()
107 keys = self.menuColours.keys()
108 keys.sort()
109 for k in keys:
110 text = self.menuColours[k]
111 menu.Append(k, text, kind=wxITEM_CHECK)
112 EVT_MENU_RANGE(self, 100, 200, self.OnMenuSetColour)
113 EVT_UPDATE_UI_RANGE(self, 100, 200, self.OnCheckMenuColours)
114 menu.Break()
115
116 for x in range(1, self.maxThickness+1):
117 menu.Append(x, str(x), kind=wxITEM_CHECK)
118 EVT_MENU_RANGE(self, 1, self.maxThickness, self.OnMenuSetThickness)
119 EVT_UPDATE_UI_RANGE(self, 1, self.maxThickness, self.OnCheckMenuThickness)
120 self.menu = menu
121
122
123 # These two event handlers are called before the menu is displayed
124 # to determine which items should be checked.
125 def OnCheckMenuColours(self, event):
126 text = self.menuColours[event.GetId()]
127 if text == self.colour:
128 event.Check(True)
129 else:
130 event.Check(False)
131 def OnCheckMenuThickness(self, event):
132 if event.GetId() == self.thickness:
133 event.Check(True)
134 else:
135 event.Check(False)
136
137
138 def OnLeftDown(self, event):
139 """called when the left mouse button is pressed"""
140 self.curLine = []
141 self.x, self.y = event.GetPositionTuple()
142 self.CaptureMouse()
143
144
145 def OnLeftUp(self, event):
146 """called when the left mouse button is released"""
147 if self.HasCapture():
148 self.lines.append( (self.colour, self.thickness, self.curLine) )
149 self.curLine = []
150 self.ReleaseMouse()
151
152
153 def OnRightUp(self, event):
154 """called when the right mouse button is released, will popup the menu"""
155 pt = event.GetPosition()
156 self.PopupMenu(self.menu, pt)
157
158
159
160 def OnMotion(self, event):
161 """
162 Called when the mouse is in motion. If the left button is
163 dragging then draw a line from the last event position to the
164 current one. Save the coordinants for redraws.
165 """
166 if event.Dragging() and event.LeftIsDown():
167 dc = wxBufferedDC(wxClientDC(self), self.buffer)
168 dc.BeginDrawing()
169 dc.SetPen(self.pen)
170 pos = event.GetPositionTuple()
171 coords = (self.x, self.y) + pos
172 self.curLine.append(coords)
173 dc.DrawLine(self.x, self.y, pos[0], pos[1])
174 self.x, self.y = pos
175 dc.EndDrawing()
176
177
178 def OnSize(self, event):
179 """
180 Called when the window is resized. We set a flag so the idle
181 handler will resize the buffer.
182 """
183 self.reInitBuffer = True
184
185
186 def OnIdle(self, event):
187 """
188 If the size was changed then resize the bitmap used for double
189 buffering to match the window size. We do it in Idle time so
190 there is only one refresh after resizing is done, not lots while
191 it is happening.
192 """
193 if self.reInitBuffer:
194 self.InitBuffer()
195 self.Refresh(False)
196
197
198 def OnPaint(self, event):
199 """
200 Called when the window is exposed.
201 """
202 # Create a buffered paint DC. It will create the real
203 # wxPaintDC and then blit the bitmap to it when dc is
204 # deleted. Since we don't need to draw anything else
205 # here that's all there is to it.
206 dc = wxBufferedPaintDC(self, self.buffer)
207
208
209 def DrawLines(self, dc):
210 """
211 Redraws all the lines that have been drawn already.
212 """
213 dc.BeginDrawing()
214 for colour, thickness, line in self.lines:
215 pen = wxPen(wxNamedColour(colour), thickness, wxSOLID)
216 dc.SetPen(pen)
217 for coords in line:
218 apply(dc.DrawLine, coords)
219 dc.EndDrawing()
220
221
222 # Event handlers for the popup menu, uses the event ID to determine
223 # the colour or the thickness to set.
224 def OnMenuSetColour(self, event):
225 self.SetColour(self.menuColours[event.GetId()])
226
227 def OnMenuSetThickness(self, event):
228 self.SetThickness(event.GetId())
229
230
231 # Observer pattern. Listeners are registered and then notified
232 # whenever doodle settings change.
233 def AddListener(self, listener):
234 self.listeners.append(listener)
235
236 def Notify(self):
237 for other in self.listeners:
238 other.Update(self.colour, self.thickness)
239
240
241 #----------------------------------------------------------------------
242
243 class DoodleFrame(wxFrame):
244 def __init__(self, parent):
245 wxFrame.__init__(self, parent, -1, "Doodle Frame", size=(800,600),
246 style=wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE)
247 doodle = DoodleWindow(self, -1)
248
249 #----------------------------------------------------------------------
250
251 if __name__ == '__main__':
252 app = wxPySimpleApp()
253 frame = DoodleFrame(None)
254 frame.Show(True)
255 app.MainLoop()
256