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 style
=wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE
)
40 self
.CreateStatusBar()
44 self
.doodle
= DoodleWindow(self
, -1)
45 cPanel
= ControlPanel(self
, -1, self
.doodle
)
47 # Create a sizer to layout the two windows side-by-side.
48 # Both will grow vertically, the doodle window will grow
49 # horizontally as well.
50 box
= wxBoxSizer(wxHORIZONTAL
)
51 box
.Add(cPanel
, 0, wxEXPAND
)
52 box
.Add(self
.doodle
, 1, wxEXPAND
)
54 # Tell the frame that it should layout itself in response to
56 self
.SetAutoLayout(true
)
59 EVT_CLOSE(self
, self
.OnCloseWindow
)
62 def OnCloseWindow(self
, event
):
69 data
= self
.doodle
.GetLinesData()
70 f
= open(self
.filename
, 'w')
78 f
= open(self
.filename
, 'r')
79 data
= cPickle
.load(f
)
81 self
.doodle
.SetLinesData(data
)
82 except cPickle
.UnpicklingError
:
83 wxMessageBox("%s is not a doodle file." % self
.filename
,
84 "oops!", style
=wxOK|wxICON_EXCLAMATION
)
88 # create the file menu
90 menu1
.Append(idOPEN
, "&Open", "Open a doodle file")
91 menu1
.Append(idSAVE
, "&Save", "Save the doodle")
92 menu1
.Append(idSAVEAS
, "Save &As", "Save the doodle in a new file")
93 menu1
.AppendSeparator()
94 menu1
.Append(idCLEAR
, "&Clear", "Clear the current doodle")
95 menu1
.AppendSeparator()
96 menu1
.Append(idEXIT
, "E&xit", "Terminate the application")
100 menu2
.Append(idABOUT
, "&About", "Display the gratuitous 'about this app' thingamajig")
102 # and add them to a menubar
103 menuBar
= wxMenuBar()
104 menuBar
.Append(menu1
, "&File")
105 menuBar
.Append(menu2
, "&Help")
106 self
.SetMenuBar(menuBar
)
108 EVT_MENU(self
, idOPEN
, self
.OnMenuOpen
)
109 EVT_MENU(self
, idSAVE
, self
.OnMenuSave
)
110 EVT_MENU(self
, idSAVEAS
, self
.OnMenuSaveAs
)
111 EVT_MENU(self
, idCLEAR
, self
.OnMenuClear
)
112 EVT_MENU(self
, idEXIT
, self
.OnMenuExit
)
113 EVT_MENU(self
, idABOUT
, self
.OnMenuAbout
)
117 wildcard
= "Doodle files (*.ddl)|*.ddl|All files (*.*)|*.*"
119 def OnMenuOpen(self
, event
):
120 dlg
= wxFileDialog(self
, "Open doodle file...", os
.getcwd(),
121 style
=wxOPEN
, wildcard
= self
.wildcard
)
122 if dlg
.ShowModal() == wxID_OK
:
123 self
.filename
= dlg
.GetPath()
125 self
.SetTitle(self
.title
+ ' -- ' + self
.filename
)
129 def OnMenuSave(self
, event
):
130 if not self
.filename
:
131 self
.OnMenuSaveAs(event
)
136 def OnMenuSaveAs(self
, event
):
137 dlg
= wxFileDialog(self
, "Save doodle as...", os
.getcwd(),
138 style
=wxSAVE | wxOVERWRITE_PROMPT
,
139 wildcard
= self
.wildcard
)
140 if dlg
.ShowModal() == wxID_OK
:
141 filename
= dlg
.GetPath()
142 if not os
.path
.splitext(filename
)[1]:
143 filename
= filename
+ '.ddl'
144 self
.filename
= filename
146 self
.SetTitle(self
.title
+ ' -- ' + self
.filename
)
150 def OnMenuClear(self
, event
):
151 self
.doodle
.SetLinesData([])
152 self
.SetTitle(self
.title
)
155 def OnMenuExit(self
, event
):
159 def OnMenuAbout(self
, event
):
160 dlg
= DoodleAbout(self
)
166 #----------------------------------------------------------------------
169 class ControlPanel(wxPanel
):
171 This class implements a very simple control panel for the DoodleWindow.
172 It creates buttons for each of the colours and thickneses supported by
173 the DoodleWindow, and event handlers to set the selected values. There is
174 also a little window that shows an example doodleLine in the selected
175 values. Nested sizers are used for layout.
177 def __init__(self
, parent
, ID
, doodle
):
178 wxPanel
.__init
__(self
, parent
, ID
, style
=wxRAISED_BORDER
)
183 # Make a grid of buttons for each colour. Attach each button
184 # event to self.OnSetColour. The button ID is the same as the
185 # key in the colour dictionary.
186 colours
= doodle
.menuColours
187 keys
= colours
.keys()
189 cGrid
= wxGridSizer(cols
=numCols
, hgap
=2, vgap
=2)
191 bmp
= self
.MakeBitmap(wxNamedColour(colours
[k
]))
192 b
= wxBitmapButton(self
, k
, bmp
)
193 EVT_BUTTON(self
, k
, self
.OnSetColour
)
196 # Save the button size so we can use it for the number buttons
197 btnSize
= b
.GetSize()
199 # Make a grid of buttons for the thicknesses. Attach each button
200 # event to self.OnSetThickness. The button ID is the same as the
202 tGrid
= wxGridSizer(cols
=numCols
, hgap
=2, vgap
=2)
203 for x
in range(1, doodle
.maxThickness
+1):
204 b
= wxButton(self
, x
, str(x
), size
=btnSize
)
205 EVT_BUTTON(self
, x
, self
.OnSetThickness
)
208 # Make a colour indicator window, it is registerd as a listener
209 # with the doodle window so it will be notified when the settings
211 ci
= ColourIndicator(self
)
212 doodle
.AddListener(ci
)
216 # Make a box sizer and put the two grids and the indicator
218 box
= wxBoxSizer(wxVERTICAL
)
219 box
.Add(cGrid
, 0, wxALL
, spacing
)
220 box
.Add(tGrid
, 0, wxALL
, spacing
)
221 box
.Add(ci
, 0, wxEXPAND|wxALL
, spacing
)
223 self
.SetAutoLayout(true
)
225 # Resize this window so it is just large enough for the
226 # minimum requirements of the sizer.
231 def MakeBitmap(self
, colour
):
233 We can create a bitmap of whatever we want by simply selecting
234 it into a wxMemoryDC and drawing on it. In this case we just set
235 a background brush and clear the dc.
237 bmp
= wxEmptyBitmap(16,16)
240 dc
.SetBackground(wxBrush(colour
))
242 dc
.SelectObject(wxNullBitmap
)
246 def OnSetColour(self
, event
):
248 Use the event ID to get the colour, set that colour in the doodle.
250 colour
= self
.doodle
.menuColours
[event
.GetId()]
251 self
.doodle
.SetColour(colour
)
254 def OnSetThickness(self
, event
):
256 Use the event ID to set the thickness in the doodle.
258 self
.doodle
.SetThickness(event
.GetId())
261 #----------------------------------------------------------------------
263 class ColourIndicator(wxWindow
):
265 An instance of this class is used on the ControlPanel to show
266 a sample of what the current doodle line will look like.
268 def __init__(self
, parent
):
269 wxWindow
.__init
__(self
, parent
, -1, style
=wxSUNKEN_BORDER
)
270 self
.SetBackgroundColour(wxWHITE
)
271 self
.SetSize(wxSize(-1, 40))
272 self
.colour
= self
.thickness
= None
273 EVT_PAINT(self
, self
.OnPaint
)
276 def Update(self
, colour
, thickness
):
278 The doodle window calls this method any time the colour
279 or line thickness changes.
282 self
.thickness
= thickness
283 self
.Refresh() # generate a paint event
286 def OnPaint(self
, event
):
288 This method is called when all or part of the window needs to be
293 sz
= self
.GetClientSize()
294 pen
= wxPen(wxNamedColour(self
.colour
), self
.thickness
)
297 dc
.DrawLine(10, sz
.height
/2, sz
.width
-10, sz
.height
/2)
301 #----------------------------------------------------------------------
303 class DoodleAbout(wxDialog
):
304 """ An about box that uses an HTML window """
308 <body bgcolor="#ACAA60">
309 <center><table bgcolor="#455481" width="100%" cellspacing="0"
310 cellpadding="0" border="1">
312 <td align="center"><h1>SuperDoodle</h1></td>
316 <p><b>SuperDoodle</b> is a demonstration program for <b>wxPython</b> that
317 will hopefully teach you a thing or two. Just follow these simple
321 <li><b>Read</b> the Source...
326 <p><b>SuperDoodle</b> and <b>wxPython</b> are brought to you by
327 <b>Robin Dunn</b> and <b>Total Control Software</b>, Copyright
328 © 1997-2001.</p>
333 def __init__(self
, parent
):
334 wxDialog
.__init
__(self
, parent
, -1, 'About SuperDoodle',
335 size
=wxSize(420, 380))
336 from wxPython
.html
import wxHtmlWindow
338 html
= wxHtmlWindow(self
, -1)
339 html
.SetPage(self
.text
)
340 button
= wxButton(self
, wxID_OK
, "Okay")
342 # constraints for the html window
343 lc
= wxLayoutConstraints()
344 lc
.top
.SameAs(self
, wxTop
, 5)
345 lc
.left
.SameAs(self
, wxLeft
, 5)
346 lc
.bottom
.SameAs(button
, wxTop
, 5)
347 lc
.right
.SameAs(self
, wxRight
, 5)
348 html
.SetConstraints(lc
)
350 # constraints for the button
351 lc
= wxLayoutConstraints()
352 lc
.bottom
.SameAs(self
, wxBottom
, 5)
353 lc
.centreX
.SameAs(self
, wxCentreX
)
356 button
.SetConstraints(lc
)
358 self
.SetAutoLayout(true
)
360 self
.CentreOnParent(wxBOTH
)
363 #----------------------------------------------------------------------
365 class DoodleApp(wxApp
):
367 frame
= DoodleFrame(None)
369 self
.SetTopWindow(frame
)
373 #----------------------------------------------------------------------
375 if __name__
== '__main__':