]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/Main.py
Added some accelerators to the menu to help with stress testing wxSound:
[wxWidgets.git] / wxPython / demo / Main.py
CommitLineData
cf694132
RD
1#!/bin/env python
2#----------------------------------------------------------------------------
3# Name: Main.py
4# Purpose: Testing lots of stuff, controls, window types, etc.
5#
f6bcfd97 6# Author: Robin Dunn
cf694132 7#
f6bcfd97 8# Created: A long time ago, in a galaxy far, far away...
cf694132
RD
9# RCS-ID: $Id$
10# Copyright: (c) 1999 by Total Control Software
11# Licence: wxWindows license
12#----------------------------------------------------------------------------
13
08ecc920
RD
14# FIXME List:
15# * Problems with flickering related to ERASE_BACKGROUND
16# and the splitters. Might be a problem with this 2.5 beta...?
17# UPDATE: can't see on 2.5.2 GTK - maybe just a faster machine :)
18# * Demo Code menu?
19# * Annoying switching between tabs and resulting flicker
20# how to replace a page in the notebook without deleting/adding?
21# Where is SetPage!? tried freeze...tried reparent of dummy panel....
22
23# TODO List:
24# * UI design more prefessional
25# * save file positions (new field in demoModules) (@ LoadDemoSource)
26# * Update main overview
27
28# * Why don't we move _treeList into a separate module
29
30import sys, os, time, traceback, types
cf694132 31
1fded56b
RD
32import wx # This module uses the new wx namespace
33import wx.html
606d919c 34
96bfd053
RD
35import images
36
d14a1e28
RD
37# For debugging
38##wx.Trap();
ef8b9c3e 39##print "wx.VERSION_STRING = ", wx.VERSION_STRING
4276dc52 40##print os.getpid();
ef8b9c3e 41##raw_input("Press Enter...")
6fa68946 42
1e4a197e 43
cf694132
RD
44#---------------------------------------------------------------------------
45
46
47_treeList = [
9c67cbec 48 # new stuff
77d4f443 49 ('Recent Additions/Updates', [
8b6df374 50 'StockButtons',
cccce1a6 51 'Ticker',
9694b21a 52 'Choicebook',
15513a80 53 'ListCtrl_edit',
9c67cbec
RD
54 ]),
55
1e4a197e 56 # managed windows == things with a (optional) caption you can close
03a604a6 57 ('Frames and Dialogs', [
299647ac
RD
58 'Dialog',
59 'Frame',
60 'MDIWindows',
61 'MiniFrame',
62 'Wizard',
9c67cbec
RD
63 ]),
64
65 # the common dialogs
66 ('Common Dialogs', [
299647ac
RD
67 'ColourDialog',
68 'DirDialog',
69 'FileDialog',
299647ac
RD
70 'FindReplaceDialog',
71 'FontDialog',
72 'MessageDialog',
73 'PageSetupDialog',
74 'PrintDialog',
75 'ProgressDialog',
76 'SingleChoiceDialog',
77 'TextEntryDialog',
9c67cbec
RD
78 ]),
79
af83019e 80 # dialogs from libraries
9c67cbec 81 ('More Dialogs', [
9c67cbec 82 'ImageBrowser',
299647ac
RD
83 'MultipleChoiceDialog',
84 'ScrolledMessageDialog',
9c67cbec
RD
85 ]),
86
87 # core controls
88 ('Core Windows/Controls', [
8b7fa2d9 89 'BitmapButton',
299647ac
RD
90 'Button',
91 'CheckBox',
92 'CheckListBox',
93 'Choice',
9694b21a 94 'Choicebook',
299647ac
RD
95 'ComboBox',
96 'Gauge',
97 'Grid',
98 'Grid_MegaExample',
99 'ListBox',
100 'ListCtrl',
101 'ListCtrl_virtual',
15513a80 102 'ListCtrl_edit',
299647ac
RD
103 'Listbook',
104 'Menu',
105 'Notebook',
1fded56b 106 'PopupMenu',
299647ac
RD
107 'PopupWindow',
108 'RadioBox',
109 'RadioButton',
110 'SashWindow',
111 'ScrolledWindow',
112 'Slider',
113 'SpinButton',
114 'SpinCtrl',
115 'SplitterWindow',
116 'StaticBitmap',
117 'StaticText',
118 'StatusBar',
f2b4a4ea 119 'StockButtons',
299647ac
RD
120 'TextCtrl',
121 'ToggleButton',
122 'ToolBar',
123 'TreeCtrl',
124 'Validator',
9c67cbec
RD
125 ]),
126
d14a1e28
RD
127 ('Custom Controls', [
128 'AnalogClockWindow',
129 'ColourSelect',
299647ac 130 'Editor',
d14a1e28 131 'GenericButtons',
299647ac
RD
132 'GenericDirCtrl',
133 'LEDNumberCtrl',
134 'MultiSash',
135 'PopupControl',
136 'PyColourChooser',
137 'TreeListCtrl',
d14a1e28
RD
138 ]),
139
8b9a4190 140 # controls coming from other libraries
9c67cbec 141 ('More Windows/Controls', [
b7c75283
RD
142 'ActiveX_FlashWindow',
143 'ActiveX_IEHtmlWindow',
144 'ActiveX_PDFWindow',
299647ac
RD
145 #'RightTextCtrl', deprecated as we have wxTE_RIGHT now.
146 'Calendar',
147 'CalendarCtrl',
9c67cbec 148 'ContextHelp',
299647ac
RD
149 'DynamicSashWindow',
150 'EditableListBox',
9c67cbec
RD
151 'FancyText',
152 'FileBrowseButton',
299647ac
RD
153 'FloatBar',
154 'FloatCanvas',
155 'HtmlWindow',
299647ac
RD
156 'IntCtrl',
157 'MVCTree',
1fded56b 158 'MaskedEditControls',
299647ac
RD
159 'MaskedNumCtrl',
160 'MimeTypesManager',
9c67cbec 161 'PyCrust',
80b27b4e 162 'PyPlot',
299647ac
RD
163 'PyShell',
164 'ScrolledPanel',
9c67cbec 165 'SplitTree',
299647ac
RD
166 'StyledTextCtrl_1',
167 'StyledTextCtrl_2',
9c67cbec 168 'TablePrint',
1e4a197e 169 'Throbber',
cccce1a6 170 'Ticker',
299647ac
RD
171 'TimeCtrl',
172 'VListBox',
9c67cbec
RD
173 ]),
174
175 # How to lay out the controls in a frame/dialog
176 ('Window Layout', [
299647ac 177 'GridBagSizer',
9c67cbec 178 'LayoutAnchors',
299647ac 179 'LayoutConstraints',
9c67cbec
RD
180 'Layoutf',
181 'RowColSizer',
299647ac 182 'ScrolledPanel',
9c67cbec 183 'Sizers',
299647ac
RD
184 'XmlResource',
185 'XmlResourceHandler',
186 'XmlResourceSubclass',
9c67cbec
RD
187 ]),
188
189 # ditto
190 ('Process and Events', [
1e4a197e 191 'EventManager',
299647ac 192 'KeyEvents',
299647ac 193 'Process',
9c67cbec
RD
194 'PythonEvents',
195 'Threads',
299647ac 196 'Timer',
ebd09fe9 197 ##'infoframe', # needs better explaination and some fixing
9c67cbec
RD
198 ]),
199
200 # Clipboard and DnD
201 ('Clipboard and DnD', [
202 'CustomDragAndDrop',
203 'DragAndDrop',
204 'URLDragAndDrop',
205 ]),
206
207 # Images
1e4a197e 208 ('Using Images', [
299647ac 209 'ArtProvider',
76343679 210 'Cursor',
299647ac
RD
211 'DragImage',
212 'Image',
8a88769e 213 'ImageAlpha',
299647ac
RD
214 'ImageFromStream',
215 'Mask',
1e4a197e 216 'Throbber',
9c67cbec
RD
217 ]),
218
219 # Other stuff
220 ('Miscellaneous', [
221 'ColourDB',
ebd09fe9 222 ##'DialogUnits', # needs more explainations
9c67cbec 223 'DrawXXXList',
299647ac 224 'FileHistory',
9c67cbec 225 'FontEnumerator',
299647ac 226 'Joystick',
299647ac 227 'OGL',
9c67cbec 228 'PrintFramework',
3628e088 229 'ShapedWindow',
78862f24 230 'Sound',
9c67cbec 231 'Unicode',
9c67cbec
RD
232 ]),
233
234 # need libs not coming with the demo
d7403ad2 235 ('Samples using an external library', [
299647ac 236 'GLCanvas',
9c67cbec
RD
237 ]),
238
70a357c2 239
9c67cbec
RD
240 ('Check out the samples dir too', [
241 ]),
242
9c67cbec
RD
243]
244
245
cf694132
RD
246
247#---------------------------------------------------------------------------
8b9a4190 248# Show how to derive a custom wxLog class
cf694132 249
1fded56b 250class MyLog(wx.PyLog):
76bfdc78 251 def __init__(self, textCtrl, logTime=0):
1fded56b 252 wx.PyLog.__init__(self)
76bfdc78
RD
253 self.tc = textCtrl
254 self.logTime = logTime
255
256 def DoLogString(self, message, timeStamp):
257 if self.logTime:
258 message = time.strftime("%X", time.localtime(timeStamp)) + \
259 ": " + message
1e4a197e
RD
260 if self.tc:
261 self.tc.AppendText(message + '\n')
76bfdc78
RD
262
263
1fded56b 264class MyTP(wx.PyTipProvider):
861a0196
RD
265 def GetTip(self):
266 return "This is my tip"
267
08ecc920 268
1fded56b
RD
269#---------------------------------------------------------------------------
270# A class to be used to display source code in the demo. Try using the
299647ac 271# wxSTC in the StyledTextCtrl_2 sample first, fall back to wxTextCtrl
1fded56b 272# if there is an error, such as the stc module not being present.
8b9a4190 273#
1fded56b
RD
274
275try:
4638eaea 276 ##raise ImportError # for testing the alternate implementation
1fded56b 277 from wx import stc
299647ac 278 from StyledTextCtrl_2 import PythonSTC
08ecc920
RD
279
280 class DemoCodeEditor(PythonSTC):
281 def __init__(self, parent):
2e839e96 282 PythonSTC.__init__(self, parent, -1, style=wx.BORDER_NONE)
80b27b4e 283 self.SetUpEditor()
1fded56b
RD
284
285 # Some methods to make it compatible with how the wxTextCtrl is used
286 def SetValue(self, value):
6c7d1792
RD
287 if wx.USE_UNICODE:
288 value = value.decode('iso8859_1')
1fded56b 289 self.SetText(value)
08ecc920
RD
290 self.EmptyUndoBuffer()
291 self.SetSavePoint()
292
293 def IsModified(self):
294 return self.GetModify()
1fded56b
RD
295
296 def Clear(self):
297 self.ClearAll()
298
299 def SetInsertionPoint(self, pos):
300 self.SetCurrentPos(pos)
08ecc920 301 self.SetAnchor(pos)
1fded56b
RD
302
303 def ShowPosition(self, pos):
08ecc920 304 line = self.LineFromPosition(pos)
887a1bd9
RD
305 #self.EnsureVisible(line)
306 self.GotoLine(line)
1fded56b
RD
307
308 def GetLastPosition(self):
309 return self.GetLength()
310
08ecc920
RD
311 def GetPositionFromLine(self, line):
312 return self.PositionFromLine(line)
313
1fded56b
RD
314 def GetRange(self, start, end):
315 return self.GetTextRange(start, end)
316
317 def GetSelection(self):
318 return self.GetAnchor(), self.GetCurrentPos()
319
320 def SetSelection(self, start, end):
321 self.SetSelectionStart(start)
322 self.SetSelectionEnd(end)
323
08ecc920
RD
324 def SelectLine(self, line):
325 start = self.PositionFromLine(line)
326 end = self.GetLineEndPosition(line)
327 self.SetSelection(start, end)
328
80b27b4e
RD
329 def SetUpEditor(self):
330 """
331 This method carries out the work of setting up the demo editor.
332 It's seperate so as not to clutter up the init code.
333 """
334 import keyword
335
336 self.SetLexer(stc.STC_LEX_PYTHON)
337 self.SetKeyWords(0, " ".join(keyword.kwlist))
338
339 # Enable folding
340 self.SetProperty("fold", "1" )
341
342 # Highlight tab/space mixing (shouldn't be any)
343 self.SetProperty("tab.timmy.whinge.level", "1")
344
345 # Set left and right margins
346 self.SetMargins(2,2)
347
348 # Set up the numbers in the margin for margin #1
349 self.SetMarginType(1, wx.stc.STC_MARGIN_NUMBER)
350 # Reasonable value for, say, 4-5 digits using a mono font (40 pix)
351 self.SetMarginWidth(1, 40)
352
353 # Indentation and tab stuff
354 self.SetIndent(4) # Proscribed indent size for wx
355 self.SetIndentationGuides(True) # Show indent guides
356 self.SetBackSpaceUnIndents(True)# Backspace unindents rather than delete 1 space
357 self.SetTabIndents(True) # Tab key indents
358 self.SetTabWidth(4) # Proscribed tab size for wx
359 self.SetUseTabs(False) # Use spaces rather than tabs, or
360 # TabTimmy will complain!
361 # White space
362 self.SetViewWhiteSpace(False) # Don't view white space
363
887a1bd9
RD
364 # EOL: Since we are loading/saving ourselves, and the
365 # strings will always have \n's in them, set the STC to
366 # edit them that way.
367 self.SetEOLMode(wx.stc.STC_EOL_LF)
368 self.SetViewEOL(False)
369
80b27b4e
RD
370 # No right-edge mode indicator
371 self.SetEdgeMode(stc.STC_EDGE_NONE)
372
373 # Setup a margin to hold fold markers
374 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
375 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
376 self.SetMarginSensitive(2, True)
377 self.SetMarginWidth(2, 12)
378
379 # and now set up the fold markers
380 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "black")
381 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "black")
382 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "black")
383 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "black")
384 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "black")
385 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "black")
386 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "black")
387
388 # Global default style
389 if wx.Platform == '__WXMSW__':
390 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
391 'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
392 else:
393 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
394 'fore:#000000,back:#FFFFFF,face:Courier,size:12')
395
396 # Clear styles and revert to default.
397 self.StyleClearAll()
398
399 # Following style specs only indicate differences from default.
400 # The rest remains unchanged.
401
402 # Line numbers in margin
e44b5196 403 self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
80b27b4e
RD
404 # Highlighted brace
405 self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
406 # Unmatched brace
407 self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
408 # Indentation guide
409 self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
410
411 # Python styles
412 self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
413 # Comments
a253aa20
RD
414 self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
415 self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
80b27b4e
RD
416 # Numbers
417 self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
418 # Strings and characters
419 self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
420 self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
421 # Keywords
422 self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
423 # Triple quotes
424 self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
425 self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
426 # Class names
427 self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
428 # Function names
429 self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
430 # Operators
431 self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
432 # Identifiers. I leave this as not bold because everything seems
433 # to be an identifier if it doesn't match the above criterae
434 self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
435
436 # Caret color
437 self.SetCaretForeground("BLUE")
438 # Selection background
439 self.SetSelBackground(1, '#66CCFF')
440
441 self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
442 self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
443
08ecc920
RD
444 def RegisterModifiedEvent(self, eventHandler):
445 self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
446
1fded56b
RD
447
448except ImportError:
08ecc920
RD
449 class DemoCodeEditor(wx.TextCtrl):
450 def __init__(self, parent):
2e839e96
RD
451 wx.TextCtrl.__init__(self, parent, -1, style =
452 wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
1fded56b 453
08ecc920
RD
454 def RegisterModifiedEvent(self, eventHandler):
455 self.Bind(wx.EVT_TEXT, eventHandler)
456
457 def SetReadOnly(self, flag):
458 self.SetEditable(not flag)
459 # NOTE: STC already has this method
460
461 def GetText(self):
462 return self.GetValue()
463
464 def GetPositionFromLine(line):
465 return self.XYToPosition(0,line)
466
467 def GotoLine(self, line):
468 pos = self.editor.GetPositionFromLine(line)
469 self.editor.SetInsertionPoint(pos)
470 self.editor.ShowPosition(pos)
471
472 def SelectLine(self, line):
473 start = self.GetPositionFromLine(line)
474 end = start + self.GetLineLength(line)
475 self.SetSelection(start, end)
476
477
478#---------------------------------------------------------------------------
479# Constants for module versions
480
481modOriginal = 0
482modModified = 1
483modDefault = modOriginal
484
485#---------------------------------------------------------------------------
486
487class DemoCodePanel(wx.Panel):
488 """Panel for the 'Demo Code' tab"""
489 def __init__(self, parent, mainFrame):
2e839e96 490 wx.Panel.__init__(self, parent, size=(1,1))
e0465cbd
RD
491 if 'wxMSW' in wx.PlatformInfo:
492 self.Hide()
08ecc920
RD
493 self.mainFrame = mainFrame
494 self.editor = DemoCodeEditor(self)
495 self.editor.RegisterModifiedEvent(self.OnCodeModified)
496
497 self.btnSave = wx.Button(self, -1, "Save Changes")
498 self.btnRestore = wx.Button(self, -1, "Delete Modified")
499 self.btnSave.Enable(False)
500 self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
501 self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
502
503 self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
504 modModified: wx.RadioButton(self, -1, "Modified") }
505
506 self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
507 self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
508 wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
509 for modID, radioButton in self.radioButtons.items():
510 self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
511 radioButton.modID = modID # makes it easier for the event handler
512 radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
513
514 self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
515 self.controlBox.Add(self.btnRestore, 0)
516
517 self.box = wx.BoxSizer(wx.VERTICAL)
518 self.box.Add(self.controlBox, 0, wx.EXPAND)
887a1bd9 519 self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
08ecc920
RD
520 self.box.Add(self.editor, 1, wx.EXPAND)
521
522 self.box.Fit(self)
523 self.SetSizer(self.box)
524
525
526 # Loads a demo from a DemoModules object
527 def LoadDemo(self, demoModules):
528 self.demoModules = demoModules
529 if (modDefault == modModified) and demoModules.Exists(modModified):
530 demoModules.SetActive(modModified)
531 else:
532 demoModules.SetActive(modOriginal)
533 self.radioButtons[demoModules.GetActiveID()].Enable(True)
534 self.ActiveModuleChanged()
535
536
537 def ActiveModuleChanged(self):
538 self.LoadDemoSource(self.demoModules.GetSource())
539 self.UpdateControlState()
a6780fa2 540 self.ReloadDemo()
08ecc920
RD
541
542
543 def LoadDemoSource(self, source):
544 self.editor.Clear()
545 self.editor.SetValue(source)
546 self.JumpToLine(0)
547 self.btnSave.Enable(False)
548
549
550 def JumpToLine(self, line, highlight=False):
551 self.editor.GotoLine(line)
552 self.editor.SetFocus()
553 if highlight:
554 self.editor.SelectLine(line)
555
556
557 def UpdateControlState(self):
558 active = self.demoModules.GetActiveID()
559 # Update the radio/restore buttons
560 for moduleID in self.radioButtons:
561 btn = self.radioButtons[moduleID]
562 if moduleID == active:
563 btn.SetValue(True)
564 else:
565 btn.SetValue(False)
566
567 if self.demoModules.Exists(moduleID):
568 btn.Enable(True)
569 if moduleID == modModified:
570 self.btnRestore.Enable(True)
571 else:
572 btn.Enable(False)
573 if moduleID == modModified:
574 self.btnRestore.Enable(False)
575
576
577 def OnRadioButton(self, event):
578 radioSelected = event.GetEventObject()
579 modSelected = radioSelected.modID
580 if modSelected != self.demoModules.GetActiveID():
581 busy = wx.BusyInfo("Reloading demo module...")
582 self.demoModules.SetActive(modSelected)
583 self.ActiveModuleChanged()
584
585
586 def ReloadDemo(self):
587 if self.demoModules.name != __name__:
a6780fa2 588 self.mainFrame.RunModule()
08ecc920
RD
589
590
591 def OnCodeModified(self, event):
592 self.btnSave.Enable(self.editor.IsModified())
593
594
595 def OnSave(self, event):
596 if self.demoModules.Exists(modModified):
597 if self.demoModules.GetActiveID() == modOriginal:
598 overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
599 "Do you want to continue?"
600 dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
601 wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
602 result = dlg.ShowModal()
603 if result == wx.ID_NO:
604 return
605 dlg.Destroy()
606
607 self.demoModules.SetActive(modModified)
608 modifiedFilename = GetModifiedFilename(self.demoModules.name)
609
610 # Create the demo directory if one doesn't already exist
611 if not os.path.exists(GetModifiedDirectory()):
612 try:
613 os.makedirs(GetModifiedDirectory())
614 if not os.path.exists(GetModifiedDirectory()):
615 wx.LogMessage("BUG: Created demo directory but it still doesn't exit")
616 raise AssetionError
617 except:
618 wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
619 return
620 else:
621 wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
622
623 # Save
887a1bd9 624 f = open(modifiedFilename, "wt")
08ecc920
RD
625 source = self.editor.GetText()
626 try:
627 f.write(source)
628 finally:
629 f.close()
630
631 busy = wx.BusyInfo("Reloading demo module...")
632 self.demoModules.LoadFromFile(modModified, modifiedFilename)
633 self.ActiveModuleChanged()
634
635
636 def OnRestore(self, event): # Handles the "Delete Modified" button
637 modifiedFilename = GetModifiedFilename(self.demoModules.name)
638 self.demoModules.Delete(modModified)
639 os.unlink(modifiedFilename) # Delete the modified copy
640 busy = wx.BusyInfo("Reloading demo module...")
641 self.ActiveModuleChanged()
642
1fded56b 643
6c5ae2d2
RD
644#---------------------------------------------------------------------------
645
646def opj(path):
647 """Convert paths to the platform-specific separator"""
08ecc920
RD
648 str = apply(os.path.join, tuple(path.split('/')))
649 # HACK: on Linux, a leading / gets lost...
650 if path.startswith('/'):
651 str = '/' + str
652 return str
653
654
655def GetModifiedDirectory():
656 """
657 Returns the directory where modified versions of the demo files
658 are stored
659 """
660 return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
661
662
663def GetModifiedFilename(name):
664 """
665 Returns the filename of the modified version of the specified demo
666 """
667 if not name.endswith(".py"):
668 name = name + ".py"
669 return GetModifiedDirectory() + name
670
671
672def GetOriginalFilename(name):
673 """
674 Returns the filename of the original version of the specified demo
675 """
676 if not name.endswith(".py"):
677 name = name + ".py"
678 return name
679
680
681def DoesModifiedExist(name):
682 """Returns whether the specified demo has a modified copy"""
683 if os.path.exists(GetModifiedFilename(name)):
684 return True
685 else:
686 return False
687
688
689#---------------------------------------------------------------------------
690
691class ModuleDictWrapper:
692 """Emulates a module with a dynamically compiled __dict__"""
693 def __init__(self, dict):
694 self.dict = dict
695
696 def __getattr__(self, name):
697 if name in self.dict:
698 return self.dict[name]
699 else:
700 raise AttributeError
701
702class DemoModules:
703 """
704 Dynamically manages the original/modified versions of a demo
705 module
706 """
707 def __init__(self, name):
708 self.modActive = -1
709 self.name = name
710
711 # (dict , source , filename , description , error information )
712 # ( 0 , 1 , 2 , 3 , 4 )
713 self.modules = [[None, "" , "" , "<original>" , None],
714 [None, "" , "" , "<modified>" , None]]
715
716 # load original module
717 self.LoadFromFile(modOriginal, GetOriginalFilename(name))
317a64e5 718 self.SetActive(modOriginal)
08ecc920
RD
719
720 # load modified module (if one exists)
721 if DoesModifiedExist(name):
722 self.LoadFromFile(modModified, GetModifiedFilename(name))
723
724
725 def LoadFromFile(self, modID, filename):
726 self.modules[modID][2] = filename
887a1bd9 727 file = open(filename, "rt")
08ecc920
RD
728 self.LoadFromSource(modID, file.read())
729 file.close()
730
731
732 def LoadFromSource(self, modID, source):
733 self.modules[modID][1] = source
734 self.LoadDict(modID)
735
736
737 def LoadDict(self, modID):
738 if self.name != __name__:
739 source = self.modules[modID][1]
740 description = self.modules[modID][3]
741
742 try:
743 self.modules[modID][0] = {}
744 code = compile(source, description, "exec")
745 exec code in self.modules[modID][0]
746 except:
747 self.modules[modID][4] = DemoError(sys.exc_info())
748 self.modules[modID][0] = None
749 else:
750 self.modules[modID][4] = None
751
752
753 def SetActive(self, modID):
754 if modID != modOriginal and modID != modModified:
755 raise LookupError
756 else:
757 self.modActive = modID
758
759
760 def GetActive(self):
761 dict = self.modules[self.modActive][0]
762 if dict is None:
763 return None
764 else:
765 return ModuleDictWrapper(dict)
766
767
768 def GetActiveID(self):
769 return self.modActive
770
771
772 def GetSource(self, modID = None):
773 if modID is None:
774 modID = self.modActive
775 return self.modules[modID][1]
776
777
778 def GetFilename(self, modID = None):
779 if modID is None:
780 modID = self.modActive
781 return self.modules[self.modActive][2]
782
783
784 def GetErrorInfo(self, modID = None):
785 if modID is None:
786 modID = self.modActive
787 return self.modules[self.modActive][4]
788
789
790 def Exists(self, modID):
791 return self.modules[modID][1] != ""
792
793
794 def UpdateFile(self, modID = None):
795 """Updates the file from which a module was loaded
796 with (possibly updated) source"""
797 if modID is None:
798 modID = self.modActive
799
800 source = self.modules[modID][1]
801 filename = self.modules[modID][2]
802
803 try:
887a1bd9 804 file = open(filename, "wt")
08ecc920
RD
805 file.write(source)
806 finally:
807 file.close()
808
809
810 def Delete(self, modID):
811 if self.modActive == modID:
812 self.SetActive(0)
813
814 self.modules[modID][0] = None
815 self.modules[modID][1] = ""
816 self.modules[modID][2] = ""
817
818
08ecc920
RD
819#---------------------------------------------------------------------------
820
821class DemoError:
822 """Wraps and stores information about the current exception"""
823 def __init__(self, exc_info):
824 import copy
825
826 excType, excValue = exc_info[:2]
827 # traceback list entries: (filename, line number, function name, text)
828 self.traceback = traceback.extract_tb(exc_info[2])
829
830 # --Based on traceback.py::format_exception_only()--
831 if type(excType) == types.ClassType:
832 self.exception_type = excType.__name__
833 else:
834 self.exception_type = excType
835
836 # If it's a syntax error, extra information needs
837 # to be added to the traceback
838 if excType is SyntaxError:
839 try:
840 msg, (filename, lineno, self.offset, line) = excValue
841 except:
842 pass
843 else:
844 if not filename:
845 filename = "<string>"
846 line = line.strip()
847 self.traceback.append( (filename, lineno, "", line) )
848 excValue = msg
849 try:
850 self.exception_details = str(excValue)
851 except:
852 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
853
854 del exc_info
855
856 def __str__(self):
857 ret = "Type %s \n \
858 Traceback: %s \n \
859 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
860 return ret
861
862#---------------------------------------------------------------------------
863
864class DemoErrorPanel(wx.Panel):
865 """Panel put into the demo tab when the demo fails to run due to errors"""
866
867 def __init__(self, parent, codePanel, demoError, log):
868 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
869 self.codePanel = codePanel
870 self.nb = parent
871 self.log = log
872
873 self.box = wx.BoxSizer(wx.VERTICAL)
874
875 # Main Label
876 self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
877 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
878
879 # Exception Information
880 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
881 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
882 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
883 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
884 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
885 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
886 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
887 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
888 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
889 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
890
891 # Set up the traceback list
892 # This one automatically resizes last column to take up remaining space
893 from ListCtrl import TestListCtrl
894 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
895 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
896 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
897 self.list.InsertColumn(0, "Filename")
898 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
899 self.list.InsertColumn(2, "Function")
900 self.list.InsertColumn(3, "Code")
901 self.InsertTraceback(self.list, demoError.traceback)
902 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
903 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
904 self.box.Add(wx.StaticText(self, -1, "Traceback:")
905 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
906 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
907 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
908 + "Double-click on them to go to the offending line")
909 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
910
911 self.box.Fit(self)
912 self.SetSizer(self.box)
913
914
915 def InsertTraceback(self, list, traceback):
916 #Add the traceback data
917 for x in range(len(traceback)):
918 data = traceback[x]
919 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
920 list.SetStringItem(x, 1, str(data[1])) # Line
921 list.SetStringItem(x, 2, str(data[2])) # Function
922 list.SetStringItem(x, 3, str(data[3])) # Code
923
924 # Check whether this entry is from the demo module
925 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
926 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
927 # Give it a blue colour
928 item = self.list.GetItem(x)
929 item.SetTextColour(wx.BLUE)
930 self.list.SetItem(item)
931 else:
932 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
933
934
935 def OnItemSelected(self, event):
936 # This occurs before OnDoubleClick and can be used to set the
937 # currentItem. OnDoubleClick doesn't get a wxListEvent....
938 self.currentItem = event.m_itemIndex
939 event.Skip()
940
941
942 def OnDoubleClick(self, event):
943 # If double-clicking on a demo's entry, jump to the line number
944 line = self.list.GetItemData(self.currentItem)
945 if line != -1:
946 self.nb.SetSelection(1) # Switch to the code viewer tab
947 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
948 event.Skip()
949
6c5ae2d2 950
76bfdc78
RD
951#---------------------------------------------------------------------------
952
1cc18e15
RD
953class DemoTaskBarIcon(wx.TaskBarIcon):
954 TBMENU_RESTORE = wx.NewId()
955 TBMENU_CLOSE = wx.NewId()
956 TBMENU_CHANGE = wx.NewId()
957 TBMENU_REMOVE = wx.NewId()
958
959 def __init__(self, frame):
960 wx.TaskBarIcon.__init__(self)
961 self.frame = frame
962
963 # Set the image
8596dc0b 964 icon = self.MakeIcon(images.getWXPdemoImage())
1cc18e15 965 self.SetIcon(icon, "wxPython Demo")
8596dc0b 966 self.imgidx = 1
1cc18e15
RD
967
968 # bind some events
969 self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
970 self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
971 self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
972 self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
973 self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
974
975
976 def CreatePopupMenu(self):
977 """
978 This method is called by the base class when it needs to popup
979 the menu for the default EVT_RIGHT_DOWN event. Just create
980 the menu how you want it and return it from this function,
981 the base class takes care of the rest.
982 """
983 menu = wx.Menu()
984 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
985 menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
986 menu.AppendSeparator()
987 menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
988 menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
989 return menu
990
991
992 def MakeIcon(self, img):
993 """
994 The various platforms have different requirements for the
995 icon size...
996 """
997 if "wxMSW" in wx.PlatformInfo:
8596dc0b 998 img = img.Scale(16, 16)
1cc18e15 999 elif "wxGTK" in wx.PlatformInfo:
8596dc0b
RD
1000 img = img.Scale(22, 22)
1001 # wxMac can be any size upto 128x128, so leave the source img alone....
1cc18e15
RD
1002 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
1003 return icon
1004
1005
1006 def OnTaskBarActivate(self, evt):
1007 if self.frame.IsIconized():
1008 self.frame.Iconize(False)
1009 if not self.frame.IsShown():
1010 self.frame.Show(True)
1011 self.frame.Raise()
1012
1013
1014 def OnTaskBarClose(self, evt):
1015 self.frame.Close()
1016
1cc18e15
RD
1017
1018 def OnTaskBarChange(self, evt):
8596dc0b
RD
1019 names = [ "WXPdemo", "WXP", "Mondrian", "Test2m",
1020 "Blom08m", "Blom10m", "Blom15m" ]
1021 name = names[self.imgidx]
1022
1023 getFunc = getattr(images, "get%sImage" % name)
1024 self.imgidx += 1
1025 if self.imgidx >= len(names):
1026 self.imgidx = 0
1027
1028 icon = self.MakeIcon(getFunc())
1029 self.SetIcon(icon, "This is a new icon: " + name)
1cc18e15
RD
1030
1031
1032 def OnTaskBarRemove(self, evt):
1033 self.RemoveIcon()
1034
1035
1036#---------------------------------------------------------------------------
1fded56b 1037class wxPythonDemo(wx.Frame):
e9159fe8 1038 overviewText = "wxPython Overview"
c368d904 1039
08ecc920 1040 def __init__(self, parent, title):
8ee202f3 1041 wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
08ecc920 1042 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
2f90df85 1043
08ecc920 1044 self.loaded = False
694759cf 1045 self.cwd = os.getcwd()
3ca6a5f0 1046 self.curOverview = ""
08ecc920
RD
1047 self.demoPage = None
1048 self.codePage = None
887a1bd9 1049 self.shell = None
a6780fa2 1050 self.firstTime = True
694759cf 1051
8596dc0b 1052 icon = images.getWXPdemoIcon()
96bfd053 1053 self.SetIcon(icon)
c368d904 1054
1cc18e15 1055 self.tbicon = DemoTaskBarIcon(self)
c368d904 1056
1fded56b 1057 wx.CallAfter(self.ShowTip)
cf694132
RD
1058
1059 self.otherWin = None
80b27b4e
RD
1060 self.Bind(wx.EVT_IDLE, self.OnIdle)
1061 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1062 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1063 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
cf694132 1064
1fded56b
RD
1065 self.Centre(wx.BOTH)
1066 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
5a7823f5 1067
0c8f2860
RD
1068 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1069 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
5a7823f5 1070
d56cebe7 1071 def EmptyHandler(evt): pass
80b27b4e
RD
1072 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1073 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
cf694132 1074
1e4a197e
RD
1075 # Prevent TreeCtrl from displaying all items after destruction when True
1076 self.dying = False
cf694132 1077
08ecc920
RD
1078 # Create a Notebook
1079 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1080
cf694132 1081 # Make a File menu
1fded56b
RD
1082 self.mainmenu = wx.MenuBar()
1083 menu = wx.Menu()
330af869
RD
1084 item = menu.Append(-1, '&Redirect Output',
1085 'Redirect print statements to a window',
1086 wx.ITEM_CHECK)
1087 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
08ecc920 1088
c8000995
RD
1089 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1090 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
1091 wx.App_SetMacExitMenuItemId(item.GetId())
cf694132
RD
1092 self.mainmenu.Append(menu, '&File')
1093
ec3e670f 1094 # Make a Demo menu
1fded56b 1095 menu = wx.Menu()
ec3e670f 1096 for item in _treeList:
1fded56b 1097 submenu = wx.Menu()
ec3e670f 1098 for childItem in item[1]:
c8000995
RD
1099 mi = submenu.Append(-1, childItem)
1100 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1fded56b 1101 menu.AppendMenu(wx.NewId(), item[0], submenu)
ec3e670f
RD
1102 self.mainmenu.Append(menu, '&Demo')
1103
08ecc920
RD
1104 # Make a Demo Code menu
1105 #TODO: Add new menu items
1106 # Like the option-enabled entries to select the
1107 # active module
1108 #TODO: should we bother?
1109
1110 #menu = wx.Menu()
1111 #saveID = wx.NewId()
1112 #restoreID = wx.NewId()
1113 #
1114 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1115 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1116 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1117 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1118 #self.mainmenu.Append(menu, 'Demo &Code')
1119 #
ec3e670f 1120
cf694132 1121 # Make a Help menu
1fded56b 1122 menu = wx.Menu()
c8000995
RD
1123 findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
1124 findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
1e4a197e 1125 menu.AppendSeparator()
887a1bd9
RD
1126
1127 shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
1128 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1129 menu.AppendSeparator()
c8000995
RD
1130 helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
1131 wx.App_SetMacAboutMenuItemId(helpItem.GetId())
887a1bd9
RD
1132
1133 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
c8000995
RD
1134 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1135 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1136 self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
80b27b4e
RD
1137 self.Bind(wx.EVT_COMMAND_FIND, self.OnFind)
1138 self.Bind(wx.EVT_COMMAND_FIND_NEXT, self.OnFind)
1139 self.Bind(wx.EVT_COMMAND_FIND_CLOSE, self.OnFindClose)
cf694132
RD
1140 self.mainmenu.Append(menu, '&Help')
1141 self.SetMenuBar(self.mainmenu)
1142
1fded56b 1143 self.finddata = wx.FindReplaceData()
1e4a197e
RD
1144
1145 if 0:
1146 # This is another way to set Accelerators, in addition to
1147 # using the '\t<key>' syntax in the menu items.
1fded56b
RD
1148 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1149 (wx.ACCEL_CTRL, ord('H'), helpID),
1150 (wx.ACCEL_CTRL, ord('F'), findID),
1151 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1152 ])
1e4a197e 1153 self.SetAcceleratorTable(aTable)
2f90df85 1154
bb0054cd 1155
cf694132 1156 # Create a TreeCtrl
1fded56b 1157 tID = wx.NewId()
f6bcfd97 1158 self.treeMap = {}
3628e088
RD
1159 self.tree = wx.TreeCtrl(splitter, tID, style =
1160 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
eb0f373c 1161 )
afb810d9 1162
e9159fe8 1163 root = self.tree.AddRoot("wxPython Overview")
f6bcfd97
BP
1164 firstChild = None
1165 for item in _treeList:
1166 child = self.tree.AppendItem(root, item[0])
1167 if not firstChild: firstChild = child
1168 for childItem in item[1]:
1169 theDemo = self.tree.AppendItem(child, childItem)
1170 self.treeMap[childItem] = theDemo
1171
1172 self.tree.Expand(root)
1173 self.tree.Expand(firstChild)
80b27b4e
RD
1174 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1175 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1176 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1177 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
cf694132 1178
1fded56b 1179 # Set up a wx.html.HtmlWindow on the Overview Notebook page
0bdca46d
RD
1180 # we put it in a panel first because there seems to be a
1181 # refresh bug of some sort (wxGTK) when it is directly in
1182 # the notebook...
1183 if 0: # the old way
1fded56b 1184 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
0bdca46d
RD
1185 self.nb.AddPage(self.ovr, self.overviewText)
1186
1fded56b
RD
1187 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1188 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1189 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
0bdca46d
RD
1190 self.nb.AddPage(panel, self.overviewText)
1191
1192 def OnOvrSize(evt, ovr=self.ovr):
1193 ovr.SetSize(evt.GetSize())
80b27b4e
RD
1194 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1195 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
0bdca46d 1196
08ecc920 1197 if "gtk2" in wx.PlatformInfo:
385721a8 1198 self.ovr.SetStandardFonts()
08ecc920 1199 self.SetOverview(self.overviewText, mainOverview)
c368d904 1200
cf694132 1201
08ecc920
RD
1202 # Set up a log window
1203 self.log = wx.TextCtrl(splitter2, -1,
1204 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1205
1206 # Set the wxWindows log target to be this textctrl
1207 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
cf694132 1208
08ecc920
RD
1209 # But instead of the above we want to show how to use our own wx.Log class
1210 wx.Log_SetActiveTarget(MyLog(self.log))
1211
1212 # for serious debugging
1213 #wx.Log_SetActiveTarget(wx.LogStderr())
1214 #wx.Log_SetTraceMask(wx.TraceMessages)
cf694132
RD
1215
1216
e44b5196
RD
1217 self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
1218 wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
1219
f6bcfd97 1220 # add the windows to the splitter and split it.
08ecc920
RD
1221 splitter2.SplitHorizontally(self.nb, self.log, -160)
1222 splitter.SplitVertically(self.tree, splitter2, 200)
afb810d9 1223
f6bcfd97 1224 splitter.SetMinimumPaneSize(20)
afb810d9
RD
1225 splitter2.SetMinimumPaneSize(20)
1226
11926a78 1227 # Make the splitter on the right expand the top window when resized
2f0f3b0f
RD
1228 def SplitterOnSize(evt):
1229 splitter = evt.GetEventObject()
1230 sz = splitter.GetSize()
08ecc920 1231 splitter.SetSashPosition(sz.height - 160, False)
2f0f3b0f 1232 evt.Skip()
80b27b4e
RD
1233
1234 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
2f0f3b0f 1235
bb0054cd
RD
1236 # select initial items
1237 self.nb.SetSelection(0)
f6bcfd97 1238 self.tree.SelectItem(root)
ec3e670f 1239
08ecc920
RD
1240 # Load 'Main' module
1241 self.LoadDemo(self.overviewText)
1242 self.loaded = True
1243
1244 # select some other initial module?
1245 if len(sys.argv) > 1:
1246 arg = sys.argv[1]
1247 if arg.endswith('.py'):
1248 arg = arg[:-3]
1249 selectedDemo = self.treeMap.get(arg, None)
f6bcfd97 1250 if selectedDemo:
ec3e670f
RD
1251 self.tree.SelectItem(selectedDemo)
1252 self.tree.EnsureVisible(selectedDemo)
1253
bb0054cd 1254
cf694132
RD
1255 #---------------------------------------------
1256 def WriteText(self, text):
f6bcfd97
BP
1257 if text[-1:] == '\n':
1258 text = text[:-1]
1fded56b 1259 wx.LogMessage(text)
f6bcfd97 1260
cf694132
RD
1261 def write(self, txt):
1262 self.WriteText(txt)
1263
1264 #---------------------------------------------
1265 def OnItemExpanded(self, event):
1266 item = event.GetItem()
1fded56b 1267 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
c368d904 1268 event.Skip()
cf694132
RD
1269
1270 #---------------------------------------------
1271 def OnItemCollapsed(self, event):
1272 item = event.GetItem()
1fded56b 1273 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
c368d904 1274 event.Skip()
f6bcfd97
BP
1275
1276 #---------------------------------------------
1277 def OnTreeLeftDown(self, event):
08ecc920 1278 # reset the overview text if the tree item is clicked on again
f6bcfd97
BP
1279 pt = event.GetPosition();
1280 item, flags = self.tree.HitTest(pt)
1281 if item == self.tree.GetSelection():
d975da9b 1282 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
185d7c3e 1283 event.Skip()
cf694132
RD
1284
1285 #---------------------------------------------
1286 def OnSelChanged(self, event):
08ecc920 1287 if self.dying or not self.loaded:
cf694132
RD
1288 return
1289
5a7823f5
RD
1290 item = event.GetItem()
1291 itemText = self.tree.GetItemText(item)
08ecc920 1292 self.LoadDemo(itemText)
5a7823f5
RD
1293
1294 #---------------------------------------------
08ecc920
RD
1295 def LoadDemo(self, demoName):
1296 try:
1297 wx.BeginBusyCursor()
1298
1299 os.chdir(self.cwd)
1300 self.ShutdownDemoModule()
1301
1302 if demoName == self.overviewText:
1303 # User selected the "wxPython Overview" node
1304 # ie: _this_ module
1305 # Changing the main window at runtime not yet supported...
1306 self.demoModules = DemoModules(__name__)
1307 self.SetOverview(self.overviewText, mainOverview)
1308 self.LoadDemoSource()
1309 self.UpdateNotebook(0)
1310 else:
1311 if os.path.exists(GetOriginalFilename(demoName)):
1312 wx.LogMessage("Loading demo %s.py..." % demoName)
1313 self.demoModules = DemoModules(demoName)
a6780fa2 1314 self.LoadDemoSource()
08ecc920
RD
1315 self.tree.Refresh()
1316 else:
1317 self.SetOverview("wxPython", mainOverview)
1318 self.codePage = None
1319 self.UpdateNotebook(0)
1320 finally:
1321 wx.EndBusyCursor()
cf694132 1322
08ecc920
RD
1323 #---------------------------------------------
1324 def LoadDemoSource(self):
1325 self.codePage = None
1326 self.codePage = DemoCodePanel(self.nb, self)
1327 self.codePage.LoadDemo(self.demoModules)
1328
1329 #---------------------------------------------
a6780fa2 1330 def RunModule(self):
08ecc920 1331 """Runs the active module"""
cf694132 1332
08ecc920
RD
1333 module = self.demoModules.GetActive()
1334 self.ShutdownDemoModule()
1335 overviewText = ""
1336
8412feb0
RD
1337 # o The RunTest() for all samples must now return a window that can
1338 # be palced in a tab in the main notebook.
08ecc920
RD
1339 # o If an error occurs (or has occured before) an error tab is created.
1340
1341 if module is not None:
1342 wx.LogMessage("Running demo module...")
1343 if hasattr(module, "overview"):
1344 overviewText = module.overview
cf694132 1345
08ecc920
RD
1346 try:
1347 self.demoPage = module.runTest(self, self.nb, self)
08ecc920 1348 except:
317a64e5 1349 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
7d0f8766
RD
1350 DemoError(sys.exc_info()), self)
1351
1352 assert self.demoPage is not None, "runTest must return a window!"
1353
08ecc920
RD
1354 else:
1355 # There was a previous error in compiling or exec-ing
317a64e5
RD
1356 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1357 self.demoModules.GetErrorInfo(), self)
a6780fa2 1358
08ecc920 1359 self.SetOverview(self.demoModules.name + " Overview", overviewText)
a6780fa2
RD
1360
1361 if self.firstTime:
1362 # cahnge to the demo page the first time a module is run
1363 self.UpdateNotebook(2)
1364 self.firstTime = False
1365 else:
1366 # otherwise just stay on the same tab in case the user has changed to another one
1367 self.UpdateNotebook()
cf694132
RD
1368
1369 #---------------------------------------------
08ecc920
RD
1370 def ShutdownDemoModule(self):
1371 if self.demoPage:
1372 # inform the window that it's time to quit if it cares
1373 if hasattr(self.demoPage, "ShutdownDemo"):
1374 self.demoPage.ShutdownDemo()
1375 wx.YieldIfNeeded() # in case the page has pending events
1376 self.demoPage = None
1377
1378 #---------------------------------------------
1379 def UpdateNotebook(self, select = -1):
1380 nb = self.nb
1381 debug = False
1382
1383 def UpdatePage(page, pageText):
1384 pageExists = False
1385 pagePos = -1
1386 for i in range(nb.GetPageCount()):
1387 if nb.GetPageText(i) == pageText:
1388 pageExists = True
1389 pagePos = i
1390 break
1391
1392 if page:
1393 if not pageExists:
1394 # Add a new page
08ecc920
RD
1395 nb.AddPage(page, pageText)
1396 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1397 else:
08ecc920
RD
1398 if nb.GetPage(pagePos) != page:
1399 # Reload an existing page
1400 nb.Freeze()
08ecc920
RD
1401 nb.DeletePage(pagePos)
1402 nb.InsertPage(pagePos, page, pageText)
1403 nb.Thaw()
1404 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1405 else:
1406 # Excellent! No redraw/flicker
1407 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1408 elif pageExists:
1409 # Delete a page
1410 nb.DeletePage(pagePos)
1411 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1412 else:
1413 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1414
1415 if select == -1:
1416 select = nb.GetSelection()
cf694132 1417
08ecc920
RD
1418 UpdatePage(self.codePage, "Demo Code")
1419 UpdatePage(self.demoPage, "Demo")
cf694132 1420
887a1bd9 1421 if select >= 0 and select < nb.GetPageCount():
08ecc920
RD
1422 nb.SetSelection(select)
1423
cf694132
RD
1424 #---------------------------------------------
1425 def SetOverview(self, name, text):
f6bcfd97
BP
1426 self.curOverview = text
1427 lead = text[:6]
1428 if lead != '<html>' and lead != '<HTML>':
1e4a197e 1429 text = '<br>'.join(text.split('\n'))
6c7d1792
RD
1430 if wx.USE_UNICODE:
1431 text = text.decode('iso8859_1')
f6bcfd97 1432 self.ovr.SetPage(text)
cf694132 1433 self.nb.SetPageText(0, name)
cf694132
RD
1434
1435 #---------------------------------------------
1436 # Menu methods
c368d904 1437 def OnFileExit(self, *event):
cf694132
RD
1438 self.Close()
1439
330af869
RD
1440 def OnToggleRedirect(self, event):
1441 app = wx.GetApp()
1442 if event.Checked():
1443 app.RedirectStdio()
1444 print "Print statements and other standard output will now be directed to this window."
1445 else:
1446 app.RestoreStdio()
1447 print "Print statements and other standard output will now be sent to the usual location."
08ecc920 1448
cf694132 1449 def OnHelpAbout(self, event):
e166644c 1450 from About import MyAboutBox
ec3e670f 1451 about = MyAboutBox(self)
cf694132
RD
1452 about.ShowModal()
1453 about.Destroy()
1454
1e4a197e
RD
1455 def OnHelpFind(self, event):
1456 self.nb.SetSelection(1)
1fded56b
RD
1457 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1458 wx.FR_NOUPDOWN |
1459 wx.FR_NOMATCHCASE |
1460 wx.FR_NOWHOLEWORD)
1e4a197e
RD
1461 self.finddlg.Show(True)
1462
1463 def OnFind(self, event):
08ecc920 1464 editor = self.codePage.editor
1e4a197e 1465 self.nb.SetSelection(1)
08ecc920
RD
1466 end = editor.GetLastPosition()
1467 textstring = editor.GetRange(0, end).lower()
1468 start = editor.GetSelection()[1]
1e4a197e
RD
1469 findstring = self.finddata.GetFindString().lower()
1470 loc = textstring.find(findstring, start)
1471 if loc == -1 and start != 0:
1472 # string not found, start at beginning
1473 start = 0
1474 loc = textstring.find(findstring, start)
1475 if loc == -1:
1fded56b 1476 dlg = wx.MessageDialog(self, 'Find String Not Found',
1e4a197e 1477 'Find String Not Found in Demo File',
1fded56b 1478 wx.OK | wx.ICON_INFORMATION)
1e4a197e
RD
1479 dlg.ShowModal()
1480 dlg.Destroy()
1481 if self.finddlg:
1482 if loc == -1:
1483 self.finddlg.SetFocus()
1484 return
1485 else:
1486 self.finddlg.Destroy()
08ecc920
RD
1487 editor.ShowPosition(loc)
1488 editor.SetSelection(loc, loc + len(findstring))
1e4a197e
RD
1489
1490
1491
1492 def OnFindNext(self, event):
1493 if self.finddata.GetFindString():
1494 self.OnFind(event)
1495 else:
1496 self.OnHelpFind(event)
1497
1498 def OnFindClose(self, event):
1499 event.GetDialog().Destroy()
1500
cf694132 1501
887a1bd9
RD
1502 def OnOpenShellWindow(self, evt):
1503 if self.shell:
1504 # if it already exists then just make sure it's visible
1505 s = self.shell
1506 if s.IsIconized():
1507 s.Iconize(False)
1508 s.Raise()
1509 else:
1510 # Make a PyShell window
1511 from wx import py
1512 namespace = { 'wx' : wx,
1513 'app' : wx.GetApp(),
1514 'frame' : self,
1515 }
1516 self.shell = py.shell.ShellFrame(None, locals=namespace)
1517 self.shell.SetSize((640,480))
1518 self.shell.Show()
1519
1520 # Hook the close event of the main frame window so that we
1521 # close the shell at the same time if it still exists
1522 def CloseShell(evt):
1523 if self.shell:
1524 self.shell.Close()
1525 evt.Skip()
1526 self.Bind(wx.EVT_CLOSE, CloseShell)
1527
1528
cf694132
RD
1529 #---------------------------------------------
1530 def OnCloseWindow(self, event):
1e4a197e 1531 self.dying = True
08ecc920
RD
1532 self.demoPage = None
1533 self.codePage = None
26197023 1534 self.mainmenu = None
1cc18e15 1535 self.tbicon.Destroy()
cf694132
RD
1536 self.Destroy()
1537
c368d904 1538
cf694132
RD
1539 #---------------------------------------------
1540 def OnIdle(self, event):
1541 if self.otherWin:
1542 self.otherWin.Raise()
08ecc920 1543 self.demoPage = self.otherWin
cf694132
RD
1544 self.otherWin = None
1545
ccf691a4
RD
1546
1547 #---------------------------------------------
1548 def ShowTip(self):
1549 try:
1550 showTipText = open(opj("data/showTips")).read()
1551 showTip, index = eval(showTipText)
1552 except IOError:
1553 showTip, index = (1, 0)
1554 if showTip:
1fded56b 1555 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
861a0196 1556 ##tp = MyTP(0)
1fded56b 1557 showTip = wx.ShowTip(self, tp)
ccf691a4
RD
1558 index = tp.GetCurrentTip()
1559 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1560
1561
ec3e670f
RD
1562 #---------------------------------------------
1563 def OnDemoMenu(self, event):
f6bcfd97
BP
1564 try:
1565 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1566 except:
1567 selectedDemo = None
1568 if selectedDemo:
1569 self.tree.SelectItem(selectedDemo)
1570 self.tree.EnsureVisible(selectedDemo)
ec3e670f 1571
c368d904 1572
c368d904 1573
f3d9dc1d
RD
1574 #---------------------------------------------
1575 def OnIconfiy(self, evt):
8412feb0 1576 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
f3d9dc1d
RD
1577 evt.Skip()
1578
1579 #---------------------------------------------
1580 def OnMaximize(self, evt):
1fded56b 1581 wx.LogMessage("OnMaximize")
f3d9dc1d
RD
1582 evt.Skip()
1583
8412feb0
RD
1584 #---------------------------------------------
1585 def OnActivate(self, evt):
1586 wx.LogMessage("OnActivate: %s" % evt.GetActive())
1587 evt.Skip()
f3d9dc1d 1588
8412feb0
RD
1589 #---------------------------------------------
1590 def OnAppActivate(self, evt):
1591 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
1592 evt.Skip()
f3d9dc1d 1593
cf694132
RD
1594#---------------------------------------------------------------------------
1595#---------------------------------------------------------------------------
1596
1fded56b 1597class MySplashScreen(wx.SplashScreen):
b5a5d647 1598 def __init__(self):
1fded56b
RD
1599 bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
1600 wx.SplashScreen.__init__(self, bmp,
a253aa20
RD
1601 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1602 3000, None, -1)
80b27b4e 1603 self.Bind(wx.EVT_CLOSE, self.OnClose)
b5a5d647
RD
1604
1605 def OnClose(self, evt):
8eca4fef 1606 self.Hide()
08ecc920 1607 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1e4a197e 1608 frame.Show()
ccf691a4 1609 evt.Skip() # Make sure the default handler runs too...
cf694132 1610
b5a5d647 1611
1fded56b 1612class MyApp(wx.App):
b5a5d647
RD
1613 def OnInit(self):
1614 """
68320e40 1615 Create and show the splash screen. It will then create and show
b5a5d647
RD
1616 the main frame when it is time to do so.
1617 """
1e4a197e 1618
35ee3288
RD
1619 # For debugging
1620 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1621
a253aa20
RD
1622 # Normally when using a SplashScreen you would create it, show
1623 # it and then continue on with the applicaiton's
1624 # initialization, finally creating and showing the main
1625 # application window(s). In this case we have nothing else to
1626 # do so we'll delay showing the main frame until later (see
b7aef858 1627 # OnClose above) so the users can see the SplashScreen effect.
b5a5d647
RD
1628 splash = MySplashScreen()
1629 splash.Show()
a253aa20 1630
1e4a197e 1631 return True
b5a5d647
RD
1632
1633
1634
cf694132
RD
1635#---------------------------------------------------------------------------
1636
1637def main():
e02c03a4 1638 try:
d56cebe7 1639 demoPath = os.path.dirname(__file__)
e02c03a4
RD
1640 os.chdir(demoPath)
1641 except:
1642 pass
03a604a6 1643 app = MyApp(False)
cf694132
RD
1644 app.MainLoop()
1645
cf694132
RD
1646#---------------------------------------------------------------------------
1647
1648
08ecc920 1649mainOverview = """<html><body>
1fded56b
RD
1650<h2>wxPython</h2>
1651
f6063c19
RD
1652<p> wxPython is a <b>GUI toolkit</b> for the Python programming
1653language. It allows Python programmers to create programs with a
1654robust, highly functional graphical user interface, simply and easily.
1655It is implemented as a Python extension module (native code) that
1656wraps the popular wxWindows cross platform GUI library, which is
1657written in C++.
1fded56b
RD
1658
1659<p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1660means that it is free for anyone to use and the source code is
1661available for anyone to look at and modify. Or anyone can contribute
8b9a4190 1662fixes or enhancements to the project.
1fded56b
RD
1663
1664<p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1665same program will run on multiple platforms without modification.
1666Currently supported platforms are 32-bit Microsoft Windows, most Unix
1667or unix-like systems, and Macintosh OS X. Since the language is
1668Python, wxPython programs are <b>simple, easy</b> to write and easy to
1669understand.
1670
1671<p> <b>This demo</b> is not only a collection of test cases for
1672wxPython, but is also designed to help you learn about and how to use
1673wxPython. Each sample is listed in the tree control on the left.
1674When a sample is selected in the tree then a module is loaded and run
1675(usually in a tab of this notebook,) and the source code of the module
1676is loaded in another tab for you to browse and learn from.
1677
1678"""
cf694132
RD
1679
1680
1681#----------------------------------------------------------------------------
1682#----------------------------------------------------------------------------
1683
1684if __name__ == '__main__':
08ecc920 1685 __name__ = 'Main'
cf694132
RD
1686 main()
1687
1688#----------------------------------------------------------------------------
1689
1690
1691
1692
1693
1694
1695