]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/doodle/doodle.py
It *does* work now
[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 import wx # This module uses the new wx namespace
10
11 #----------------------------------------------------------------------
12
13 class DoodleWindow(wx.Window):
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 wx.Window.__init__(self, parent, ID, style=wx.NO_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 wx.EVT_LEFT_DOWN(self, self.OnLeftDown)
48 wx.EVT_LEFT_UP(self, self.OnLeftUp)
49 wx.EVT_RIGHT_UP(self, self.OnRightUp)
50 wx.EVT_MOTION(self, self.OnMotion)
51
52 # the window resize event and idle events for managing the buffer
53 wx.EVT_SIZE(self, self.OnSize)
54 wx.EVT_IDLE(self, self.OnIdle)
55
56 # and the refresh event
57 wx.EVT_PAINT(self, self.OnPaint)
58
59 # When the window is destroyed, clean up resources.
60 wx.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 = wx.EmptyBitmap(size.width, size.height)
73 dc = wx.BufferedDC(None, self.buffer)
74 dc.SetBackground(wx.Brush(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 = wx.Pen(self.colour, self.thickness, wx.SOLID)
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 = wx.Pen(self.colour, self.thickness, wx.SOLID)
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 = wx.Menu()
107 keys = self.menuColours.keys()
108 keys.sort()
109 for k in keys:
110 text = self.menuColours[k]
111 menu.Append(k, text, kind=wx.ITEM_CHECK)
112 wx.EVT_MENU_RANGE(self, 100, 200, self.OnMenuSetColour)
113 wx.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=wx.ITEM_CHECK)
118 wx.EVT_MENU_RANGE(self, 1, self.maxThickness, self.OnMenuSetThickness)
119 wx.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 event.SetText(text.upper())
130 else:
131 event.Check(False)
132 event.SetText(text)
133
134 def OnCheckMenuThickness(self, event):
135 if event.GetId() == self.thickness:
136 event.Check(True)
137 else:
138 event.Check(False)
139
140
141 def OnLeftDown(self, event):
142 """called when the left mouse button is pressed"""
143 self.curLine = []
144 self.x, self.y = event.GetPositionTuple()
145 self.CaptureMouse()
146
147
148 def OnLeftUp(self, event):
149 """called when the left mouse button is released"""
150 if self.HasCapture():
151 self.lines.append( (self.colour, self.thickness, self.curLine) )
152 self.curLine = []
153 self.ReleaseMouse()
154
155
156 def OnRightUp(self, event):
157 """called when the right mouse button is released, will popup the menu"""
158 pt = event.GetPosition()
159 self.PopupMenu(self.menu, pt)
160
161
162
163 def OnMotion(self, event):
164 """
165 Called when the mouse is in motion. If the left button is
166 dragging then draw a line from the last event position to the
167 current one. Save the coordinants for redraws.
168 """
169 if event.Dragging() and event.LeftIsDown():
170 dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
171 dc.BeginDrawing()
172 dc.SetPen(self.pen)
173 pos = event.GetPositionTuple()
174 coords = (self.x, self.y) + pos
175 self.curLine.append(coords)
176 dc.DrawLine(self.x, self.y, pos[0], pos[1])
177 self.x, self.y = pos
178 dc.EndDrawing()
179
180
181 def OnSize(self, event):
182 """
183 Called when the window is resized. We set a flag so the idle
184 handler will resize the buffer.
185 """
186 self.reInitBuffer = True
187
188
189 def OnIdle(self, event):
190 """
191 If the size was changed then resize the bitmap used for double
192 buffering to match the window size. We do it in Idle time so
193 there is only one refresh after resizing is done, not lots while
194 it is happening.
195 """
196 if self.reInitBuffer:
197 self.InitBuffer()
198 self.Refresh(False)
199
200
201 def OnPaint(self, event):
202 """
203 Called when the window is exposed.
204 """
205 # Create a buffered paint DC. It will create the real
206 # wx.PaintDC and then blit the bitmap to it when dc is
207 # deleted. Since we don't need to draw anything else
208 # here that's all there is to it.
209 dc = wx.BufferedPaintDC(self, self.buffer)
210
211
212 def DrawLines(self, dc):
213 """
214 Redraws all the lines that have been drawn already.
215 """
216 dc.BeginDrawing()
217 for colour, thickness, line in self.lines:
218 pen = wx.Pen(colour, thickness, wx.SOLID)
219 dc.SetPen(pen)
220 for coords in line:
221 apply(dc.DrawLine, coords)
222 dc.EndDrawing()
223
224
225 # Event handlers for the popup menu, uses the event ID to determine
226 # the colour or the thickness to set.
227 def OnMenuSetColour(self, event):
228 self.SetColour(self.menuColours[event.GetId()])
229
230 def OnMenuSetThickness(self, event):
231 self.SetThickness(event.GetId())
232
233
234 # Observer pattern. Listeners are registered and then notified
235 # whenever doodle settings change.
236 def AddListener(self, listener):
237 self.listeners.append(listener)
238
239 def Notify(self):
240 for other in self.listeners:
241 other.Update(self.colour, self.thickness)
242
243
244 #----------------------------------------------------------------------
245
246 class DoodleFrame(wx.Frame):
247 def __init__(self, parent):
248 wx.Frame.__init__(self, parent, -1, "Doodle Frame", size=(800,600),
249 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
250 doodle = DoodleWindow(self, -1)
251
252 #----------------------------------------------------------------------
253
254 if __name__ == '__main__':
255 app = wx.PySimpleApp()
256 frame = DoodleFrame(None)
257 frame.Show(True)
258 app.MainLoop()
259