]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/doodle/doodle.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[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.pos = wx.Point(0,0)
42 self.MakeMenu()
43
44 self.InitBuffer()
45
46 self.SetCursor(wx.StockCursor(wx.CURSOR_PENCIL))
47
48 # hook some mouse events
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)
53
54 # the window resize event and idle events for managing the buffer
55 self.Bind(wx.EVT_SIZE, self.OnSize)
56 self.Bind(wx.EVT_IDLE, self.OnIdle)
57
58 # and the refresh event
59 self.Bind(wx.EVT_PAINT, self.OnPaint)
60
61 # When the window is destroyed, clean up resources.
62 self.Bind(wx.EVT_WINDOW_DESTROY, self.Cleanup)
63
64
65 def Cleanup(self, evt):
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()
74 self.buffer = wx.EmptyBitmap(max(1,size.width), max(1,size.height))
75 dc = wx.BufferedDC(None, self.buffer)
76 dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
77 dc.Clear()
78 self.DrawLines(dc)
79 self.reInitBuffer = False
80
81
82 def SetColour(self, colour):
83 """Set a new colour and make a matching pen"""
84 self.colour = colour
85 self.pen = wx.Pen(self.colour, self.thickness, wx.SOLID)
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
92 self.pen = wx.Pen(self.colour, self.thickness, wx.SOLID)
93 self.Notify()
94
95
96 def GetLinesData(self):
97 return self.lines[:]
98
99
100 def SetLinesData(self, lines):
101 self.lines = lines[:]
102 self.InitBuffer()
103 self.Refresh()
104
105
106 def MakeMenu(self):
107 """Make a menu that can be popped up later"""
108 menu = wx.Menu()
109 keys = self.menuColours.keys()
110 keys.sort()
111 for k in keys:
112 text = self.menuColours[k]
113 menu.Append(k, text, kind=wx.ITEM_CHECK)
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)
116 menu.Break()
117
118 for x in range(1, self.maxThickness+1):
119 menu.Append(x, str(x), kind=wx.ITEM_CHECK)
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)
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:
131 event.Check(True)
132 event.SetText(text.upper())
133 else:
134 event.Check(False)
135 event.SetText(text)
136
137 def OnCheckMenuThickness(self, event):
138 if event.GetId() == self.thickness:
139 event.Check(True)
140 else:
141 event.Check(False)
142
143
144 def OnLeftDown(self, event):
145 """called when the left mouse button is pressed"""
146 self.curLine = []
147 self.pos = event.GetPosition()
148 self.CaptureMouse()
149
150
151 def OnLeftUp(self, event):
152 """called when the left mouse button is released"""
153 if self.HasCapture():
154 self.lines.append( (self.colour, self.thickness, self.curLine) )
155 self.curLine = []
156 self.ReleaseMouse()
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():
173 dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
174 dc.BeginDrawing()
175 dc.SetPen(self.pen)
176 pos = event.GetPosition()
177 coords = (self.pos.x, self.pos.y, pos.x, pos.y)
178 self.curLine.append(coords)
179 dc.DrawLine(*coords)
180 self.pos = pos
181 dc.EndDrawing()
182
183
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 """
189 self.reInitBuffer = True
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()
201 self.Refresh(False)
202
203
204 def OnPaint(self, event):
205 """
206 Called when the window is exposed.
207 """
208 # Create a buffered paint DC. It will create the real
209 # wx.PaintDC and then blit the bitmap to it when dc is
210 # deleted. Since we don't need to draw anything else
211 # here that's all there is to it.
212 dc = wx.BufferedPaintDC(self, self.buffer)
213
214
215 def DrawLines(self, dc):
216 """
217 Redraws all the lines that have been drawn already.
218 """
219 dc.BeginDrawing()
220 for colour, thickness, line in self.lines:
221 pen = wx.Pen(colour, thickness, wx.SOLID)
222 dc.SetPen(pen)
223 for coords in line:
224 dc.DrawLine(*coords)
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
249 class DoodleFrame(wx.Frame):
250 def __init__(self, parent):
251 wx.Frame.__init__(self, parent, -1, "Doodle Frame", size=(800,600),
252 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
253 doodle = DoodleWindow(self, -1)
254
255 #----------------------------------------------------------------------
256
257 if __name__ == '__main__':
258 app = wx.PySimpleApp()
259 frame = DoodleFrame(None)
260 frame.Show(True)
261 app.MainLoop()
262