4 This module implements the SuperDoodle demo application.  It takes the 
   5 DoodleWindow previously presented and reuses it in a much more 
   6 intelligent Frame.  This one has a menu and a statusbar, is able to 
   7 save and reload doodles, clear the workspace, and has a simple control 
   8 panel for setting color and line thickness in addition to the popup 
   9 menu that DoodleWindow provides.  There is also a nice About dialog 
  10 implmented using an wxHtmlWindow. 
  13 from wxPython
.wx 
import * 
  14 from doodle 
import DoodleWindow
 
  19 #---------------------------------------------------------------------- 
  30 class DoodleFrame(wxFrame
): 
  32     A DoodleFrame contains a DoodleWindow and a ControlPanel and manages 
  33     their layout with a wxBoxSizer.  A menu and associated event handlers 
  34     provides for saving a doodle to a file, etc. 
  37     def __init__(self
, parent
): 
  38         wxFrame
.__init
__(self
, parent
, -1, self
.title
, size
=(800,600)) 
  39         self
.CreateStatusBar() 
  43         self
.doodle 
= DoodleWindow(self
, -1) 
  44         cPanel 
= ControlPanel(self
, -1, self
.doodle
) 
  46         # Create a sizer to layout the two windows side-by-side. 
  47         # Both will grow vertically, the doodle window will grow 
  48         # horizontally as well. 
  49         box 
= wxBoxSizer(wxHORIZONTAL
) 
  50         box
.Add(cPanel
, 0, wxEXPAND
) 
  51         box
.Add(self
.doodle
, 1, wxEXPAND
) 
  53         # Tell the frame that it should layout itself in response to 
  55         self
.SetAutoLayout(true
) 
  61             data 
= self
.doodle
.GetLinesData() 
  62             f 
= open(self
.filename
, 'w') 
  70                 f 
= open(self
.filename
, 'r') 
  71                 data 
= cPickle
.load(f
) 
  73                 self
.doodle
.SetLinesData(data
) 
  74             except cPickle
.UnpicklingError
: 
  75                 wxMessageBox("%s is not a doodle file." % self
.filename
, 
  76                              "oops!", style
=wxOK|wxICON_EXCLAMATION
) 
  80         # create the file menu 
  82         menu1
.Append(idOPEN
, "&Open", "Open a doodle file") 
  83         menu1
.Append(idSAVE
, "&Save", "Save the doodle") 
  84         menu1
.Append(idSAVEAS
, "Save &As", "Save the doodle in a new file") 
  85         menu1
.AppendSeparator() 
  86         menu1
.Append(idCLEAR
, "&Clear", "Clear the current doodle") 
  87         menu1
.AppendSeparator() 
  88         menu1
.Append(idEXIT
, "E&xit", "Terminate the application") 
  92         menu2
.Append(idABOUT
, "&About", "Display the gratuitous 'about this app' thingamajig") 
  94         # and add them to a menubar 
  96         menuBar
.Append(menu1
, "&File") 
  97         menuBar
.Append(menu2
, "&Help") 
  98         self
.SetMenuBar(menuBar
) 
 100         EVT_MENU(self
, idOPEN
,   self
.OnMenuOpen
) 
 101         EVT_MENU(self
, idSAVE
,   self
.OnMenuSave
) 
 102         EVT_MENU(self
, idSAVEAS
, self
.OnMenuSaveAs
) 
 103         EVT_MENU(self
, idCLEAR
,  self
.OnMenuClear
) 
 104         EVT_MENU(self
, idEXIT
,   self
.OnMenuExit
) 
 105         EVT_MENU(self
, idABOUT
,  self
.OnMenuAbout
) 
 109     wildcard 
= "Doodle files (*.ddl)|*.ddl|All files (*.*)|*.*" 
 111     def OnMenuOpen(self
, event
): 
 112         dlg 
= wxFileDialog(self
, "Open doodle file...", os
.getcwd(), 
 113                            style
=wxOPEN
, wildcard 
= self
.wildcard
) 
 114         if dlg
.ShowModal() == wxID_OK
: 
 115             self
.filename 
= dlg
.GetPath() 
 117             self
.SetTitle(self
.title 
+ ' -- ' + self
.filename
) 
 121     def OnMenuSave(self
, event
): 
 122         if not self
.filename
: 
 123             self
.OnMenuSaveAs(event
) 
 128     def OnMenuSaveAs(self
, event
): 
 129         dlg 
= wxFileDialog(self
, "Save doodle as...", os
.getcwd(), 
 130                            style
=wxSAVE | wxOVERWRITE_PROMPT
, 
 131                            wildcard 
= self
.wildcard
) 
 132         if dlg
.ShowModal() == wxID_OK
: 
 133             filename 
= dlg
.GetPath() 
 134             if not os
.path
.splitext(filename
)[1]: 
 135                 filename 
= filename 
+ '.ddl' 
 136             self
.filename 
= filename
 
 138             self
.SetTitle(self
.title 
+ ' -- ' + self
.filename
) 
 142     def OnMenuClear(self
, event
): 
 143         self
.doodle
.SetLinesData([]) 
 144         self
.SetTitle(self
.title
) 
 147     def OnMenuExit(self
, event
): 
 151     def OnMenuAbout(self
, event
): 
 152         dlg 
= DoodleAbout(self
) 
 158 #---------------------------------------------------------------------- 
 161 class ControlPanel(wxPanel
): 
 163     This class implements a very simple control panel for the DoodleWindow. 
 164     It creates buttons for each of the colours and thickneses supported by 
 165     the DoodleWindow, and event handlers to set the selected values.  There is 
 166     also a little window that shows an example doodleLine in the selected 
 167     values.  Nested sizers are used for layout. 
 169     def __init__(self
, parent
, ID
, doodle
): 
 170         wxPanel
.__init
__(self
, parent
, ID
, style
=wxRAISED_BORDER
) 
 175         # Make a grid of buttons for each colour.  Attach each button 
 176         # event to self.OnSetColour.  The button ID is the same as the 
 177         # key in the colour dictionary. 
 178         colours 
= doodle
.menuColours
 
 179         keys 
= colours
.keys() 
 181         cGrid 
= wxGridSizer(cols
=numCols
, hgap
=2, vgap
=2) 
 183             bmp 
= self
.MakeBitmap(wxNamedColour(colours
[k
])) 
 184             b 
= wxBitmapButton(self
, k
, bmp
) 
 185             EVT_BUTTON(self
, k
, self
.OnSetColour
) 
 188         # Save the button size so we can use it for the number buttons 
 189         btnSize 
= b
.GetSize() 
 191         # Make a grid of buttons for the thicknesses.  Attach each button 
 192         # event to self.OnSetThickness.  The button ID is the same as the 
 194         tGrid 
= wxGridSizer(cols
=numCols
, hgap
=2, vgap
=2) 
 195         for x 
in range(1, doodle
.maxThickness
+1): 
 196             b 
= wxButton(self
, x
, str(x
), size
=btnSize
) 
 197             EVT_BUTTON(self
, x
, self
.OnSetThickness
) 
 200         # Make a colour indicator window, it is registerd as a listener 
 201         # with the doodle window so it will be notified when the settings 
 203         ci 
= ColourIndicator(self
) 
 204         doodle
.AddListener(ci
) 
 208         # Make a box sizer and put the two grids and the indicator 
 210         box 
= wxBoxSizer(wxVERTICAL
) 
 211         box
.Add(cGrid
, 0, wxALL
, spacing
) 
 212         box
.Add(tGrid
, 0, wxALL
, spacing
) 
 213         box
.Add(ci
, 0, wxEXPAND|wxALL
, spacing
) 
 215         self
.SetAutoLayout(true
) 
 217         # Resize this window so it is just large enough for the 
 218         # minimum requirements of the sizer. 
 223     def MakeBitmap(self
, colour
): 
 225         We can create a bitmap of whatever we want by simply selecting 
 226         it into a wxMemoryDC and drawing on it.  In this case we just set 
 227         a background brush and clear the dc. 
 229         bmp 
= wxEmptyBitmap(16,16) 
 232         dc
.SetBackground(wxBrush(colour
)) 
 234         dc
.SelectObject(wxNullBitmap
) 
 238     def OnSetColour(self
, event
): 
 240         Use the event ID to get the colour, set that colour in the doodle. 
 242         colour 
= self
.doodle
.menuColours
[event
.GetId()] 
 243         self
.doodle
.SetColour(colour
) 
 246     def OnSetThickness(self
, event
): 
 248         Use the event ID to set the thickness in the doodle. 
 250         self
.doodle
.SetThickness(event
.GetId()) 
 253 #---------------------------------------------------------------------- 
 255 class ColourIndicator(wxWindow
): 
 257     An instance of this class is used on the ControlPanel to show 
 258     a sample of what the current doodle line will look like. 
 260     def __init__(self
, parent
): 
 261         wxWindow
.__init
__(self
, parent
, -1, style
=wxSUNKEN_BORDER
) 
 262         self
.SetBackgroundColour(wxWHITE
) 
 263         self
.SetSize(wxSize(-1, 40)) 
 264         self
.colour 
= self
.thickness 
= None 
 265         EVT_PAINT(self
, self
.OnPaint
) 
 268     def Update(self
, colour
, thickness
): 
 270         The doodle window calls this method any time the colour 
 271         or line thickness changes. 
 274         self
.thickness 
= thickness
 
 275         self
.Refresh()  # generate a paint event 
 278     def OnPaint(self
, event
): 
 280         This method is called when all or part of the window needs to be 
 285             sz 
= self
.GetClientSize() 
 286             pen 
= wxPen(wxNamedColour(self
.colour
), self
.thickness
) 
 289             dc
.DrawLine(10, sz
.height
/2, sz
.width
-10, sz
.height
/2) 
 293 #---------------------------------------------------------------------- 
 295 class DoodleAbout(wxDialog
): 
 296     """ An about box that uses an HTML window """ 
 300 <body bgcolor="#ACAA60"> 
 301 <center><table bgcolor="#455481" width="100%" cellspacing="0" 
 302 cellpadding="0" border="1"> 
 304     <td align="center"><h1>SuperDoodle</h1></td> 
 308 <p><b>SuperDoodle</b> is a demonstration program for <b>wxPython</b> that 
 309 will hopefully teach you a thing or two.  Just follow these simple 
 313   <li><b>Read</b> the Source... 
 318 <p><b>SuperDoodle</b> and <b>wxPython</b> are brought to you by 
 319 <b>Robin Dunn</b> and <b>Total Control Software</b>, Copyright 
 320 © 1997-2001.</p> 
 325     def __init__(self
, parent
): 
 326         wxDialog
.__init
__(self
, parent
, -1, 'About SuperDoodle', 
 327                           size
=wxSize(420, 380)) 
 328         from wxPython
.html 
import wxHtmlWindow
 
 330         html 
= wxHtmlWindow(self
, -1) 
 331         html
.SetPage(self
.text
) 
 332         button 
= wxButton(self
, wxID_OK
, "Okay") 
 334         # constraints for the html window 
 335         lc 
= wxLayoutConstraints() 
 336         lc
.top
.SameAs(self
, wxTop
, 5) 
 337         lc
.left
.SameAs(self
, wxLeft
, 5) 
 338         lc
.bottom
.SameAs(button
, wxTop
, 5) 
 339         lc
.right
.SameAs(self
, wxRight
, 5) 
 340         html
.SetConstraints(lc
) 
 342         # constraints for the button 
 343         lc 
= wxLayoutConstraints() 
 344         lc
.bottom
.SameAs(self
, wxBottom
, 5) 
 345         lc
.centreX
.SameAs(self
, wxCentreX
) 
 348         button
.SetConstraints(lc
) 
 350         self
.SetAutoLayout(true
) 
 352         self
.CentreOnParent(wxBOTH
) 
 355 #---------------------------------------------------------------------- 
 357 class DoodleApp(wxApp
): 
 359         frame 
= DoodleFrame(None) 
 361         self
.SetTopWindow(frame
) 
 365 #---------------------------------------------------------------------- 
 367 if __name__ 
== '__main__':