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