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