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