4 This module contains the DoodleWindow class which is a window that you
5 can do simple drawings upon.
9 import wx
# This module uses the new wx namespace
11 #----------------------------------------------------------------------
13 class DoodleWindow(wx
.Window
):
14 menuColours
= { 100 : 'Black',
34 def __init__(self
, parent
, ID
):
35 wx
.Window
.__init
__(self
, parent
, ID
, style
=wx
.NO_FULL_REPAINT_ON_RESIZE
)
36 self
.SetBackgroundColour("WHITE")
39 self
.SetColour("Black")
41 self
.pos
= wx
.Point(0,0)
46 self
.SetCursor(wx
.StockCursor(wx
.CURSOR_PENCIL
))
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
)
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
)
58 # and the refresh event
59 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
61 # When the window is destroyed, clean up resources.
62 self
.Bind(wx
.EVT_WINDOW_DESTROY
, self
.Cleanup
)
65 def Cleanup(self
, evt
):
66 if hasattr(self
, "menu"):
72 """Initialize the bitmap used for buffering the display."""
73 size
= self
.GetClientSize()
74 self
.buffer = wx
.EmptyBitmap(size
.width
, size
.height
)
75 dc
= wx
.BufferedDC(None, self
.buffer)
76 dc
.SetBackground(wx
.Brush(self
.GetBackgroundColour()))
79 self
.reInitBuffer
= False
82 def SetColour(self
, colour
):
83 """Set a new colour and make a matching pen"""
85 self
.pen
= wx
.Pen(self
.colour
, self
.thickness
, wx
.SOLID
)
89 def SetThickness(self
, num
):
90 """Set a new line thickness and make a matching pen"""
92 self
.pen
= wx
.Pen(self
.colour
, self
.thickness
, wx
.SOLID
)
96 def GetLinesData(self
):
100 def SetLinesData(self
, lines
):
101 self
.lines
= lines
[:]
107 """Make a menu that can be popped up later"""
109 keys
= self
.menuColours
.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)
118 for x
in range(1, self
.maxThickness
+1):
119 menu
.Append(x
, str(x
), kind
=wx
.ITEM_CHECK
)
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
)
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
:
132 event
.SetText(text
.upper())
137 def OnCheckMenuThickness(self
, event
):
138 if event
.GetId() == self
.thickness
:
144 def OnLeftDown(self
, event
):
145 """called when the left mouse button is pressed"""
147 self
.pos
= event
.GetPosition()
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
) )
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
)
166 def OnMotion(self
, event
):
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.
172 if event
.Dragging() and event
.LeftIsDown():
173 dc
= wx
.BufferedDC(wx
.ClientDC(self
), self
.buffer)
176 pos
= event
.GetPosition()
177 coords
= (self
.pos
.x
, self
.pos
.y
, pos
.x
, pos
.y
)
178 self
.curLine
.append(coords
)
184 def OnSize(self
, event
):
186 Called when the window is resized. We set a flag so the idle
187 handler will resize the buffer.
189 self
.reInitBuffer
= True
192 def OnIdle(self
, event
):
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
199 if self
.reInitBuffer
:
204 def OnPaint(self
, event
):
206 Called when the window is exposed.
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)
215 def DrawLines(self
, dc
):
217 Redraws all the lines that have been drawn already.
220 for colour
, thickness
, line
in self
.lines
:
221 pen
= wx
.Pen(colour
, thickness
, wx
.SOLID
)
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()])
233 def OnMenuSetThickness(self
, event
):
234 self
.SetThickness(event
.GetId())
237 # Observer pattern. Listeners are registered and then notified
238 # whenever doodle settings change.
239 def AddListener(self
, listener
):
240 self
.listeners
.append(listener
)
243 for other
in self
.listeners
:
244 other
.Update(self
.colour
, self
.thickness
)
247 #----------------------------------------------------------------------
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)
255 #----------------------------------------------------------------------
257 if __name__
== '__main__':
258 app
= wx
.PySimpleApp()
259 frame
= DoodleFrame(None)