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)