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