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