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