]> git.saurik.com Git - wxWidgets.git/blame - wxPython/demo/Main.py
MaskedEdit updates from Will Sadkin:
[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
e44b5196 401 self.StyleSetSpec(wx.stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2')
80b27b4e
RD
402 # Highlighted brace
403 self.StyleSetSpec(wx.stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00')
404 # Unmatched brace
405 self.StyleSetSpec(wx.stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000')
406 # Indentation guide
407 self.StyleSetSpec(wx.stc.STC_STYLE_INDENTGUIDE, "fore:#CDCDCD")
408
409 # Python styles
410 self.StyleSetSpec(wx.stc.STC_P_DEFAULT, 'fore:#000000')
411 # Comments
a253aa20
RD
412 self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, 'fore:#008000,back:#F0FFF0')
413 self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, 'fore:#008000,back:#F0FFF0')
80b27b4e
RD
414 # Numbers
415 self.StyleSetSpec(wx.stc.STC_P_NUMBER, 'fore:#008080')
416 # Strings and characters
417 self.StyleSetSpec(wx.stc.STC_P_STRING, 'fore:#800080')
418 self.StyleSetSpec(wx.stc.STC_P_CHARACTER, 'fore:#800080')
419 # Keywords
420 self.StyleSetSpec(wx.stc.STC_P_WORD, 'fore:#000080,bold')
421 # Triple quotes
422 self.StyleSetSpec(wx.stc.STC_P_TRIPLE, 'fore:#800080,back:#FFFFEA')
423 self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, 'fore:#800080,back:#FFFFEA')
424 # Class names
425 self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, 'fore:#0000FF,bold')
426 # Function names
427 self.StyleSetSpec(wx.stc.STC_P_DEFNAME, 'fore:#008080,bold')
428 # Operators
429 self.StyleSetSpec(wx.stc.STC_P_OPERATOR, 'fore:#800000,bold')
430 # Identifiers. I leave this as not bold because everything seems
431 # to be an identifier if it doesn't match the above criterae
432 self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, 'fore:#000000')
433
434 # Caret color
435 self.SetCaretForeground("BLUE")
436 # Selection background
437 self.SetSelBackground(1, '#66CCFF')
438
439 self.SetSelBackground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT))
440 self.SetSelForeground(True, wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
441
08ecc920
RD
442 def RegisterModifiedEvent(self, eventHandler):
443 self.Bind(wx.stc.EVT_STC_CHANGE, eventHandler)
444
1fded56b
RD
445
446except ImportError:
08ecc920
RD
447 class DemoCodeEditor(wx.TextCtrl):
448 def __init__(self, parent):
2e839e96
RD
449 wx.TextCtrl.__init__(self, parent, -1, style =
450 wx.TE_MULTILINE | wx.HSCROLL | wx.TE_RICH2 | wx.TE_NOHIDESEL)
1fded56b 451
08ecc920
RD
452 def RegisterModifiedEvent(self, eventHandler):
453 self.Bind(wx.EVT_TEXT, eventHandler)
454
455 def SetReadOnly(self, flag):
456 self.SetEditable(not flag)
457 # NOTE: STC already has this method
458
459 def GetText(self):
460 return self.GetValue()
461
462 def GetPositionFromLine(line):
463 return self.XYToPosition(0,line)
464
465 def GotoLine(self, line):
466 pos = self.editor.GetPositionFromLine(line)
467 self.editor.SetInsertionPoint(pos)
468 self.editor.ShowPosition(pos)
469
470 def SelectLine(self, line):
471 start = self.GetPositionFromLine(line)
472 end = start + self.GetLineLength(line)
473 self.SetSelection(start, end)
474
475
476#---------------------------------------------------------------------------
477# Constants for module versions
478
479modOriginal = 0
480modModified = 1
481modDefault = modOriginal
482
483#---------------------------------------------------------------------------
484
485class DemoCodePanel(wx.Panel):
486 """Panel for the 'Demo Code' tab"""
487 def __init__(self, parent, mainFrame):
2e839e96 488 wx.Panel.__init__(self, parent, size=(1,1))
e0465cbd
RD
489 if 'wxMSW' in wx.PlatformInfo:
490 self.Hide()
08ecc920
RD
491 self.mainFrame = mainFrame
492 self.editor = DemoCodeEditor(self)
493 self.editor.RegisterModifiedEvent(self.OnCodeModified)
494
495 self.btnSave = wx.Button(self, -1, "Save Changes")
496 self.btnRestore = wx.Button(self, -1, "Delete Modified")
497 self.btnSave.Enable(False)
498 self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
499 self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
500
501 self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
502 modModified: wx.RadioButton(self, -1, "Modified") }
503
504 self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
505 self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
506 wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
507 for modID, radioButton in self.radioButtons.items():
508 self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
509 radioButton.modID = modID # makes it easier for the event handler
510 radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
511
512 self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
513 self.controlBox.Add(self.btnRestore, 0)
514
515 self.box = wx.BoxSizer(wx.VERTICAL)
516 self.box.Add(self.controlBox, 0, wx.EXPAND)
887a1bd9 517 self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
08ecc920
RD
518 self.box.Add(self.editor, 1, wx.EXPAND)
519
520 self.box.Fit(self)
521 self.SetSizer(self.box)
522
523
524 # Loads a demo from a DemoModules object
525 def LoadDemo(self, demoModules):
526 self.demoModules = demoModules
527 if (modDefault == modModified) and demoModules.Exists(modModified):
528 demoModules.SetActive(modModified)
529 else:
530 demoModules.SetActive(modOriginal)
531 self.radioButtons[demoModules.GetActiveID()].Enable(True)
532 self.ActiveModuleChanged()
533
534
535 def ActiveModuleChanged(self):
536 self.LoadDemoSource(self.demoModules.GetSource())
537 self.UpdateControlState()
a6780fa2 538 self.ReloadDemo()
08ecc920
RD
539
540
541 def LoadDemoSource(self, source):
542 self.editor.Clear()
543 self.editor.SetValue(source)
544 self.JumpToLine(0)
545 self.btnSave.Enable(False)
546
547
548 def JumpToLine(self, line, highlight=False):
549 self.editor.GotoLine(line)
550 self.editor.SetFocus()
551 if highlight:
552 self.editor.SelectLine(line)
553
554
555 def UpdateControlState(self):
556 active = self.demoModules.GetActiveID()
557 # Update the radio/restore buttons
558 for moduleID in self.radioButtons:
559 btn = self.radioButtons[moduleID]
560 if moduleID == active:
561 btn.SetValue(True)
562 else:
563 btn.SetValue(False)
564
565 if self.demoModules.Exists(moduleID):
566 btn.Enable(True)
567 if moduleID == modModified:
568 self.btnRestore.Enable(True)
569 else:
570 btn.Enable(False)
571 if moduleID == modModified:
572 self.btnRestore.Enable(False)
573
574
575 def OnRadioButton(self, event):
576 radioSelected = event.GetEventObject()
577 modSelected = radioSelected.modID
578 if modSelected != self.demoModules.GetActiveID():
579 busy = wx.BusyInfo("Reloading demo module...")
580 self.demoModules.SetActive(modSelected)
581 self.ActiveModuleChanged()
582
583
584 def ReloadDemo(self):
585 if self.demoModules.name != __name__:
a6780fa2 586 self.mainFrame.RunModule()
08ecc920
RD
587
588
589 def OnCodeModified(self, event):
590 self.btnSave.Enable(self.editor.IsModified())
591
592
593 def OnSave(self, event):
594 if self.demoModules.Exists(modModified):
595 if self.demoModules.GetActiveID() == modOriginal:
596 overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
597 "Do you want to continue?"
598 dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
599 wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
600 result = dlg.ShowModal()
601 if result == wx.ID_NO:
602 return
603 dlg.Destroy()
604
605 self.demoModules.SetActive(modModified)
606 modifiedFilename = GetModifiedFilename(self.demoModules.name)
607
608 # Create the demo directory if one doesn't already exist
609 if not os.path.exists(GetModifiedDirectory()):
610 try:
611 os.makedirs(GetModifiedDirectory())
612 if not os.path.exists(GetModifiedDirectory()):
613 wx.LogMessage("BUG: Created demo directory but it still doesn't exit")
614 raise AssetionError
615 except:
616 wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
617 return
618 else:
619 wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
620
621 # Save
887a1bd9 622 f = open(modifiedFilename, "wt")
08ecc920
RD
623 source = self.editor.GetText()
624 try:
625 f.write(source)
626 finally:
627 f.close()
628
629 busy = wx.BusyInfo("Reloading demo module...")
630 self.demoModules.LoadFromFile(modModified, modifiedFilename)
631 self.ActiveModuleChanged()
632
633
634 def OnRestore(self, event): # Handles the "Delete Modified" button
635 modifiedFilename = GetModifiedFilename(self.demoModules.name)
636 self.demoModules.Delete(modModified)
637 os.unlink(modifiedFilename) # Delete the modified copy
638 busy = wx.BusyInfo("Reloading demo module...")
639 self.ActiveModuleChanged()
640
1fded56b 641
6c5ae2d2
RD
642#---------------------------------------------------------------------------
643
644def opj(path):
645 """Convert paths to the platform-specific separator"""
08ecc920
RD
646 str = apply(os.path.join, tuple(path.split('/')))
647 # HACK: on Linux, a leading / gets lost...
648 if path.startswith('/'):
649 str = '/' + str
650 return str
651
652
653def GetModifiedDirectory():
654 """
655 Returns the directory where modified versions of the demo files
656 are stored
657 """
658 return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
659
660
661def GetModifiedFilename(name):
662 """
663 Returns the filename of the modified version of the specified demo
664 """
665 if not name.endswith(".py"):
666 name = name + ".py"
667 return GetModifiedDirectory() + name
668
669
670def GetOriginalFilename(name):
671 """
672 Returns the filename of the original version of the specified demo
673 """
674 if not name.endswith(".py"):
675 name = name + ".py"
676 return name
677
678
679def DoesModifiedExist(name):
680 """Returns whether the specified demo has a modified copy"""
681 if os.path.exists(GetModifiedFilename(name)):
682 return True
683 else:
684 return False
685
686
687#---------------------------------------------------------------------------
688
689class ModuleDictWrapper:
690 """Emulates a module with a dynamically compiled __dict__"""
691 def __init__(self, dict):
692 self.dict = dict
693
694 def __getattr__(self, name):
695 if name in self.dict:
696 return self.dict[name]
697 else:
698 raise AttributeError
699
700class DemoModules:
701 """
702 Dynamically manages the original/modified versions of a demo
703 module
704 """
705 def __init__(self, name):
706 self.modActive = -1
707 self.name = name
708
709 # (dict , source , filename , description , error information )
710 # ( 0 , 1 , 2 , 3 , 4 )
711 self.modules = [[None, "" , "" , "<original>" , None],
712 [None, "" , "" , "<modified>" , None]]
713
714 # load original module
715 self.LoadFromFile(modOriginal, GetOriginalFilename(name))
317a64e5 716 self.SetActive(modOriginal)
08ecc920
RD
717
718 # load modified module (if one exists)
719 if DoesModifiedExist(name):
720 self.LoadFromFile(modModified, GetModifiedFilename(name))
721
722
723 def LoadFromFile(self, modID, filename):
724 self.modules[modID][2] = filename
887a1bd9 725 file = open(filename, "rt")
08ecc920
RD
726 self.LoadFromSource(modID, file.read())
727 file.close()
728
729
730 def LoadFromSource(self, modID, source):
731 self.modules[modID][1] = source
732 self.LoadDict(modID)
733
734
735 def LoadDict(self, modID):
736 if self.name != __name__:
737 source = self.modules[modID][1]
738 description = self.modules[modID][3]
739
740 try:
741 self.modules[modID][0] = {}
742 code = compile(source, description, "exec")
743 exec code in self.modules[modID][0]
744 except:
745 self.modules[modID][4] = DemoError(sys.exc_info())
746 self.modules[modID][0] = None
747 else:
748 self.modules[modID][4] = None
749
750
751 def SetActive(self, modID):
752 if modID != modOriginal and modID != modModified:
753 raise LookupError
754 else:
755 self.modActive = modID
756
757
758 def GetActive(self):
759 dict = self.modules[self.modActive][0]
760 if dict is None:
761 return None
762 else:
763 return ModuleDictWrapper(dict)
764
765
766 def GetActiveID(self):
767 return self.modActive
768
769
770 def GetSource(self, modID = None):
771 if modID is None:
772 modID = self.modActive
773 return self.modules[modID][1]
774
775
776 def GetFilename(self, modID = None):
777 if modID is None:
778 modID = self.modActive
779 return self.modules[self.modActive][2]
780
781
782 def GetErrorInfo(self, modID = None):
783 if modID is None:
784 modID = self.modActive
785 return self.modules[self.modActive][4]
786
787
788 def Exists(self, modID):
789 return self.modules[modID][1] != ""
790
791
792 def UpdateFile(self, modID = None):
793 """Updates the file from which a module was loaded
794 with (possibly updated) source"""
795 if modID is None:
796 modID = self.modActive
797
798 source = self.modules[modID][1]
799 filename = self.modules[modID][2]
800
801 try:
887a1bd9 802 file = open(filename, "wt")
08ecc920
RD
803 file.write(source)
804 finally:
805 file.close()
806
807
808 def Delete(self, modID):
809 if self.modActive == modID:
810 self.SetActive(0)
811
812 self.modules[modID][0] = None
813 self.modules[modID][1] = ""
814 self.modules[modID][2] = ""
815
816
08ecc920
RD
817#---------------------------------------------------------------------------
818
819class DemoError:
820 """Wraps and stores information about the current exception"""
821 def __init__(self, exc_info):
822 import copy
823
824 excType, excValue = exc_info[:2]
825 # traceback list entries: (filename, line number, function name, text)
826 self.traceback = traceback.extract_tb(exc_info[2])
827
828 # --Based on traceback.py::format_exception_only()--
829 if type(excType) == types.ClassType:
830 self.exception_type = excType.__name__
831 else:
832 self.exception_type = excType
833
834 # If it's a syntax error, extra information needs
835 # to be added to the traceback
836 if excType is SyntaxError:
837 try:
838 msg, (filename, lineno, self.offset, line) = excValue
839 except:
840 pass
841 else:
842 if not filename:
843 filename = "<string>"
844 line = line.strip()
845 self.traceback.append( (filename, lineno, "", line) )
846 excValue = msg
847 try:
848 self.exception_details = str(excValue)
849 except:
850 self.exception_details = "<unprintable %s object>" & type(excValue).__name__
851
852 del exc_info
853
854 def __str__(self):
855 ret = "Type %s \n \
856 Traceback: %s \n \
857 Details : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
858 return ret
859
860#---------------------------------------------------------------------------
861
862class DemoErrorPanel(wx.Panel):
863 """Panel put into the demo tab when the demo fails to run due to errors"""
864
865 def __init__(self, parent, codePanel, demoError, log):
866 wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
867 self.codePanel = codePanel
868 self.nb = parent
869 self.log = log
870
871 self.box = wx.BoxSizer(wx.VERTICAL)
872
873 # Main Label
874 self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
875 , 0, wx.ALIGN_CENTER | wx.TOP, 10)
876
877 # Exception Information
878 boxInfo = wx.StaticBox(self, -1, "Exception Info" )
879 boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
880 boxInfoGrid = wx.FlexGridSizer(0, 2, 0, 0)
881 textFlags = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
882 boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
883 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
884 boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
885 boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
886 boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
887 self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
888
889 # Set up the traceback list
890 # This one automatically resizes last column to take up remaining space
891 from ListCtrl import TestListCtrl
892 self.list = TestListCtrl(self, -1, style=wx.LC_REPORT | wx.SUNKEN_BORDER)
893 self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
894 self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
895 self.list.InsertColumn(0, "Filename")
896 self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
897 self.list.InsertColumn(2, "Function")
898 self.list.InsertColumn(3, "Code")
899 self.InsertTraceback(self.list, demoError.traceback)
900 self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
901 self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
902 self.box.Add(wx.StaticText(self, -1, "Traceback:")
903 , 0, wx.ALIGN_CENTER | wx.TOP, 5)
904 self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
905 self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
906 + "Double-click on them to go to the offending line")
907 , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
908
909 self.box.Fit(self)
910 self.SetSizer(self.box)
911
912
913 def InsertTraceback(self, list, traceback):
914 #Add the traceback data
915 for x in range(len(traceback)):
916 data = traceback[x]
917 list.InsertStringItem(x, os.path.basename(data[0])) # Filename
918 list.SetStringItem(x, 1, str(data[1])) # Line
919 list.SetStringItem(x, 2, str(data[2])) # Function
920 list.SetStringItem(x, 3, str(data[3])) # Code
921
922 # Check whether this entry is from the demo module
923 if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
924 self.list.SetItemData(x, int(data[1])) # Store line number for easy access
925 # Give it a blue colour
926 item = self.list.GetItem(x)
927 item.SetTextColour(wx.BLUE)
928 self.list.SetItem(item)
929 else:
930 self.list.SetItemData(x, -1) # Editor can't jump into this one's code
931
932
933 def OnItemSelected(self, event):
934 # This occurs before OnDoubleClick and can be used to set the
935 # currentItem. OnDoubleClick doesn't get a wxListEvent....
936 self.currentItem = event.m_itemIndex
937 event.Skip()
938
939
940 def OnDoubleClick(self, event):
941 # If double-clicking on a demo's entry, jump to the line number
942 line = self.list.GetItemData(self.currentItem)
943 if line != -1:
944 self.nb.SetSelection(1) # Switch to the code viewer tab
945 wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
946 event.Skip()
947
6c5ae2d2 948
76bfdc78
RD
949#---------------------------------------------------------------------------
950
1cc18e15
RD
951class DemoTaskBarIcon(wx.TaskBarIcon):
952 TBMENU_RESTORE = wx.NewId()
953 TBMENU_CLOSE = wx.NewId()
954 TBMENU_CHANGE = wx.NewId()
955 TBMENU_REMOVE = wx.NewId()
956
957 def __init__(self, frame):
958 wx.TaskBarIcon.__init__(self)
959 self.frame = frame
960
961 # Set the image
962 icon = self.MakeIcon(images.getMondrianImage())
963 self.SetIcon(icon, "wxPython Demo")
964
965 # bind some events
966 self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
967 self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
968 self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
969 self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
970 self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
971
972
973 def CreatePopupMenu(self):
974 """
975 This method is called by the base class when it needs to popup
976 the menu for the default EVT_RIGHT_DOWN event. Just create
977 the menu how you want it and return it from this function,
978 the base class takes care of the rest.
979 """
980 menu = wx.Menu()
981 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
982 menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
983 menu.AppendSeparator()
984 menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
985 menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
986 return menu
987
988
989 def MakeIcon(self, img):
990 """
991 The various platforms have different requirements for the
992 icon size...
993 """
994 if "wxMSW" in wx.PlatformInfo:
995 img.Scale(16, 16)
996 elif "wxGTK" in wx.PlatformInfo:
49090f6f 997 img.Scale(20, 20)
1cc18e15
RD
998 # wxMac can be any size upto 128.128....
999 icon = wx.IconFromBitmap(img.ConvertToBitmap() )
1000 return icon
1001
1002
1003 def OnTaskBarActivate(self, evt):
1004 if self.frame.IsIconized():
1005 self.frame.Iconize(False)
1006 if not self.frame.IsShown():
1007 self.frame.Show(True)
1008 self.frame.Raise()
1009
1010
1011 def OnTaskBarClose(self, evt):
1012 self.frame.Close()
1013
1cc18e15
RD
1014
1015 def OnTaskBarChange(self, evt):
49090f6f 1016 icon = self.MakeIcon(images.getBlom10MaskedImage())
1cc18e15
RD
1017 self.SetIcon(icon, "This is a new icon")
1018
1019
1020 def OnTaskBarRemove(self, evt):
1021 self.RemoveIcon()
1022
1023
1024#---------------------------------------------------------------------------
1fded56b 1025class wxPythonDemo(wx.Frame):
e9159fe8 1026 overviewText = "wxPython Overview"
c368d904 1027
08ecc920 1028 def __init__(self, parent, title):
8ee202f3 1029 wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
08ecc920 1030 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
2f90df85 1031
08ecc920 1032 self.loaded = False
694759cf 1033 self.cwd = os.getcwd()
3ca6a5f0 1034 self.curOverview = ""
08ecc920
RD
1035 self.demoPage = None
1036 self.codePage = None
887a1bd9 1037 self.shell = None
a6780fa2 1038 self.firstTime = True
694759cf 1039
afb810d9 1040 icon = images.getMondrianIcon()
96bfd053 1041 self.SetIcon(icon)
c368d904 1042
1cc18e15 1043 self.tbicon = DemoTaskBarIcon(self)
c368d904 1044
1fded56b 1045 wx.CallAfter(self.ShowTip)
cf694132
RD
1046
1047 self.otherWin = None
80b27b4e
RD
1048 self.Bind(wx.EVT_IDLE, self.OnIdle)
1049 self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
1050 self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
1051 self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
cf694132 1052
1fded56b
RD
1053 self.Centre(wx.BOTH)
1054 self.CreateStatusBar(1, wx.ST_SIZEGRIP)
5a7823f5 1055
0c8f2860
RD
1056 splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
1057 splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
5a7823f5 1058
d56cebe7 1059 def EmptyHandler(evt): pass
80b27b4e
RD
1060 #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
1061 #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
cf694132 1062
1e4a197e
RD
1063 # Prevent TreeCtrl from displaying all items after destruction when True
1064 self.dying = False
cf694132 1065
08ecc920
RD
1066 # Create a Notebook
1067 self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
1068
cf694132 1069 # Make a File menu
1fded56b
RD
1070 self.mainmenu = wx.MenuBar()
1071 menu = wx.Menu()
330af869
RD
1072 item = menu.Append(-1, '&Redirect Output',
1073 'Redirect print statements to a window',
1074 wx.ITEM_CHECK)
1075 self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
08ecc920 1076
c8000995
RD
1077 item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
1078 self.Bind(wx.EVT_MENU, self.OnFileExit, item)
1079 wx.App_SetMacExitMenuItemId(item.GetId())
cf694132
RD
1080 self.mainmenu.Append(menu, '&File')
1081
ec3e670f 1082 # Make a Demo menu
1fded56b 1083 menu = wx.Menu()
ec3e670f 1084 for item in _treeList:
1fded56b 1085 submenu = wx.Menu()
ec3e670f 1086 for childItem in item[1]:
c8000995
RD
1087 mi = submenu.Append(-1, childItem)
1088 self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
1fded56b 1089 menu.AppendMenu(wx.NewId(), item[0], submenu)
ec3e670f
RD
1090 self.mainmenu.Append(menu, '&Demo')
1091
08ecc920
RD
1092 # Make a Demo Code menu
1093 #TODO: Add new menu items
1094 # Like the option-enabled entries to select the
1095 # active module
1096 #TODO: should we bother?
1097
1098 #menu = wx.Menu()
1099 #saveID = wx.NewId()
1100 #restoreID = wx.NewId()
1101 #
1102 #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
1103 #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
1104 #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
1105 #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
1106 #self.mainmenu.Append(menu, 'Demo &Code')
1107 #
ec3e670f 1108
cf694132 1109 # Make a Help menu
1fded56b 1110 menu = wx.Menu()
c8000995
RD
1111 findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
1112 findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
1e4a197e 1113 menu.AppendSeparator()
887a1bd9
RD
1114
1115 shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
1116 'An interactive interpreter window with the demo app and frame objects in the namesapce')
1117 menu.AppendSeparator()
c8000995
RD
1118 helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
1119 wx.App_SetMacAboutMenuItemId(helpItem.GetId())
887a1bd9
RD
1120
1121 self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
c8000995
RD
1122 self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
1123 self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
1124 self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
80b27b4e
RD
1125 self.Bind(wx.EVT_COMMAND_FIND, self.OnFind)
1126 self.Bind(wx.EVT_COMMAND_FIND_NEXT, self.OnFind)
1127 self.Bind(wx.EVT_COMMAND_FIND_CLOSE, self.OnFindClose)
cf694132
RD
1128 self.mainmenu.Append(menu, '&Help')
1129 self.SetMenuBar(self.mainmenu)
1130
1fded56b 1131 self.finddata = wx.FindReplaceData()
1e4a197e
RD
1132
1133 if 0:
1134 # This is another way to set Accelerators, in addition to
1135 # using the '\t<key>' syntax in the menu items.
1fded56b
RD
1136 aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitID),
1137 (wx.ACCEL_CTRL, ord('H'), helpID),
1138 (wx.ACCEL_CTRL, ord('F'), findID),
1139 (wx.ACCEL_NORMAL, WXK_F3, findnextID)
1140 ])
1e4a197e 1141 self.SetAcceleratorTable(aTable)
2f90df85 1142
bb0054cd 1143
cf694132 1144 # Create a TreeCtrl
1fded56b 1145 tID = wx.NewId()
f6bcfd97 1146 self.treeMap = {}
3628e088
RD
1147 self.tree = wx.TreeCtrl(splitter, tID, style =
1148 wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
eb0f373c 1149 )
afb810d9 1150
e9159fe8 1151 root = self.tree.AddRoot("wxPython Overview")
f6bcfd97
BP
1152 firstChild = None
1153 for item in _treeList:
1154 child = self.tree.AppendItem(root, item[0])
1155 if not firstChild: firstChild = child
1156 for childItem in item[1]:
1157 theDemo = self.tree.AppendItem(child, childItem)
1158 self.treeMap[childItem] = theDemo
1159
1160 self.tree.Expand(root)
1161 self.tree.Expand(firstChild)
80b27b4e
RD
1162 self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
1163 self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
1164 self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
1165 self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
cf694132 1166
1fded56b 1167 # Set up a wx.html.HtmlWindow on the Overview Notebook page
0bdca46d
RD
1168 # we put it in a panel first because there seems to be a
1169 # refresh bug of some sort (wxGTK) when it is directly in
1170 # the notebook...
1171 if 0: # the old way
1fded56b 1172 self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
0bdca46d
RD
1173 self.nb.AddPage(self.ovr, self.overviewText)
1174
1fded56b
RD
1175 else: # hopefully I can remove this hacky code soon, see SF bug #216861
1176 panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
1177 self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
0bdca46d
RD
1178 self.nb.AddPage(panel, self.overviewText)
1179
1180 def OnOvrSize(evt, ovr=self.ovr):
1181 ovr.SetSize(evt.GetSize())
80b27b4e
RD
1182 panel.Bind(wx.EVT_SIZE, OnOvrSize)
1183 panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
0bdca46d 1184
08ecc920 1185 if "gtk2" in wx.PlatformInfo:
385721a8 1186 self.ovr.SetStandardFonts()
08ecc920 1187 self.SetOverview(self.overviewText, mainOverview)
c368d904 1188
cf694132 1189
08ecc920
RD
1190 # Set up a log window
1191 self.log = wx.TextCtrl(splitter2, -1,
1192 style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
1193
1194 # Set the wxWindows log target to be this textctrl
1195 #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
cf694132 1196
08ecc920
RD
1197 # But instead of the above we want to show how to use our own wx.Log class
1198 wx.Log_SetActiveTarget(MyLog(self.log))
1199
1200 # for serious debugging
1201 #wx.Log_SetActiveTarget(wx.LogStderr())
1202 #wx.Log_SetTraceMask(wx.TraceMessages)
cf694132
RD
1203
1204
e44b5196
RD
1205 self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
1206 wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
1207
f6bcfd97 1208 # add the windows to the splitter and split it.
08ecc920
RD
1209 splitter2.SplitHorizontally(self.nb, self.log, -160)
1210 splitter.SplitVertically(self.tree, splitter2, 200)
afb810d9 1211
f6bcfd97 1212 splitter.SetMinimumPaneSize(20)
afb810d9
RD
1213 splitter2.SetMinimumPaneSize(20)
1214
11926a78 1215 # Make the splitter on the right expand the top window when resized
2f0f3b0f
RD
1216 def SplitterOnSize(evt):
1217 splitter = evt.GetEventObject()
1218 sz = splitter.GetSize()
08ecc920 1219 splitter.SetSashPosition(sz.height - 160, False)
2f0f3b0f 1220 evt.Skip()
80b27b4e
RD
1221
1222 splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
2f0f3b0f 1223
bb0054cd
RD
1224 # select initial items
1225 self.nb.SetSelection(0)
f6bcfd97 1226 self.tree.SelectItem(root)
ec3e670f 1227
08ecc920
RD
1228 # Load 'Main' module
1229 self.LoadDemo(self.overviewText)
1230 self.loaded = True
1231
1232 # select some other initial module?
1233 if len(sys.argv) > 1:
1234 arg = sys.argv[1]
1235 if arg.endswith('.py'):
1236 arg = arg[:-3]
1237 selectedDemo = self.treeMap.get(arg, None)
f6bcfd97 1238 if selectedDemo:
ec3e670f
RD
1239 self.tree.SelectItem(selectedDemo)
1240 self.tree.EnsureVisible(selectedDemo)
1241
bb0054cd 1242
cf694132
RD
1243 #---------------------------------------------
1244 def WriteText(self, text):
f6bcfd97
BP
1245 if text[-1:] == '\n':
1246 text = text[:-1]
1fded56b 1247 wx.LogMessage(text)
f6bcfd97 1248
cf694132
RD
1249 def write(self, txt):
1250 self.WriteText(txt)
1251
1252 #---------------------------------------------
1253 def OnItemExpanded(self, event):
1254 item = event.GetItem()
1fded56b 1255 wx.LogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
c368d904 1256 event.Skip()
cf694132
RD
1257
1258 #---------------------------------------------
1259 def OnItemCollapsed(self, event):
1260 item = event.GetItem()
1fded56b 1261 wx.LogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
c368d904 1262 event.Skip()
f6bcfd97
BP
1263
1264 #---------------------------------------------
1265 def OnTreeLeftDown(self, event):
08ecc920 1266 # reset the overview text if the tree item is clicked on again
f6bcfd97
BP
1267 pt = event.GetPosition();
1268 item, flags = self.tree.HitTest(pt)
1269 if item == self.tree.GetSelection():
d975da9b 1270 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
185d7c3e 1271 event.Skip()
cf694132
RD
1272
1273 #---------------------------------------------
1274 def OnSelChanged(self, event):
08ecc920 1275 if self.dying or not self.loaded:
cf694132
RD
1276 return
1277
5a7823f5
RD
1278 item = event.GetItem()
1279 itemText = self.tree.GetItemText(item)
08ecc920 1280 self.LoadDemo(itemText)
5a7823f5
RD
1281
1282 #---------------------------------------------
08ecc920
RD
1283 def LoadDemo(self, demoName):
1284 try:
1285 wx.BeginBusyCursor()
1286
1287 os.chdir(self.cwd)
1288 self.ShutdownDemoModule()
1289
1290 if demoName == self.overviewText:
1291 # User selected the "wxPython Overview" node
1292 # ie: _this_ module
1293 # Changing the main window at runtime not yet supported...
1294 self.demoModules = DemoModules(__name__)
1295 self.SetOverview(self.overviewText, mainOverview)
1296 self.LoadDemoSource()
1297 self.UpdateNotebook(0)
1298 else:
1299 if os.path.exists(GetOriginalFilename(demoName)):
1300 wx.LogMessage("Loading demo %s.py..." % demoName)
1301 self.demoModules = DemoModules(demoName)
a6780fa2 1302 self.LoadDemoSource()
08ecc920
RD
1303 self.tree.Refresh()
1304 else:
1305 self.SetOverview("wxPython", mainOverview)
1306 self.codePage = None
1307 self.UpdateNotebook(0)
1308 finally:
1309 wx.EndBusyCursor()
cf694132 1310
08ecc920
RD
1311 #---------------------------------------------
1312 def LoadDemoSource(self):
1313 self.codePage = None
1314 self.codePage = DemoCodePanel(self.nb, self)
1315 self.codePage.LoadDemo(self.demoModules)
1316
1317 #---------------------------------------------
a6780fa2 1318 def RunModule(self):
08ecc920 1319 """Runs the active module"""
cf694132 1320
08ecc920
RD
1321 module = self.demoModules.GetActive()
1322 self.ShutdownDemoModule()
1323 overviewText = ""
1324
8412feb0
RD
1325 # o The RunTest() for all samples must now return a window that can
1326 # be palced in a tab in the main notebook.
08ecc920
RD
1327 # o If an error occurs (or has occured before) an error tab is created.
1328
1329 if module is not None:
1330 wx.LogMessage("Running demo module...")
1331 if hasattr(module, "overview"):
1332 overviewText = module.overview
cf694132 1333
08ecc920
RD
1334 try:
1335 self.demoPage = module.runTest(self, self.nb, self)
08ecc920 1336 except:
317a64e5 1337 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
7d0f8766
RD
1338 DemoError(sys.exc_info()), self)
1339
1340 assert self.demoPage is not None, "runTest must return a window!"
1341
08ecc920
RD
1342 else:
1343 # There was a previous error in compiling or exec-ing
317a64e5
RD
1344 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
1345 self.demoModules.GetErrorInfo(), self)
a6780fa2 1346
08ecc920 1347 self.SetOverview(self.demoModules.name + " Overview", overviewText)
a6780fa2
RD
1348
1349 if self.firstTime:
1350 # cahnge to the demo page the first time a module is run
1351 self.UpdateNotebook(2)
1352 self.firstTime = False
1353 else:
1354 # otherwise just stay on the same tab in case the user has changed to another one
1355 self.UpdateNotebook()
cf694132
RD
1356
1357 #---------------------------------------------
08ecc920
RD
1358 def ShutdownDemoModule(self):
1359 if self.demoPage:
1360 # inform the window that it's time to quit if it cares
1361 if hasattr(self.demoPage, "ShutdownDemo"):
1362 self.demoPage.ShutdownDemo()
1363 wx.YieldIfNeeded() # in case the page has pending events
1364 self.demoPage = None
1365
1366 #---------------------------------------------
1367 def UpdateNotebook(self, select = -1):
1368 nb = self.nb
1369 debug = False
1370
1371 def UpdatePage(page, pageText):
1372 pageExists = False
1373 pagePos = -1
1374 for i in range(nb.GetPageCount()):
1375 if nb.GetPageText(i) == pageText:
1376 pageExists = True
1377 pagePos = i
1378 break
1379
1380 if page:
1381 if not pageExists:
1382 # Add a new page
08ecc920
RD
1383 nb.AddPage(page, pageText)
1384 if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
1385 else:
08ecc920
RD
1386 if nb.GetPage(pagePos) != page:
1387 # Reload an existing page
1388 nb.Freeze()
08ecc920
RD
1389 nb.DeletePage(pagePos)
1390 nb.InsertPage(pagePos, page, pageText)
1391 nb.Thaw()
1392 if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
1393 else:
1394 # Excellent! No redraw/flicker
1395 if debug: wx.LogMessage("DBG: SAVED from reloading %s" % pageText)
1396 elif pageExists:
1397 # Delete a page
1398 nb.DeletePage(pagePos)
1399 if debug: wx.LogMessage("DBG: DELETED %s" % pageText)
1400 else:
1401 if debug: wx.LogMessage("DBG: STILL GONE - %s" % pageText)
1402
1403 if select == -1:
1404 select = nb.GetSelection()
cf694132 1405
08ecc920
RD
1406 UpdatePage(self.codePage, "Demo Code")
1407 UpdatePage(self.demoPage, "Demo")
cf694132 1408
887a1bd9 1409 if select >= 0 and select < nb.GetPageCount():
08ecc920
RD
1410 nb.SetSelection(select)
1411
cf694132
RD
1412 #---------------------------------------------
1413 def SetOverview(self, name, text):
f6bcfd97
BP
1414 self.curOverview = text
1415 lead = text[:6]
1416 if lead != '<html>' and lead != '<HTML>':
1e4a197e 1417 text = '<br>'.join(text.split('\n'))
6c7d1792
RD
1418 if wx.USE_UNICODE:
1419 text = text.decode('iso8859_1')
f6bcfd97 1420 self.ovr.SetPage(text)
cf694132 1421 self.nb.SetPageText(0, name)
cf694132
RD
1422
1423 #---------------------------------------------
1424 # Menu methods
c368d904 1425 def OnFileExit(self, *event):
cf694132
RD
1426 self.Close()
1427
330af869
RD
1428 def OnToggleRedirect(self, event):
1429 app = wx.GetApp()
1430 if event.Checked():
1431 app.RedirectStdio()
1432 print "Print statements and other standard output will now be directed to this window."
1433 else:
1434 app.RestoreStdio()
1435 print "Print statements and other standard output will now be sent to the usual location."
08ecc920 1436
cf694132 1437 def OnHelpAbout(self, event):
e166644c 1438 from About import MyAboutBox
ec3e670f 1439 about = MyAboutBox(self)
cf694132
RD
1440 about.ShowModal()
1441 about.Destroy()
1442
1e4a197e
RD
1443 def OnHelpFind(self, event):
1444 self.nb.SetSelection(1)
1fded56b
RD
1445 self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
1446 wx.FR_NOUPDOWN |
1447 wx.FR_NOMATCHCASE |
1448 wx.FR_NOWHOLEWORD)
1e4a197e
RD
1449 self.finddlg.Show(True)
1450
1451 def OnFind(self, event):
08ecc920 1452 editor = self.codePage.editor
1e4a197e 1453 self.nb.SetSelection(1)
08ecc920
RD
1454 end = editor.GetLastPosition()
1455 textstring = editor.GetRange(0, end).lower()
1456 start = editor.GetSelection()[1]
1e4a197e
RD
1457 findstring = self.finddata.GetFindString().lower()
1458 loc = textstring.find(findstring, start)
1459 if loc == -1 and start != 0:
1460 # string not found, start at beginning
1461 start = 0
1462 loc = textstring.find(findstring, start)
1463 if loc == -1:
1fded56b 1464 dlg = wx.MessageDialog(self, 'Find String Not Found',
1e4a197e 1465 'Find String Not Found in Demo File',
1fded56b 1466 wx.OK | wx.ICON_INFORMATION)
1e4a197e
RD
1467 dlg.ShowModal()
1468 dlg.Destroy()
1469 if self.finddlg:
1470 if loc == -1:
1471 self.finddlg.SetFocus()
1472 return
1473 else:
1474 self.finddlg.Destroy()
08ecc920
RD
1475 editor.ShowPosition(loc)
1476 editor.SetSelection(loc, loc + len(findstring))
1e4a197e
RD
1477
1478
1479
1480 def OnFindNext(self, event):
1481 if self.finddata.GetFindString():
1482 self.OnFind(event)
1483 else:
1484 self.OnHelpFind(event)
1485
1486 def OnFindClose(self, event):
1487 event.GetDialog().Destroy()
1488
cf694132 1489
887a1bd9
RD
1490 def OnOpenShellWindow(self, evt):
1491 if self.shell:
1492 # if it already exists then just make sure it's visible
1493 s = self.shell
1494 if s.IsIconized():
1495 s.Iconize(False)
1496 s.Raise()
1497 else:
1498 # Make a PyShell window
1499 from wx import py
1500 namespace = { 'wx' : wx,
1501 'app' : wx.GetApp(),
1502 'frame' : self,
1503 }
1504 self.shell = py.shell.ShellFrame(None, locals=namespace)
1505 self.shell.SetSize((640,480))
1506 self.shell.Show()
1507
1508 # Hook the close event of the main frame window so that we
1509 # close the shell at the same time if it still exists
1510 def CloseShell(evt):
1511 if self.shell:
1512 self.shell.Close()
1513 evt.Skip()
1514 self.Bind(wx.EVT_CLOSE, CloseShell)
1515
1516
cf694132
RD
1517 #---------------------------------------------
1518 def OnCloseWindow(self, event):
1e4a197e 1519 self.dying = True
08ecc920
RD
1520 self.demoPage = None
1521 self.codePage = None
26197023 1522 self.mainmenu = None
1cc18e15 1523 self.tbicon.Destroy()
cf694132
RD
1524 self.Destroy()
1525
c368d904 1526
cf694132
RD
1527 #---------------------------------------------
1528 def OnIdle(self, event):
1529 if self.otherWin:
1530 self.otherWin.Raise()
08ecc920 1531 self.demoPage = self.otherWin
cf694132
RD
1532 self.otherWin = None
1533
ccf691a4
RD
1534
1535 #---------------------------------------------
1536 def ShowTip(self):
1537 try:
1538 showTipText = open(opj("data/showTips")).read()
1539 showTip, index = eval(showTipText)
1540 except IOError:
1541 showTip, index = (1, 0)
1542 if showTip:
1fded56b 1543 tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
861a0196 1544 ##tp = MyTP(0)
1fded56b 1545 showTip = wx.ShowTip(self, tp)
ccf691a4
RD
1546 index = tp.GetCurrentTip()
1547 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
1548
1549
ec3e670f
RD
1550 #---------------------------------------------
1551 def OnDemoMenu(self, event):
f6bcfd97
BP
1552 try:
1553 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
1554 except:
1555 selectedDemo = None
1556 if selectedDemo:
1557 self.tree.SelectItem(selectedDemo)
1558 self.tree.EnsureVisible(selectedDemo)
ec3e670f 1559
c368d904 1560
c368d904 1561
f3d9dc1d
RD
1562 #---------------------------------------------
1563 def OnIconfiy(self, evt):
8412feb0 1564 wx.LogMessage("OnIconfiy: %s" % evt.Iconized())
f3d9dc1d
RD
1565 evt.Skip()
1566
1567 #---------------------------------------------
1568 def OnMaximize(self, evt):
1fded56b 1569 wx.LogMessage("OnMaximize")
f3d9dc1d
RD
1570 evt.Skip()
1571
8412feb0
RD
1572 #---------------------------------------------
1573 def OnActivate(self, evt):
1574 wx.LogMessage("OnActivate: %s" % evt.GetActive())
1575 evt.Skip()
f3d9dc1d 1576
8412feb0
RD
1577 #---------------------------------------------
1578 def OnAppActivate(self, evt):
1579 wx.LogMessage("OnAppActivate: %s" % evt.GetActive())
1580 evt.Skip()
f3d9dc1d 1581
cf694132
RD
1582#---------------------------------------------------------------------------
1583#---------------------------------------------------------------------------
1584
1fded56b 1585class MySplashScreen(wx.SplashScreen):
b5a5d647 1586 def __init__(self):
1fded56b
RD
1587 bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
1588 wx.SplashScreen.__init__(self, bmp,
a253aa20
RD
1589 wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
1590 3000, None, -1)
80b27b4e 1591 self.Bind(wx.EVT_CLOSE, self.OnClose)
b5a5d647
RD
1592
1593 def OnClose(self, evt):
8eca4fef 1594 self.Hide()
08ecc920 1595 frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
1e4a197e 1596 frame.Show()
ccf691a4 1597 evt.Skip() # Make sure the default handler runs too...
cf694132 1598
b5a5d647 1599
1fded56b 1600class MyApp(wx.App):
b5a5d647
RD
1601 def OnInit(self):
1602 """
68320e40 1603 Create and show the splash screen. It will then create and show
b5a5d647
RD
1604 the main frame when it is time to do so.
1605 """
1e4a197e 1606
35ee3288
RD
1607 # For debugging
1608 #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
1609
a253aa20
RD
1610 # Normally when using a SplashScreen you would create it, show
1611 # it and then continue on with the applicaiton's
1612 # initialization, finally creating and showing the main
1613 # application window(s). In this case we have nothing else to
1614 # do so we'll delay showing the main frame until later (see
b7aef858 1615 # OnClose above) so the users can see the SplashScreen effect.
b5a5d647
RD
1616 splash = MySplashScreen()
1617 splash.Show()
a253aa20 1618
1e4a197e 1619 return True
b5a5d647
RD
1620
1621
1622
cf694132
RD
1623#---------------------------------------------------------------------------
1624
1625def main():
e02c03a4 1626 try:
d56cebe7 1627 demoPath = os.path.dirname(__file__)
e02c03a4
RD
1628 os.chdir(demoPath)
1629 except:
1630 pass
03a604a6 1631 app = MyApp(False)
cf694132
RD
1632 app.MainLoop()
1633
cf694132
RD
1634#---------------------------------------------------------------------------
1635
1636
08ecc920 1637mainOverview = """<html><body>
1fded56b
RD
1638<h2>wxPython</h2>
1639
f6063c19
RD
1640<p> wxPython is a <b>GUI toolkit</b> for the Python programming
1641language. It allows Python programmers to create programs with a
1642robust, highly functional graphical user interface, simply and easily.
1643It is implemented as a Python extension module (native code) that
1644wraps the popular wxWindows cross platform GUI library, which is
1645written in C++.
1fded56b
RD
1646
1647<p> Like Python and wxWindows, wxPython is <b>Open Source</b> which
1648means that it is free for anyone to use and the source code is
1649available for anyone to look at and modify. Or anyone can contribute
8b9a4190 1650fixes or enhancements to the project.
1fded56b
RD
1651
1652<p> wxPython is a <b>cross-platform</b> toolkit. This means that the
1653same program will run on multiple platforms without modification.
1654Currently supported platforms are 32-bit Microsoft Windows, most Unix
1655or unix-like systems, and Macintosh OS X. Since the language is
1656Python, wxPython programs are <b>simple, easy</b> to write and easy to
1657understand.
1658
1659<p> <b>This demo</b> is not only a collection of test cases for
1660wxPython, but is also designed to help you learn about and how to use
1661wxPython. Each sample is listed in the tree control on the left.
1662When a sample is selected in the tree then a module is loaded and run
1663(usually in a tab of this notebook,) and the source code of the module
1664is loaded in another tab for you to browse and learn from.
1665
1666"""
cf694132
RD
1667
1668
1669#----------------------------------------------------------------------------
1670#----------------------------------------------------------------------------
1671
1672if __name__ == '__main__':
08ecc920 1673 __name__ = 'Main'
cf694132
RD
1674 main()
1675
1676#----------------------------------------------------------------------------
1677
1678
1679
1680
1681
1682
1683