]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/doodle/superdoodle.py
Added wxRTTI for the new wxVScrolledWindow, wxVListBox, and
[wxWidgets.git] / wxPython / samples / doodle / superdoodle.py
CommitLineData
c12bc4de
RD
1# superdoodle.py
2
3"""
4This module implements the SuperDoodle demo application. It takes the
5DoodleWindow previously presented and reuses it in a much more
6intelligent Frame. This one has a menu and a statusbar, is able to
7save and reload doodles, clear the workspace, and has a simple control
8panel for setting color and line thickness in addition to the popup
9menu that DoodleWindow provides. There is also a nice About dialog
1fded56b 10implmented using an wx.html.HtmlWindow.
c12bc4de
RD
11"""
12
1fded56b
RD
13import wx # This module uses the new wx namespace
14import wx.html
15from wx.lib import buttons # for generic button classes
c12bc4de
RD
16from doodle import DoodleWindow
17
18import os, cPickle
19
20
21#----------------------------------------------------------------------
22
1fded56b 23wx.RegisterId(5000) # Give a high starting value for the IDs, just for kicks
c12bc4de 24
1fded56b
RD
25idNEW = wx.NewId()
26idOPEN = wx.NewId()
27idSAVE = wx.NewId()
28idSAVEAS = wx.NewId()
29idCLEAR = wx.NewId()
30idEXIT = wx.NewId()
31idABOUT = wx.NewId()
c12bc4de 32
1fded56b
RD
33
34
35class DoodleFrame(wx.Frame):
c12bc4de
RD
36 """
37 A DoodleFrame contains a DoodleWindow and a ControlPanel and manages
1fded56b 38 their layout with a wx.BoxSizer. A menu and associated event handlers
c12bc4de
RD
39 provides for saving a doodle to a file, etc.
40 """
41 title = "Do a doodle"
42 def __init__(self, parent):
1fded56b
RD
43 wx.Frame.__init__(self, parent, -1, self.title, size=(800,600),
44 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
c12bc4de
RD
45 self.CreateStatusBar()
46 self.MakeMenu()
47 self.filename = None
48
49 self.doodle = DoodleWindow(self, -1)
50 cPanel = ControlPanel(self, -1, self.doodle)
51
52 # Create a sizer to layout the two windows side-by-side.
53 # Both will grow vertically, the doodle window will grow
54 # horizontally as well.
1fded56b
RD
55 box = wx.BoxSizer(wx.HORIZONTAL)
56 box.Add(cPanel, 0, wx.EXPAND)
57 box.Add(self.doodle, 1, wx.EXPAND)
c12bc4de
RD
58
59 # Tell the frame that it should layout itself in response to
60 # size events.
1e4a197e 61 self.SetAutoLayout(True)
c12bc4de
RD
62 self.SetSizer(box)
63
64
65 def SaveFile(self):
66 if self.filename:
67 data = self.doodle.GetLinesData()
68 f = open(self.filename, 'w')
69 cPickle.dump(data, f)
70 f.close()
71
72
73 def ReadFile(self):
74 if self.filename:
75 try:
76 f = open(self.filename, 'r')
77 data = cPickle.load(f)
78 f.close()
79 self.doodle.SetLinesData(data)
80 except cPickle.UnpicklingError:
1fded56b
RD
81 wx.MessageBox("%s is not a doodle file." % self.filename,
82 "oops!", style=wx.OK|wx.ICON_EXCLAMATION)
c12bc4de
RD
83
84
85 def MakeMenu(self):
86 # create the file menu
1fded56b
RD
87 menu1 = wx.Menu()
88
89 # Using the "\tKeyName" syntax automatically creates a
90 # wx.AcceleratorTable for this frame and binds the keys to
91 # the menu items.
92 menu1.Append(idOPEN, "&Open\tCtrl-O", "Open a doodle file")
93 menu1.Append(idSAVE, "&Save\tCtrl-S", "Save the doodle")
c12bc4de
RD
94 menu1.Append(idSAVEAS, "Save &As", "Save the doodle in a new file")
95 menu1.AppendSeparator()
96 menu1.Append(idCLEAR, "&Clear", "Clear the current doodle")
97 menu1.AppendSeparator()
98 menu1.Append(idEXIT, "E&xit", "Terminate the application")
99
100 # and the help menu
1fded56b
RD
101 menu2 = wx.Menu()
102 menu2.Append(idABOUT, "&About\tCtrl-H", "Display the gratuitous 'about this app' thingamajig")
c12bc4de
RD
103
104 # and add them to a menubar
1fded56b 105 menuBar = wx.MenuBar()
c12bc4de
RD
106 menuBar.Append(menu1, "&File")
107 menuBar.Append(menu2, "&Help")
108 self.SetMenuBar(menuBar)
109
1fded56b
RD
110 wx.EVT_MENU(self, idOPEN, self.OnMenuOpen)
111 wx.EVT_MENU(self, idSAVE, self.OnMenuSave)
112 wx.EVT_MENU(self, idSAVEAS, self.OnMenuSaveAs)
113 wx.EVT_MENU(self, idCLEAR, self.OnMenuClear)
114 wx.EVT_MENU(self, idEXIT, self.OnMenuExit)
115 wx.EVT_MENU(self, idABOUT, self.OnMenuAbout)
c12bc4de
RD
116
117
118
119 wildcard = "Doodle files (*.ddl)|*.ddl|All files (*.*)|*.*"
120
121 def OnMenuOpen(self, event):
1fded56b
RD
122 dlg = wx.FileDialog(self, "Open doodle file...", os.getcwd(),
123 style=wx.OPEN, wildcard = self.wildcard)
124 if dlg.ShowModal() == wx.ID_OK:
c12bc4de
RD
125 self.filename = dlg.GetPath()
126 self.ReadFile()
127 self.SetTitle(self.title + ' -- ' + self.filename)
128 dlg.Destroy()
129
130
131 def OnMenuSave(self, event):
132 if not self.filename:
133 self.OnMenuSaveAs(event)
134 else:
135 self.SaveFile()
136
137
138 def OnMenuSaveAs(self, event):
1fded56b
RD
139 dlg = wx.FileDialog(self, "Save doodle as...", os.getcwd(),
140 style=wx.SAVE | wx.OVERWRITE_PROMPT,
c12bc4de 141 wildcard = self.wildcard)
1fded56b 142 if dlg.ShowModal() == wx.ID_OK:
c12bc4de
RD
143 filename = dlg.GetPath()
144 if not os.path.splitext(filename)[1]:
145 filename = filename + '.ddl'
146 self.filename = filename
147 self.SaveFile()
148 self.SetTitle(self.title + ' -- ' + self.filename)
149 dlg.Destroy()
150
151
152 def OnMenuClear(self, event):
153 self.doodle.SetLinesData([])
154 self.SetTitle(self.title)
155
156
157 def OnMenuExit(self, event):
158 self.Close()
159
160
161 def OnMenuAbout(self, event):
162 dlg = DoodleAbout(self)
163 dlg.ShowModal()
164 dlg.Destroy()
165
166
167
168#----------------------------------------------------------------------
169
170
1fded56b 171class ControlPanel(wx.Panel):
c12bc4de
RD
172 """
173 This class implements a very simple control panel for the DoodleWindow.
174 It creates buttons for each of the colours and thickneses supported by
175 the DoodleWindow, and event handlers to set the selected values. There is
176 also a little window that shows an example doodleLine in the selected
177 values. Nested sizers are used for layout.
178 """
1fded56b
RD
179
180 BMP_SIZE = 16
181 BMP_BORDER = 3
182
c12bc4de 183 def __init__(self, parent, ID, doodle):
1fded56b 184 wx.Panel.__init__(self, parent, ID, style=wx.RAISED_BORDER)
c12bc4de
RD
185
186 numCols = 4
187 spacing = 4
188
1fded56b
RD
189 btnSize = wx.Size(self.BMP_SIZE + 2*self.BMP_BORDER,
190 self.BMP_SIZE + 2*self.BMP_BORDER)
191
c12bc4de
RD
192 # Make a grid of buttons for each colour. Attach each button
193 # event to self.OnSetColour. The button ID is the same as the
194 # key in the colour dictionary.
1fded56b 195 self.clrBtns = {}
c12bc4de
RD
196 colours = doodle.menuColours
197 keys = colours.keys()
198 keys.sort()
1fded56b 199 cGrid = wx.GridSizer(cols=numCols, hgap=2, vgap=2)
c12bc4de 200 for k in keys:
1fded56b
RD
201 bmp = self.MakeBitmap(colours[k])
202 b = buttons.GenBitmapToggleButton(self, k, bmp, size=btnSize )
203 b.SetBezelWidth(1)
204 b.SetUseFocusIndicator(False)
205 wx.EVT_BUTTON(self, k, self.OnSetColour)
c12bc4de 206 cGrid.Add(b, 0)
1fded56b
RD
207 self.clrBtns[colours[k]] = b
208 self.clrBtns[colours[keys[0]]].SetToggle(True)
c12bc4de 209
c12bc4de
RD
210
211 # Make a grid of buttons for the thicknesses. Attach each button
212 # event to self.OnSetThickness. The button ID is the same as the
213 # thickness value.
1fded56b
RD
214 self.thknsBtns = {}
215 tGrid = wx.GridSizer(cols=numCols, hgap=2, vgap=2)
c12bc4de 216 for x in range(1, doodle.maxThickness+1):
1fded56b
RD
217 b = buttons.GenToggleButton(self, x, str(x), size=btnSize)
218 b.SetBezelWidth(1)
219 b.SetUseFocusIndicator(False)
220 wx.EVT_BUTTON(self, x, self.OnSetThickness)
c12bc4de 221 tGrid.Add(b, 0)
1fded56b
RD
222 self.thknsBtns[x] = b
223 self.thknsBtns[1].SetToggle(True)
c12bc4de
RD
224
225 # Make a colour indicator window, it is registerd as a listener
226 # with the doodle window so it will be notified when the settings
227 # change
228 ci = ColourIndicator(self)
229 doodle.AddListener(ci)
230 doodle.Notify()
231 self.doodle = doodle
232
233 # Make a box sizer and put the two grids and the indicator
234 # window in it.
1fded56b
RD
235 box = wx.BoxSizer(wx.VERTICAL)
236 box.Add(cGrid, 0, wx.ALL, spacing)
237 box.Add(tGrid, 0, wx.ALL, spacing)
238 box.Add(ci, 0, wx.EXPAND|wx.ALL, spacing)
c12bc4de 239 self.SetSizer(box)
1e4a197e 240 self.SetAutoLayout(True)
c12bc4de
RD
241
242 # Resize this window so it is just large enough for the
243 # minimum requirements of the sizer.
244 box.Fit(self)
245
246
247
248 def MakeBitmap(self, colour):
249 """
250 We can create a bitmap of whatever we want by simply selecting
1fded56b 251 it into a wx.MemoryDC and drawing on it. In this case we just set
c12bc4de
RD
252 a background brush and clear the dc.
253 """
1fded56b
RD
254 bmp = wx.EmptyBitmap(self.BMP_SIZE, self.BMP_SIZE)
255 dc = wx.MemoryDC()
c12bc4de 256 dc.SelectObject(bmp)
1fded56b 257 dc.SetBackground(wx.Brush(colour))
c12bc4de 258 dc.Clear()
1fded56b 259 dc.SelectObject(wx.NullBitmap)
c12bc4de
RD
260 return bmp
261
262
263 def OnSetColour(self, event):
264 """
265 Use the event ID to get the colour, set that colour in the doodle.
266 """
267 colour = self.doodle.menuColours[event.GetId()]
1fded56b
RD
268 if colour != self.doodle.colour:
269 # untoggle the old colour button
270 self.clrBtns[self.doodle.colour].SetToggle(False)
271 # set the new colour
c12bc4de
RD
272 self.doodle.SetColour(colour)
273
274
275 def OnSetThickness(self, event):
276 """
277 Use the event ID to set the thickness in the doodle.
278 """
1fded56b
RD
279 thickness = event.GetId()
280 if thickness != self.doodle.thickness:
281 # untoggle the old thickness button
282 self.thknsBtns[self.doodle.thickness].SetToggle(False)
283 # set the new colour
284 self.doodle.SetThickness(thickness)
285
c12bc4de
RD
286
287
288#----------------------------------------------------------------------
289
1fded56b 290class ColourIndicator(wx.Window):
c12bc4de
RD
291 """
292 An instance of this class is used on the ControlPanel to show
293 a sample of what the current doodle line will look like.
294 """
295 def __init__(self, parent):
1fded56b
RD
296 wx.Window.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)
297 self.SetBackgroundColour(wx.WHITE)
298 self.SetSize( (-1, 45) )
c12bc4de 299 self.colour = self.thickness = None
1fded56b 300 wx.EVT_PAINT(self, self.OnPaint)
c12bc4de
RD
301
302
303 def Update(self, colour, thickness):
304 """
305 The doodle window calls this method any time the colour
306 or line thickness changes.
307 """
308 self.colour = colour
309 self.thickness = thickness
310 self.Refresh() # generate a paint event
311
312
313 def OnPaint(self, event):
314 """
315 This method is called when all or part of the window needs to be
316 redrawn.
317 """
1fded56b 318 dc = wx.PaintDC(self)
c12bc4de
RD
319 if self.colour:
320 sz = self.GetClientSize()
1fded56b 321 pen = wx.Pen(self.colour, self.thickness)
c12bc4de
RD
322 dc.BeginDrawing()
323 dc.SetPen(pen)
324 dc.DrawLine(10, sz.height/2, sz.width-10, sz.height/2)
325 dc.EndDrawing()
326
327
328#----------------------------------------------------------------------
329
1fded56b 330class DoodleAbout(wx.Dialog):
c12bc4de
RD
331 """ An about box that uses an HTML window """
332
333 text = '''
334<html>
335<body bgcolor="#ACAA60">
336<center><table bgcolor="#455481" width="100%" cellspacing="0"
337cellpadding="0" border="1">
338<tr>
339 <td align="center"><h1>SuperDoodle</h1></td>
340</tr>
341</table>
342</center>
343<p><b>SuperDoodle</b> is a demonstration program for <b>wxPython</b> that
344will hopefully teach you a thing or two. Just follow these simple
345instructions: </p>
346<p>
347<ol>
348 <li><b>Read</b> the Source...
349 <li><b>Learn</b>...
350 <li><b>Do!</b>
351</ol>
352
353<p><b>SuperDoodle</b> and <b>wxPython</b> are brought to you by
354<b>Robin Dunn</b> and <b>Total Control Software</b>, Copyright
1fded56b 355&copy; 1997-2003.</p>
c12bc4de
RD
356</body>
357</html>
358'''
359
360 def __init__(self, parent):
1fded56b
RD
361 wx.Dialog.__init__(self, parent, -1, 'About SuperDoodle',
362 size=(420, 380) )
c12bc4de 363
1fded56b 364 html = wx.html.HtmlWindow(self, -1)
c12bc4de 365 html.SetPage(self.text)
1fded56b 366 button = wx.Button(self, wx.ID_OK, "Okay")
c12bc4de
RD
367
368 # constraints for the html window
1fded56b
RD
369 lc = wx.LayoutConstraints()
370 lc.top.SameAs(self, wx.Top, 5)
371 lc.left.SameAs(self, wx.Left, 5)
372 lc.bottom.SameAs(button, wx.Top, 5)
373 lc.right.SameAs(self, wx.Right, 5)
c12bc4de
RD
374 html.SetConstraints(lc)
375
376 # constraints for the button
1fded56b
RD
377 lc = wx.LayoutConstraints()
378 lc.bottom.SameAs(self, wx.Bottom, 5)
379 lc.centreX.SameAs(self, wx.CentreX)
c12bc4de
RD
380 lc.width.AsIs()
381 lc.height.AsIs()
382 button.SetConstraints(lc)
383
1e4a197e 384 self.SetAutoLayout(True)
c12bc4de 385 self.Layout()
1fded56b 386 self.CentreOnParent(wx.BOTH)
c12bc4de
RD
387
388
389#----------------------------------------------------------------------
390
1fded56b 391class DoodleApp(wx.App):
c12bc4de
RD
392 def OnInit(self):
393 frame = DoodleFrame(None)
1e4a197e 394 frame.Show(True)
c12bc4de 395 self.SetTopWindow(frame)
1e4a197e 396 return True
c12bc4de
RD
397
398
399#----------------------------------------------------------------------
400
401if __name__ == '__main__':
402 app = DoodleApp(0)
403 app.MainLoop()