]> git.saurik.com Git - wxWidgets.git/blob - wxPython/demo/Main.py
suppress an assert when a combobox receives WM_KILLFOCUS while it is being destroyed
[wxWidgets.git] / wxPython / demo / Main.py
1 #!/bin/env python
2 #----------------------------------------------------------------------------
3 # Name: Main.py
4 # Purpose: Testing lots of stuff, controls, window types, etc.
5 #
6 # Author: Robin Dunn
7 #
8 # Created: A long time ago, in a galaxy far, far away...
9 # RCS-ID: $Id$
10 # Copyright: (c) 1999 by Total Control Software
11 # Licence: wxWindows license
12 #----------------------------------------------------------------------------
13
14 import sys, os, time, string
15 from wxPython.wx import *
16 from wxPython.html import wxHtmlWindow
17
18 ##from wxPython.stc import *
19
20 import images
21
22 #---------------------------------------------------------------------------
23
24
25 _treeList = [
26 # new stuff
27 ('New since last release', [
28 'RowColSizer',
29 'Unicode',
30 'wxFileHistory',
31 'wxGenericDirCtrl',
32 'wxImageFromStream',
33 ]),
34
35 # managed windows == things with a caption you can close
36 ('Base Frames and Dialogs', [
37 'wxDialog',
38 'wxFrame',
39 'wxMDIWindows',
40 'wxMiniFrame',
41 ]),
42
43 # the common dialogs
44 ('Common Dialogs', [
45 'wxColourDialog',
46 'wxDirDialog',
47 'wxFileDialog',
48 'wxFindReplaceDialog',
49 'wxFontDialog',
50 'wxMessageDialog',
51 'wxPageSetupDialog',
52 'wxPrintDialog',
53 'wxProgressDialog',
54 'wxSingleChoiceDialog',
55 'wxTextEntryDialog',
56 ]),
57
58 # dialogs form libraries
59 ('More Dialogs', [
60 'ErrorDialogs',
61 'ImageBrowser',
62 'wxMultipleChoiceDialog',
63 'wxScrolledMessageDialog',
64 ]),
65
66 # core controls
67 ('Core Windows/Controls', [
68 'VirtualListCtrl',
69 'wxButton',
70 'wxCheckBox',
71 'wxCheckListBox',
72 'wxChoice',
73 'wxComboBox',
74 'wxGauge',
75 'wxGenericDirCtrl',
76 'wxGrid',
77 'wxListBox',
78 'wxListCtrl',
79 'wxNotebook',
80 'wxPopupWindow',
81 'wxRadioBox',
82 'wxSashWindow',
83 'wxSlider',
84 'wxScrolledWindow',
85 'wxSplitterWindow',
86 'wxSpinButton',
87 'wxSpinCtrl',
88 'wxStaticText',
89 'wxStaticBitmap',
90 'wxStatusBar',
91 'wxTextCtrl',
92 'wxToggleButton',
93 'wxToolBar',
94 'wxTreeCtrl',
95 'wxValidator',
96 ]),
97
98 # controls coming from other librairies
99 ('More Windows/Controls', [
100 'ColourSelect',
101 'ContextHelp',
102 'FancyText',
103 'FileBrowseButton',
104 'GenericButtons',
105 'PyCrust',
106 'PyCrustWithFilling',
107 'SplitTree',
108 'TablePrint',
109 'wxCalendar',
110 'wxCalendarCtrl',
111 'wxDynamicSashWindow',
112 'wxEditableListBox',
113 'wxEditor',
114 'wxFloatBar',
115 'wxHtmlWindow',
116 'wxLEDNumberCtrl',
117 'wxMimeTypesManager',
118 'wxMVCTree',
119 'wxStyledTextCtrl_1',
120 'wxStyledTextCtrl_2',
121 'wxRightTextCtrl',
122 ]),
123
124 # How to lay out the controls in a frame/dialog
125 ('Window Layout', [
126 'LayoutAnchors',
127 'Layoutf',
128 'RowColSizer',
129 'Sizers',
130 'wxLayoutConstraints',
131 'XML_Resource',
132 ]),
133
134 # ditto
135 ('Process and Events', [
136 'infoframe',
137 'OOR',
138 'PythonEvents',
139 'Threads',
140 'wxProcess',
141 'wxTimer',
142 ]),
143
144 # Clipboard and DnD
145 ('Clipboard and DnD', [
146 'CustomDragAndDrop',
147 'DragAndDrop',
148 'URLDragAndDrop',
149 ]),
150
151 # Images
152 ('Images', [
153 'wxDragImage',
154 'wxImage',
155 'wxImageFromStream',
156 'wxMask',
157 ]),
158
159 # Other stuff
160 ('Miscellaneous', [
161 'ColourDB',
162 'DialogUnits',
163 'DrawXXXList',
164 'FontEnumerator',
165 'PrintFramework',
166 'Unicode',
167 'wxFileHistory',
168 'wxJoystick',
169 'wxOGL',
170 'wxWave',
171 ]),
172
173 # need libs not coming with the demo
174 ('Objects using an external library', [
175 'ActiveXWrapper_Acrobat',
176 'ActiveXWrapper_IE',
177 'wxGLCanvas',
178 'wxPlotCanvas',
179 ]),
180
181 # pyTree, hangman, ... in the samples dir
182 ('Check out the samples dir too', [
183 ]),
184
185 ]
186
187
188
189 #---------------------------------------------------------------------------
190
191 class MyLog(wxPyLog):
192 def __init__(self, textCtrl, logTime=0):
193 wxPyLog.__init__(self)
194 self.tc = textCtrl
195 self.logTime = logTime
196
197 def DoLogString(self, message, timeStamp):
198 if self.logTime:
199 message = time.strftime("%X", time.localtime(timeStamp)) + \
200 ": " + message
201 self.tc.AppendText(message + '\n')
202
203
204 #---------------------------------------------------------------------------
205
206 def opj(path):
207 """Convert paths to the platform-specific separator"""
208 return apply(os.path.join, tuple(string.split(path, '/')))
209
210
211 #---------------------------------------------------------------------------
212
213 class wxPythonDemo(wxFrame):
214 overviewText = "wxPython Overview"
215
216 def __init__(self, parent, id, title):
217 wxFrame.__init__(self, parent, -1, title, size = (800, 600),
218 style=wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE)
219
220 self.cwd = os.getcwd()
221 self.curOverview = ""
222
223 icon = images.getMondrianIcon()
224 self.SetIcon(icon)
225
226 if wxPlatform == '__WXMSW__':
227 # setup a taskbar icon, and catch some events from it
228 self.tbicon = wxTaskBarIcon()
229 self.tbicon.SetIcon(icon, "wxPython Demo")
230 EVT_TASKBAR_LEFT_DCLICK(self.tbicon, self.OnTaskBarActivate)
231 EVT_TASKBAR_RIGHT_UP(self.tbicon, self.OnTaskBarMenu)
232 EVT_MENU(self.tbicon, self.TBMENU_RESTORE, self.OnTaskBarActivate)
233 EVT_MENU(self.tbicon, self.TBMENU_CLOSE, self.OnTaskBarClose)
234
235
236 self.otherWin = None
237 self.showTip = true
238 EVT_IDLE(self, self.OnIdle)
239 EVT_CLOSE(self, self.OnCloseWindow)
240 EVT_ICONIZE(self, self.OnIconfiy)
241 EVT_MAXIMIZE(self, self.OnMaximize)
242
243 self.Centre(wxBOTH)
244 self.CreateStatusBar(1, wxST_SIZEGRIP)
245
246 splitter = wxSplitterWindow(self, -1, style=wxNO_3D|wxSP_3D)
247 splitter2 = wxSplitterWindow(splitter, -1, style=wxNO_3D|wxSP_3D)
248
249 def EmptyHandler(evt): pass
250 EVT_ERASE_BACKGROUND(splitter, EmptyHandler)
251 EVT_ERASE_BACKGROUND(splitter2, EmptyHandler)
252
253 # Prevent TreeCtrl from displaying all items after destruction when true
254 self.dying = false
255
256 # Make a File menu
257 self.mainmenu = wxMenuBar()
258 menu = wxMenu()
259 exitID = wxNewId()
260 menu.Append(exitID, 'E&xit\tAlt-X', 'Get the heck outta here!')
261 EVT_MENU(self, exitID, self.OnFileExit)
262 self.mainmenu.Append(menu, '&File')
263
264 # Make a Demo menu
265 menu = wxMenu()
266 for item in _treeList:
267 submenu = wxMenu()
268 for childItem in item[1]:
269 mID = wxNewId()
270 submenu.Append(mID, childItem)
271 EVT_MENU(self, mID, self.OnDemoMenu)
272 menu.AppendMenu(wxNewId(), item[0], submenu)
273 self.mainmenu.Append(menu, '&Demo')
274
275
276 # Make a Help menu
277 helpID = wxNewId()
278 menu = wxMenu()
279 menu.Append(helpID, '&About\tCtrl-H', 'wxPython RULES!!!')
280 EVT_MENU(self, helpID, self.OnHelpAbout)
281 self.mainmenu.Append(menu, '&Help')
282 self.SetMenuBar(self.mainmenu)
283
284 # set the menu accellerator table...
285 aTable = wxAcceleratorTable([(wxACCEL_ALT, ord('X'), exitID),
286 (wxACCEL_CTRL, ord('H'), helpID)])
287 self.SetAcceleratorTable(aTable)
288
289
290 # Create a TreeCtrl
291 tID = wxNewId()
292 self.treeMap = {}
293 self.tree = wxTreeCtrl(splitter, tID,
294 style=wxTR_HAS_BUTTONS |
295 wxTR_EDIT_LABELS |
296 wxTR_HAS_VARIABLE_ROW_HEIGHT)
297
298 #self.tree.SetBackgroundColour(wxNamedColour("Pink"))
299 root = self.tree.AddRoot("wxPython Overview")
300 firstChild = None
301 for item in _treeList:
302 child = self.tree.AppendItem(root, item[0])
303 if not firstChild: firstChild = child
304 for childItem in item[1]:
305 theDemo = self.tree.AppendItem(child, childItem)
306 self.treeMap[childItem] = theDemo
307
308 self.tree.Expand(root)
309 self.tree.Expand(firstChild)
310 EVT_TREE_ITEM_EXPANDED (self.tree, tID, self.OnItemExpanded)
311 EVT_TREE_ITEM_COLLAPSED (self.tree, tID, self.OnItemCollapsed)
312 EVT_TREE_SEL_CHANGED (self.tree, tID, self.OnSelChanged)
313 EVT_LEFT_DOWN (self.tree, self.OnTreeLeftDown)
314
315 # Create a Notebook
316 self.nb = wxNotebook(splitter2, -1, style=wxCLIP_CHILDREN)
317
318 # Set up a wxHtmlWindow on the Overview Notebook page
319 # we put it in a panel first because there seems to be a
320 # refresh bug of some sort (wxGTK) when it is directly in
321 # the notebook...
322 if 0: # the old way
323 self.ovr = wxHtmlWindow(self.nb, -1, size=(400, 400))
324 self.nb.AddPage(self.ovr, self.overviewText)
325
326 else: # hopefully I can remove this hacky code soon, see bug #216861
327 panel = wxPanel(self.nb, -1, style=wxCLIP_CHILDREN)
328 self.ovr = wxHtmlWindow(panel, -1, size=(400, 400))
329 self.nb.AddPage(panel, self.overviewText)
330
331 def OnOvrSize(evt, ovr=self.ovr):
332 ovr.SetSize(evt.GetSize())
333
334 EVT_SIZE(panel, OnOvrSize)
335 EVT_ERASE_BACKGROUND(panel, EmptyHandler)
336
337
338 self.SetOverview(self.overviewText, overview)
339
340
341 # Set up a TextCtrl on the Demo Code Notebook page
342 self.txt = wxTextCtrl(self.nb, -1,
343 style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
344 self.nb.AddPage(self.txt, "Demo Code")
345
346
347 # Set up a log on the View Log Notebook page
348 self.log = wxTextCtrl(splitter2, -1,
349 style = wxTE_MULTILINE|wxTE_READONLY|wxHSCROLL)
350
351 # Set the wxWindows log target to be this textctrl
352 #wxLog_SetActiveTarget(wxLogTextCtrl(self.log))
353
354 # But instead of the above we want to show how to use our own wxLog class
355 wxLog_SetActiveTarget(MyLog(self.log))
356
357
358
359 self.Show(true)
360
361 # add the windows to the splitter and split it.
362 splitter2.SplitHorizontally(self.nb, self.log)
363 splitter.SplitVertically(self.tree, splitter2)
364
365 splitter.SetSashPosition(180, true)
366 splitter.SetMinimumPaneSize(20)
367 splitter2.SetSashPosition(450, true)
368 splitter2.SetMinimumPaneSize(20)
369
370
371
372 # select initial items
373 self.nb.SetSelection(0)
374 self.tree.SelectItem(root)
375
376 if len(sys.argv) == 2:
377 try:
378 selectedDemo = self.treeMap[sys.argv[1]]
379 except:
380 selectedDemo = None
381 if selectedDemo:
382 self.tree.SelectItem(selectedDemo)
383 self.tree.EnsureVisible(selectedDemo)
384
385
386 wxLogMessage('window handle: %s' % self.GetHandle())
387
388
389 #---------------------------------------------
390 def WriteText(self, text):
391 if text[-1:] == '\n':
392 text = text[:-1]
393 wxLogMessage(text)
394
395
396 def write(self, txt):
397 self.WriteText(txt)
398
399 #---------------------------------------------
400 def OnItemExpanded(self, event):
401 item = event.GetItem()
402 wxLogMessage("OnItemExpanded: %s" % self.tree.GetItemText(item))
403 event.Skip()
404
405 #---------------------------------------------
406 def OnItemCollapsed(self, event):
407 item = event.GetItem()
408 wxLogMessage("OnItemCollapsed: %s" % self.tree.GetItemText(item))
409 event.Skip()
410
411 #---------------------------------------------
412 def OnTreeLeftDown(self, event):
413 pt = event.GetPosition();
414 item, flags = self.tree.HitTest(pt)
415 if item == self.tree.GetSelection():
416 self.SetOverview(self.tree.GetItemText(item)+" Overview", self.curOverview)
417 event.Skip()
418
419 #---------------------------------------------
420 def OnSelChanged(self, event):
421 if self.dying:
422 return
423
424 item = event.GetItem()
425 itemText = self.tree.GetItemText(item)
426 self.RunDemo(itemText)
427
428
429 #---------------------------------------------
430 def RunDemo(self, itemText):
431 os.chdir(self.cwd)
432 if self.nb.GetPageCount() == 3:
433 if self.nb.GetSelection() == 2:
434 self.nb.SetSelection(0)
435 self.nb.DeletePage(2)
436
437 if itemText == self.overviewText:
438 self.GetDemoFile('Main.py')
439 self.SetOverview(self.overviewText, overview)
440 self.nb.Refresh();
441 self.window = None
442
443 else:
444 if os.path.exists(itemText + '.py'):
445 wxBeginBusyCursor()
446 wxLogMessage("Running demo %s.py..." % itemText)
447 try:
448 self.GetDemoFile(itemText + '.py')
449 module = __import__(itemText, globals())
450 self.SetOverview(itemText + " Overview", module.overview)
451 finally:
452 wxEndBusyCursor()
453 self.tree.Refresh()
454
455 # in case runTest is modal, make sure things look right...
456 self.nb.Refresh();
457 wxSafeYield()
458
459 self.window = module.runTest(self, self.nb, self) ###
460 if self.window:
461 self.nb.AddPage(self.window, 'Demo')
462 self.nb.SetSelection(2)
463 self.nb.Refresh() # without this wxMac has troubles showing the just added page
464
465 else:
466 self.ovr.SetPage("")
467 self.txt.Clear()
468 self.window = None
469
470
471
472 #---------------------------------------------
473 # Get the Demo files
474 def GetDemoFile(self, filename):
475 self.txt.Clear()
476 try:
477 self.txt.SetValue(open(filename).read())
478 except IOError:
479 self.txt.WriteText("Cannot open %s file." % filename)
480
481 self.txt.SetInsertionPoint(0)
482 self.txt.ShowPosition(0)
483
484 #---------------------------------------------
485 def SetOverview(self, name, text):
486 self.curOverview = text
487 lead = text[:6]
488 if lead != '<html>' and lead != '<HTML>':
489 text = string.join(string.split(text, '\n'), '<br>')
490 self.ovr.SetPage(text)
491 self.nb.SetPageText(0, name)
492
493 #---------------------------------------------
494 # Menu methods
495 def OnFileExit(self, *event):
496 self.Close()
497
498
499 def OnHelpAbout(self, event):
500 from About import MyAboutBox
501 about = MyAboutBox(self)
502 about.ShowModal()
503 about.Destroy()
504
505
506 #---------------------------------------------
507 def OnCloseWindow(self, event):
508 self.dying = true
509 self.window = None
510 self.mainmenu = None
511 if hasattr(self, "tbicon"):
512 del self.tbicon
513 self.Destroy()
514
515
516 #---------------------------------------------
517 def OnIdle(self, event):
518 if self.otherWin:
519 self.otherWin.Raise()
520 self.window = self.otherWin
521 self.otherWin = None
522
523 if self.showTip:
524 self.ShowTip()
525 self.showTip = false
526
527
528 #---------------------------------------------
529 def ShowTip(self):
530 try:
531 showTipText = open(opj("data/showTips")).read()
532 showTip, index = eval(showTipText)
533 except IOError:
534 showTip, index = (1, 0)
535 if showTip:
536 tp = wxCreateFileTipProvider(opj("data/tips.txt"), index)
537 showTip = wxShowTip(self, tp)
538 index = tp.GetCurrentTip()
539 open(opj("data/showTips"), "w").write(str( (showTip, index) ))
540
541
542 #---------------------------------------------
543 def OnDemoMenu(self, event):
544 try:
545 selectedDemo = self.treeMap[self.mainmenu.GetLabel(event.GetId())]
546 except:
547 selectedDemo = None
548 if selectedDemo:
549 self.tree.SelectItem(selectedDemo)
550 self.tree.EnsureVisible(selectedDemo)
551
552
553 #---------------------------------------------
554 def OnTaskBarActivate(self, evt):
555 if self.IsIconized():
556 self.Iconize(false)
557 if not self.IsShown():
558 self.Show(true)
559 self.Raise()
560
561 #---------------------------------------------
562
563 TBMENU_RESTORE = 1000
564 TBMENU_CLOSE = 1001
565
566 def OnTaskBarMenu(self, evt):
567 menu = wxMenu()
568 menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
569 menu.Append(self.TBMENU_CLOSE, "Close")
570 self.tbicon.PopupMenu(menu)
571 menu.Destroy()
572
573 #---------------------------------------------
574 def OnTaskBarClose(self, evt):
575 self.Close()
576
577 # because of the way wxTaskBarIcon.PopupMenu is implemented we have to
578 # prod the main idle handler a bit to get the window to actually close
579 wxGetApp().ProcessIdle()
580
581
582 #---------------------------------------------
583 def OnIconfiy(self, evt):
584 wxLogMessage("OnIconfiy")
585 evt.Skip()
586
587 #---------------------------------------------
588 def OnMaximize(self, evt):
589 wxLogMessage("OnMaximize")
590 evt.Skip()
591
592
593
594
595 #---------------------------------------------------------------------------
596 #---------------------------------------------------------------------------
597
598 class MySplashScreen(wxSplashScreen):
599 def __init__(self):
600 bmp = wxImage(opj("bitmaps/splash.gif")).ConvertToBitmap()
601 wxSplashScreen.__init__(self, bmp,
602 wxSPLASH_CENTRE_ON_SCREEN|wxSPLASH_TIMEOUT,
603 4000, None, -1,
604 style = wxSIMPLE_BORDER|wxFRAME_NO_TASKBAR|wxSTAY_ON_TOP)
605 EVT_CLOSE(self, self.OnClose)
606
607 def OnClose(self, evt):
608 frame = wxPythonDemo(None, -1, "wxPython: (A Demonstration)")
609 frame.Show(true)
610 evt.Skip() # Make sure the default handler runs too...
611
612
613 class MyApp(wxApp):
614 def OnInit(self):
615 """
616 Create and show the splash screen. It will then create and show
617 the main frame when it is time to do so.
618 """
619 wxInitAllImageHandlers()
620 splash = MySplashScreen()
621 splash.Show()
622 return true
623
624
625
626 #---------------------------------------------------------------------------
627
628 def main():
629 try:
630 demoPath = os.path.dirname(__file__)
631 os.chdir(demoPath)
632 except:
633 pass
634 app = MyApp(wxPlatform == "__WXMAC__")
635 app.MainLoop()
636
637
638 #---------------------------------------------------------------------------
639
640
641
642 overview = """<html><body>
643 <h2>Python</h2>
644
645 Python is an interpreted, interactive, object-oriented programming
646 language often compared to Tcl, Perl, Scheme, or Java.
647
648 <p> Python combines remarkable power with very clear syntax. It has
649 modules, classes, exceptions, very high level dynamic data types, and
650 dynamic typing. There are interfaces to many system calls and
651 libraries, and new built-in modules are easily written in C or
652 C++. Python is also usable as an extension language for applications
653 that need a programmable interface. <p>
654
655 <h2>wxWindows</h2>
656
657 wxWindows is a free C++ framework designed to make cross-platform
658 programming child's play. Well, almost. wxWindows 2 supports Windows
659 3.1/95/98/NT, Unix with GTK/Motif/Lesstif, with a Mac version
660 underway. Other ports are under consideration. <p>
661
662 wxWindows is a set of libraries that allows C++ applications to
663 compile and run on several different types of computers, with minimal
664 source code changes. There is one library per supported GUI (such as
665 Motif, or Windows). As well as providing a common API (Application
666 Programming Interface) for GUI functionality, it provides
667 functionality for accessing some commonly-used operating system
668 facilities, such as copying or deleting files. wxWindows is a
669 'framework' in the sense that it provides a lot of built-in
670 functionality, which the application can use or replace as required,
671 thus saving a great deal of coding effort. Basic data structures such
672 as strings, linked lists and hash tables are also supported.
673
674 <p>
675 <h2>wxPython</h2>
676
677 wxPython is a Python extension module that encapsulates the wxWindows
678 GUI classes. Currently it is only available for the Win32 and GTK
679 ports of wxWindows, but as soon as the other ports are brought up to
680 the same level as Win32 and GTK, it should be fairly trivial to
681 enable wxPython to be used with the new GUI.
682
683 <p>
684
685 The wxPython extension module attempts to mirror the class heiarchy
686 of wxWindows as closely as possible. This means that there is a
687 wxFrame class in wxPython that looks, smells, tastes and acts almost
688 the same as the wxFrame class in the C++ version. Unfortunately,
689 because of differences in the languages, wxPython doesn't match
690 wxWindows exactly, but the differences should be easy to absorb
691 because they are natural to Python. For example, some methods that
692 return multiple values via argument pointers in C++ will return a
693 tuple of values in Python.
694
695 <p>
696
697 There is still much to be done for wxPython, many classes still need
698 to be mirrored. Also, wxWindows is still somewhat of a moving target
699 so it is a bit of an effort just keeping wxPython up to date. On the
700 other hand, there are enough of the core classes completed that
701 useful applications can be written.
702
703 <p>
704
705 wxPython is close enough to the C++ version that the majority of
706 the wxPython documentation is actually just notes attached to the C++
707 documents that describe the places where wxPython is different. There
708 is also a series of sample programs included, and a series of
709 documentation pages that assist the programmer in getting started
710 with wxPython.
711
712 """
713
714
715 #----------------------------------------------------------------------------
716 #----------------------------------------------------------------------------
717
718 if __name__ == '__main__':
719 main()
720
721 #----------------------------------------------------------------------------
722
723
724
725
726
727
728