File: PmwBase.py.html

package info (click to toggle)
python-pmw 0.6.2-0.1
  • links: PTS
  • area: main
  • in suites: hamm
  • size: 1,652 kB
  • ctags: 2,716
  • sloc: python: 10,720; makefile: 44; sh: 24
file content (1432 lines) | stat: -rw-r--r-- 64,150 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
<!DOCTYPE HTML PUBLIC "-//Netscape_Microsoft//DTD HTML 3.0//EN">
<HTML>

<!-- This file generated using the Python HTMLgen module. -->
<HEAD>
  <META NAME="GENERATOR" CONTENT="HTMLgen 1.1">
        <TITLE>PmwBase.py</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<PRE>
<FONT COLOR="#DD0000"># Pmw megawidget framework.</FONT>

<FONT COLOR="#DD0000"># This module provides a framework for building megawidgets.  It</FONT>
<FONT COLOR="#DD0000"># contains the MegaArchetype class which manages component widgets and</FONT>
<FONT COLOR="#DD0000"># configuration options.  Also provided are the MegaToplevel and</FONT>
<FONT COLOR="#DD0000"># MegaWidget classes, derived from the MegaArchetype class.  The</FONT>
<FONT COLOR="#DD0000"># MegaToplevel class contains a Tkinter Toplevel widget to act as the</FONT>
<FONT COLOR="#DD0000"># container of the megawidget.  This is used as the base class of all</FONT>
<FONT COLOR="#DD0000"># megawidgets that are contained in their own top level window, such</FONT>
<FONT COLOR="#DD0000"># as a Dialog window.  The MegaWidget class contains a Tkinter Frame</FONT>
<FONT COLOR="#DD0000"># to act as the container of the megawidget.  This is used as the base</FONT>
<FONT COLOR="#DD0000"># class of all other megawidgets, such as a ComboBox or ButtonBox.</FONT>
<FONT COLOR="#DD0000">#</FONT>
<FONT COLOR="#DD0000"># Megawidgets are built by creating a class that inherits from either</FONT>
<FONT COLOR="#DD0000"># the MegaToplevel or MegaWidget class.</FONT>

import string
import sys
import traceback
import types
import Tkinter
from PmwUtils import forwardmethods

<FONT COLOR="#DD0000"># Constant used to indicate that an option can only be set by a call</FONT>
<FONT COLOR="#DD0000"># to the constructor.</FONT>
INITOPT = [42]
_DEFAULT_OPTION_VALUE = [69]
_useTkOptionDb = 0

<FONT COLOR="#DD0000"># Symbolic constants for the indexes into an optionInfo list.</FONT>
_OPT_DEFAULT         = 0
_OPT_VALUE           = 1
_OPT_FUNCTION        = 2

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<STRONG><FONT COLOR="#CC6600">class MegaArchetype</FONT></STRONG>:
    <FONT COLOR="#DD0000"># Megawidget abstract root class.</FONT>

    <FONT COLOR="#DD0000"># This class provides methods which are inherited by classes</FONT>
    <FONT COLOR="#DD0000"># implementing useful bases (this class doesn't provide a</FONT>
    <FONT COLOR="#DD0000"># container widget inside which the megawidget can be built).</FONT>

<STRONG>    def __init__</STRONG>(self, parent = None, hullClass = None):

	<FONT COLOR="#DD0000"># Mapping from each megawidget option to a list of information</FONT>
	<FONT COLOR="#DD0000"># about the option</FONT>
	<FONT COLOR="#DD0000">#   - default value</FONT>
	<FONT COLOR="#DD0000">#   - current value</FONT>
	<FONT COLOR="#DD0000">#   - function to call when the option is initialised in the</FONT>
	<FONT COLOR="#DD0000">#     call to initialiseoptions() in the constructor or</FONT>
	<FONT COLOR="#DD0000">#     modified via configure().  If this is INITOPT, the</FONT>
	<FONT COLOR="#DD0000">#     option is an initialisation option (an option that can</FONT>
	<FONT COLOR="#DD0000">#     be set by the call to the constructor but can not be</FONT>
	<FONT COLOR="#DD0000">#     used with configure).</FONT>
	<FONT COLOR="#DD0000"># This mapping is not initialised here, but in the call to</FONT>
	<FONT COLOR="#DD0000"># defineoptions() which precedes construction of this base class.</FONT>
	<FONT COLOR="#DD0000">#</FONT>
	<FONT COLOR="#DD0000"># self._optionInfo = {}</FONT>

	<FONT COLOR="#DD0000"># Mapping from each component name to a tuple of information</FONT>
	<FONT COLOR="#DD0000"># about the component.</FONT>
	<FONT COLOR="#DD0000">#   - component widget instance</FONT>
	<FONT COLOR="#DD0000">#   - configure function of widget instance</FONT>
	<FONT COLOR="#DD0000">#   - the class of the widget (Frame, EntryField, etc)</FONT>
	<FONT COLOR="#DD0000">#   - cget function of widget instance</FONT>
	<FONT COLOR="#DD0000">#   - the name of the component group of this component, if any</FONT>
	self.__componentInfo = {}

	<FONT COLOR="#DD0000"># Mapping from alias names to the names of components or</FONT>
	<FONT COLOR="#DD0000"># sub-components.</FONT>
	self.__componentAliases = {}

	<FONT COLOR="#DD0000"># Contains information about the keywords provided to the</FONT>
	<FONT COLOR="#DD0000"># constructor.  It is a mapping from the keyword to a tuple</FONT>
	<FONT COLOR="#DD0000"># containing:</FONT>
	<FONT COLOR="#DD0000">#    - value of keyword</FONT>
	<FONT COLOR="#DD0000">#    - a boolean indicating if the keyword has been used.</FONT>
	<FONT COLOR="#DD0000"># A keyword is used if, during the construction of a megawidget,</FONT>
	<FONT COLOR="#DD0000">#    - it is defined in a call to defineoptions(), or</FONT>
	<FONT COLOR="#DD0000">#    - it references, by name, a component of the megawidget, or</FONT>
	<FONT COLOR="#DD0000">#    - it references, by group, at least one component</FONT>
	<FONT COLOR="#DD0000"># At the end of megawidget construction, a call is made to</FONT>
	<FONT COLOR="#DD0000"># initialiseoptions() which reports an error if there are</FONT>
	<FONT COLOR="#DD0000"># unused options given to the constructor.</FONT>
	<FONT COLOR="#DD0000">#</FONT>
	<FONT COLOR="#DD0000"># self._constructorKeywords = {}</FONT>

	if hullClass is None:
	    self._hull = None
	else:
	    if parent is None:
		parent = Tkinter._default_root

	    <FONT COLOR="#DD0000"># Create the hull.</FONT>
	    self._hull = self.createcomponent(<FONT COLOR="#009900">'hull'</FONT>,
		    (), None,
		    hullClass, (parent,))
	    _hullToMegaWidget[self._hull] = self

	    if _useTkOptionDb:
		<FONT COLOR="#DD0000"># Now that a widget has been created, query the Tk</FONT>
		<FONT COLOR="#DD0000"># option database to get the default values for the</FONT>
		<FONT COLOR="#DD0000"># options which have not been set in the call to the</FONT>
		<FONT COLOR="#DD0000"># constructor.  This assumes that defineoptions() is</FONT>
		<FONT COLOR="#DD0000"># called before the __init__().</FONT>
		option_get = self.option_get
		VALUE = _OPT_VALUE
		DEFAULT = _OPT_DEFAULT
		for name, info in self._optionInfo.items():
		    value = info[VALUE]
		    if value is _DEFAULT_OPTION_VALUE:
			resourceClass = string.upper(name[0]) + name[1:]
			value = option_get(name, resourceClass)
			if value != <FONT COLOR="#009900">''</FONT>:
			    try:
				<FONT COLOR="#DD0000"># Convert the string to int/float/tuple, etc</FONT>
				value = eval(value, {<FONT COLOR="#009900">'__builtins__'</FONT>: {}})
			    except:
				pass
			    info[VALUE] = value
			else:
			    info[VALUE] = info[DEFAULT]

    <FONT COLOR="#DD0000">#======================================================================</FONT>
    <FONT COLOR="#DD0000"># Methods used (mainly) during the construction of the megawidget.</FONT>

<STRONG>    def defineoptions</STRONG>(self, keywords, optionDefs):
	<FONT COLOR="#DD0000"># Create options, providing the default value and the method</FONT>
	<FONT COLOR="#DD0000"># to call when the value is changed.  If any option created by</FONT>
	<FONT COLOR="#DD0000"># base classes has the same name as one in &lt;optionDefs&gt;, the</FONT>
	<FONT COLOR="#DD0000"># base class's value and function will be overriden.</FONT>

	<FONT COLOR="#DD0000"># This should be called before the constructor of the base</FONT>
	<FONT COLOR="#DD0000"># class, so that default values defined in the derived class</FONT>
	<FONT COLOR="#DD0000"># override those in the base class.</FONT>

	if not hasattr(self, <FONT COLOR="#009900">'_constructorKeywords'</FONT>):
	    tmp = {}
	    for option, value in keywords.items():
		tmp[option] = [value, 0]
	    self._constructorKeywords = tmp
	    self._optionInfo = {}

	<FONT COLOR="#DD0000"># optimisations:</FONT>
	optionInfo = self._optionInfo
	optionInfo_has_key = optionInfo.has_key
	keywords = self._constructorKeywords
	keywords_has_key = keywords.has_key
	FUNCTION = _OPT_FUNCTION

	for name, default, function in optionDefs:
	    index = string.find(name, <FONT COLOR="#009900">'_'</FONT>)
	    if index < 0:
		<FONT COLOR="#DD0000"># The option will already exist if it has been defined</FONT>
		<FONT COLOR="#DD0000"># in a derived class.  In this case, do not override the</FONT>
		<FONT COLOR="#DD0000"># default value of the option or the callback function</FONT>
		<FONT COLOR="#DD0000"># if it is not None.</FONT>
		if not optionInfo_has_key(name):
		    if keywords_has_key(name):
			value = keywords[name][0]
			optionInfo[name] = [default, value, function]
			del keywords[name]
		    else:
			if _useTkOptionDb:
			    optionInfo[name] = \
				    [default, _DEFAULT_OPTION_VALUE, function]
			else:
			    optionInfo[name] = [default, default, function]
		elif optionInfo[name][FUNCTION] is None:
		    optionInfo[name][FUNCTION] = function
	    else:
		<FONT COLOR="#DD0000"># This option is of the form "component_option".  If this is</FONT>
		<FONT COLOR="#DD0000"># not already defined in self._constructorKeywords add it.</FONT>
		<FONT COLOR="#DD0000"># This allows a derived class to override the default value</FONT>
		<FONT COLOR="#DD0000"># of an option of a component of a base class.</FONT>
		if not keywords_has_key(name):
		    keywords[name] = [default, 0]

<STRONG>    def createcomponent</STRONG>(self, name, aliases, group, widgetClass, widgetArgs, **kw):
	<FONT COLOR="#DD0000"># Create a component (during construction or later).</FONT>

	if hasattr(self, <FONT COLOR="#009900">'_constructorKeywords'</FONT>):
	    keywords = self._constructorKeywords
	else:
	    keywords = {}
	for alias, component in aliases:
	    <FONT COLOR="#DD0000"># Create aliases to the component and its sub-components.</FONT>
	    index = string.find(component, <FONT COLOR="#009900">'_'</FONT>)
	    if index < 0:
		self.__componentAliases[alias] = (component, None)
	    else:
		mainComponent = component[:index]
		subComponent = component[(index + 1):]
		self.__componentAliases[alias] = (mainComponent, subComponent)

	    <FONT COLOR="#DD0000"># Remove aliases from the constructor keyword arguments by</FONT>
	    <FONT COLOR="#DD0000"># replacing any keyword arguments that begin with *alias*</FONT>
	    <FONT COLOR="#DD0000"># with corresponding keys beginning with *component*.</FONT>

	    alias = alias + <FONT COLOR="#009900">'_'</FONT>
	    aliasLen = len(alias)
	    for option in keywords.keys():
		if len(option) > aliasLen and option[:aliasLen] == alias:
		    newkey = component + <FONT COLOR="#009900">'_'</FONT> + option[aliasLen:]
		    keywords[newkey] = keywords[option]
		    del keywords[option]

	componentName = name + <FONT COLOR="#009900">'_'</FONT>
	nameLen = len(componentName)
	for option in keywords.keys():
	    if len(option) > nameLen and option[:nameLen] == componentName:
		<FONT COLOR="#DD0000"># The keyword argument refers to this component, so add</FONT>
		<FONT COLOR="#DD0000"># this to the options to use when constructing the widget.</FONT>
		kw[option[nameLen:]] = keywords[option][0]
		del keywords[option]
	    else:
		<FONT COLOR="#DD0000"># Check if this keyword argument refers to the group</FONT>
		<FONT COLOR="#DD0000"># of this component.  If so, add this to the options</FONT>
		<FONT COLOR="#DD0000"># to use when constructing the widget.  Mark the</FONT>
		<FONT COLOR="#DD0000"># keyword argument as being used, but do not remove it</FONT>
		<FONT COLOR="#DD0000"># since it may be required when creating another</FONT>
		<FONT COLOR="#DD0000"># component.</FONT>
		index = string.find(option, <FONT COLOR="#009900">'_'</FONT>)
		if index >= 0 and group == option[:index]:
		    rest = option[(index + 1):]
		    kw[rest] = keywords[option][0]
		    keywords[option][1] = 1

	if kw.has_key(<FONT COLOR="#009900">'pyclass'</FONT>):
	    widgetClass = kw[<FONT COLOR="#009900">'pyclass'</FONT>]
	    del kw[<FONT COLOR="#009900">'pyclass'</FONT>]
	if widgetClass is None:
	    return None
	widget = apply(widgetClass, widgetArgs, kw)
	componentClass = widget.__class__.__name__
	self.__componentInfo[name] = (widget, widget.configure,
		componentClass, widget.cget, group)

	return widget

<STRONG>    def destroycomponent</STRONG>(self, name):
	<FONT COLOR="#DD0000"># Remove a megawidget component.</FONT>

	<FONT COLOR="#DD0000"># This command is for use by megawidget designers to destroy a</FONT>
	<FONT COLOR="#DD0000"># megawidget component.</FONT>

	self.__componentInfo[name][0].destroy()
	del self.__componentInfo[name]

<STRONG>    def createlabel</STRONG>(self, parent, childCols = 1, childRows = 1):

	labelpos = self[<FONT COLOR="#009900">'labelpos'</FONT>]
	labelmargin = self[<FONT COLOR="#009900">'labelmargin'</FONT>]
	if labelpos is None:
	    return

	label = self.createcomponent(<FONT COLOR="#009900">'label'</FONT>,
		(), None,
		Tkinter.Label, (parent,))

	if labelpos[0] in <FONT COLOR="#009900">'ns'</FONT>:
	    <FONT COLOR="#DD0000"># vertical layout</FONT>
	    if labelpos[0] == <FONT COLOR="#009900">'n'</FONT>:
		row = 0
		margin = 1
	    else:
		row = childRows + 3
		margin = row - 1
	    label.grid(column=2, row=row, columnspan=childCols, sticky=labelpos)
	    parent.grid_rowconfigure(margin, minsize=labelmargin)
	else:
	    <FONT COLOR="#DD0000"># horizontal layout</FONT>
	    if labelpos[0] == <FONT COLOR="#009900">'w'</FONT>:
		col = 0
		margin = 1
	    else:
		col = childCols + 3
		margin = col - 1
	    label.grid(column=col, row=2, rowspan=childRows, sticky=labelpos)
	    parent.grid_columnconfigure(margin, minsize=labelmargin)

<STRONG>    def initialiseoptions</STRONG>(self, myClass):
	if self.__class__ is myClass:
	    unusedOptions = []
	    keywords = self._constructorKeywords
	    for name in keywords.keys():
		used = keywords[name][1]
		if not used:
		    unusedOptions.append(name)
	    self._constructorKeywords = {}
	    if len(unusedOptions) > 0:
		if len(unusedOptions) == 1:
		    text = <FONT COLOR="#009900">'Unknown option "'</FONT>
		else:
		    text = <FONT COLOR="#009900">'Unknown options "'</FONT>
		raise TypeError, text + string.join(unusedOptions, <FONT COLOR="#009900">', '</FONT>) + \
			<FONT COLOR="#009900">'" for '</FONT> + myClass.__name__

	    <FONT COLOR="#DD0000"># Call the configuration callback function for every option.</FONT>
	    FUNCTION = _OPT_FUNCTION
	    for info in self._optionInfo.values():
		func = info[FUNCTION]
		if func is not None and func is not INITOPT:
		    func()

    <FONT COLOR="#DD0000">#======================================================================</FONT>
    <FONT COLOR="#DD0000"># Method used to configure the megawidget.</FONT>

<STRONG>    def configure</STRONG>(self, option=None, **kw):
	<FONT COLOR="#DD0000"># Query or configure the megawidget options.</FONT>
	<FONT COLOR="#DD0000">#</FONT>
	<FONT COLOR="#DD0000"># If not empty, *kw* is a dictionary giving new</FONT>
	<FONT COLOR="#DD0000"># values for some of the options of this megawidget or its</FONT>
	<FONT COLOR="#DD0000"># components.  For options defined for this megawidget, set</FONT>
	<FONT COLOR="#DD0000"># the value of the option to the new value and call the</FONT>
	<FONT COLOR="#DD0000"># configuration callback function, if any.  For options of the</FONT>
	<FONT COLOR="#DD0000"># form &lt;component&gt;_&lt;option&gt;, where &lt;component&gt; is a component</FONT>
	<FONT COLOR="#DD0000"># of this megawidget, call the configure method of the</FONT>
	<FONT COLOR="#DD0000"># component giving it the new value of the option.  The</FONT>
	<FONT COLOR="#DD0000"># &lt;component&gt; part may be an alias or a component group name.</FONT>
	<FONT COLOR="#DD0000">#</FONT>
	<FONT COLOR="#DD0000"># If *option* is None, return all megawidget configuration</FONT>
	<FONT COLOR="#DD0000"># options and settings.  Options are returned as standard 5</FONT>
	<FONT COLOR="#DD0000"># element tuples</FONT>
	<FONT COLOR="#DD0000">#</FONT>
	<FONT COLOR="#DD0000"># If *option* is a string, return the 5 element tuple for the</FONT>
	<FONT COLOR="#DD0000"># given configuration option.</FONT>

	<FONT COLOR="#DD0000"># First, deal with the option queries.</FONT>
	if len(kw) == 0:
	    <FONT COLOR="#DD0000"># This configure call is querying the values of one or all options.</FONT>
	    <FONT COLOR="#DD0000"># Return 5-tuples:</FONT>
	    <FONT COLOR="#DD0000">#     (optionName, resourceName, resourceClass, default, value)</FONT>
	    if option is None:
		rtn = {}
		for option, config in self._optionInfo.items():
		    resourceClass = string.upper(option[0]) + option[1:]
		    rtn[option] = (option, option, resourceClass,
			    config[_OPT_DEFAULT], config[_OPT_VALUE])
		return rtn
	    else:
		config = self._optionInfo[option]
		resourceClass = string.upper(option[0]) + option[1:]
		return (option, option, resourceClass, config[_OPT_DEFAULT],
			config[_OPT_VALUE])

	<FONT COLOR="#DD0000"># optimisations:</FONT>
	optionInfo = self._optionInfo
	optionInfo_has_key = optionInfo.has_key
	componentInfo = self.__componentInfo
	componentInfo_has_key = componentInfo.has_key
	componentAliases = self.__componentAliases
	componentAliases_has_key = componentAliases.has_key
	VALUE = _OPT_VALUE
	FUNCTION = _OPT_FUNCTION

	<FONT COLOR="#DD0000"># This will contain a list of options in *kw* which</FONT>
	<FONT COLOR="#DD0000"># are known to this megawidget.</FONT>
	directOptions = []

	<FONT COLOR="#DD0000"># This will contain information about the options in</FONT>
	<FONT COLOR="#DD0000"># *kw* of the form &lt;component&gt;_&lt;option&gt;, where</FONT>
	<FONT COLOR="#DD0000"># &lt;component&gt; is a component of this megawidget.  It is a</FONT>
	<FONT COLOR="#DD0000"># dictionary whose keys are the configure method of each</FONT>
	<FONT COLOR="#DD0000"># component and whose values are a dictionary of options and</FONT>
	<FONT COLOR="#DD0000"># values for the component.</FONT>
	indirectOptions = {}
	indirectOptions_has_key = indirectOptions.has_key

	for option, value in kw.items():
	    if optionInfo_has_key(option):
		<FONT COLOR="#DD0000"># This is one of the options of this megawidget. </FONT>
		<FONT COLOR="#DD0000"># Check it is an initialisation option.</FONT>
		if optionInfo[option][FUNCTION] is INITOPT:
		    raise IndexError, \
			    <FONT COLOR="#009900">'Cannot configure initialisation option "'</FONT> \
			    + option + <FONT COLOR="#009900">'" for '</FONT> + self.__class__.__name__
		optionInfo[option][VALUE] = value
		directOptions.append(option)
	    else:
		index = string.find(option, <FONT COLOR="#009900">'_'</FONT>)
		if index >= 0:
		    <FONT COLOR="#DD0000"># This option may be of the form &lt;component&gt;_&lt;option&gt;.</FONT>
		    component = option[:index]
		    componentOption = option[(index + 1):]

		    <FONT COLOR="#DD0000"># Expand component alias</FONT>
		    if componentAliases_has_key(component):
			component, subComponent = componentAliases[component]
			if subComponent is not None:
			    componentOption = subComponent + <FONT COLOR="#009900">'_'</FONT> \
				    + componentOption

			<FONT COLOR="#DD0000"># Expand option string to write on error</FONT>
			option = component + <FONT COLOR="#009900">'_'</FONT> + componentOption

		    if componentInfo_has_key(component):
			<FONT COLOR="#DD0000"># Configure the named component</FONT>
			componentConfigFuncs = [componentInfo[component][1]]
		    else:
			<FONT COLOR="#DD0000"># Check if this is a group name and configure all</FONT>
			<FONT COLOR="#DD0000"># components in the group.</FONT>
			componentConfigFuncs = []
			for info in componentInfo.values():
			    if info[4] == component:
			        componentConfigFuncs.append(info[1])

			if len(componentConfigFuncs) == 0:
			    raise IndexError, <FONT COLOR="#009900">'Unknown option "'</FONT> + option + \
				    <FONT COLOR="#009900">'" for '</FONT> + self.__class__.__name__

		    <FONT COLOR="#DD0000"># Add the configure method(s) (may be more than</FONT>
		    <FONT COLOR="#DD0000"># one if this is configuring a component group)</FONT>
		    <FONT COLOR="#DD0000"># and option/value to dictionary.</FONT>
		    for componentConfigFunc in componentConfigFuncs:
			if not indirectOptions_has_key(componentConfigFunc):
			    indirectOptions[componentConfigFunc] = {}
			indirectOptions[componentConfigFunc][componentOption] \
				= value
		else:
		    raise IndexError, <FONT COLOR="#009900">'Unknown option "'</FONT> + option + \
			    <FONT COLOR="#009900">'" for '</FONT> + self.__class__.__name__

	<FONT COLOR="#DD0000"># Call the configure methods for any components.</FONT>
	map(apply, indirectOptions.keys(),
		((),) * len(indirectOptions), indirectOptions.values())

	<FONT COLOR="#DD0000"># Call the configuration callback function for each option.</FONT>
	for option in directOptions:
	    info = optionInfo[option]
	    func = info[_OPT_FUNCTION]
	    if func is not None:
	      func()

    <FONT COLOR="#DD0000">#======================================================================</FONT>
    <FONT COLOR="#DD0000"># Methods used to query the megawidget.</FONT>

<STRONG>    def component</STRONG>(self, name):
	<FONT COLOR="#DD0000"># Return a component widget of the megawidget given the</FONT>
	<FONT COLOR="#DD0000"># component's name</FONT>
	<FONT COLOR="#DD0000"># This allows the user of a megawidget to access and configure</FONT>
	<FONT COLOR="#DD0000"># widget components directly.</FONT>

	<FONT COLOR="#DD0000"># Find the main component and any subcomponents</FONT>
	index = string.find(name, <FONT COLOR="#009900">'_'</FONT>)
	if index < 0:
	    component = name
	    remainingComponents = None
	else:
	    component = name[:index]
	    remainingComponents = name[(index + 1):]

	<FONT COLOR="#DD0000"># Expand component alias</FONT>
	if self.__componentAliases.has_key(component):
	    component, subComponent = self.__componentAliases[component]
	    if subComponent is not None:
		if remainingComponents is None:
		    remainingComponents = subComponent
		else:
		    remainingComponents = subComponent + <FONT COLOR="#009900">'_'</FONT> \
			    + remainingComponents

	widget = self.__componentInfo[component][0]
	if remainingComponents is None:
	    return widget
	else:
	    return widget.component(remainingComponents)

<STRONG>    def interior</STRONG>(self):
	<FONT COLOR="#DD0000"># Return the widget framing the remaining interior space.</FONT>
	<FONT COLOR="#DD0000"># By default it returns the hull.</FONT>

	<FONT COLOR="#DD0000"># Return the widget framing the interior space of the</FONT>
	<FONT COLOR="#DD0000"># megawidget.  For the MegaWidget and MegaToplevel classes,</FONT>
	# this is the same as the <FONT COLOR="#009900">'hull'</FONT> component.  When a subclass
	<FONT COLOR="#DD0000"># is creating components they should usually use interior() as</FONT>
	<FONT COLOR="#DD0000"># the widget in which components should be contained. </FONT>
	<FONT COLOR="#DD0000"># Megawidgets (such as a toplevel window with a menu bar and</FONT>
	<FONT COLOR="#DD0000"># status bar) which can be further subclassed should redefine</FONT>
	<FONT COLOR="#DD0000"># interior() to return the widget in which subclasses should</FONT>
	<FONT COLOR="#DD0000"># create their components.  The overall containing widget is</FONT>
	# always available as <FONT COLOR="#009900">'hull'</FONT> (and should not be redefined).
	
	<FONT COLOR="#DD0000"># This method is for use by MegaWidget or MegaToplevel</FONT>
	<FONT COLOR="#DD0000"># subclasses.  Other classes should still access all</FONT>
	<FONT COLOR="#DD0000"># megawidget components by name e.g.  a widget redefining</FONT>
	<FONT COLOR="#DD0000"># interior() should create it as a component of the</FONT>
	<FONT COLOR="#DD0000"># megawidget.</FONT>

	return self._hull

<STRONG>    def __str__</STRONG>(self):
	return str(self._hull)

<STRONG>    def cget</STRONG>(self, option):
	<FONT COLOR="#DD0000"># Get current configuration setting.</FONT>

	# Return the value of an option, for example myWidget[<FONT COLOR="#009900">'font'</FONT>]. 

	if self._optionInfo.has_key(option):
	    return self._optionInfo[option][_OPT_VALUE]
	else:
	    index = string.find(option, <FONT COLOR="#009900">'_'</FONT>)
	    if index >= 0:
		component = option[:index]
		componentOption = option[(index + 1):]

		<FONT COLOR="#DD0000"># Expand component alias</FONT>
		if self.__componentAliases.has_key(component):
		    component, subComponent = self.__componentAliases[component]
		    if subComponent is not None:
			componentOption = subComponent + <FONT COLOR="#009900">'_'</FONT> + componentOption

		    <FONT COLOR="#DD0000"># Expand option string to write on error</FONT>
		    option = component + <FONT COLOR="#009900">'_'</FONT> + componentOption

		if self.__componentInfo.has_key(component):
		    <FONT COLOR="#DD0000"># Call cget on the component.</FONT>
		    componentCget = self.__componentInfo[component][3]
		    return componentCget(componentOption)
		else:
		    <FONT COLOR="#DD0000"># If this is a group name, call cget for one of</FONT>
		    <FONT COLOR="#DD0000"># the components in the group.</FONT>
		    for info in self.__componentInfo.values():
			if info[4] == component:
			    componentCget = info[3]
			    return componentCget(componentOption)

	raise IndexError, <FONT COLOR="#009900">'Unknown option "'</FONT> + option + \
		<FONT COLOR="#009900">'" for '</FONT> + self.__class__.__name__

    __getitem__ = cget

<STRONG>    def isinitoption</STRONG>(self, option):
	return self._optionInfo[option][_OPT_FUNCTION] is INITOPT

<STRONG>    def options</STRONG>(self):
	options = []
	if hasattr(self, <FONT COLOR="#009900">'_optionInfo'</FONT>):
	    for option, info in self._optionInfo.items():
		isinit = info[_OPT_FUNCTION] is INITOPT
		default = info[_OPT_DEFAULT]
		options.append((option, default, isinit))
	    options.sort()
	return options

<STRONG>    def components</STRONG>(self):
	<FONT COLOR="#DD0000"># Return a list of all components.</FONT>

	# This list includes the <FONT COLOR="#009900">'hull'</FONT> component and all widget subcomponents

	names = self.__componentInfo.keys()
	names.sort()
	return names

<STRONG>    def componentaliases</STRONG>(self):
	<FONT COLOR="#DD0000"># Return a list of all component aliases.</FONT>

	componentAliases = self.__componentAliases

	names = componentAliases.keys()
	names.sort()
	rtn = []
	for alias in names:
	    (mainComponent, subComponent) = componentAliases[alias]
	    if subComponent is None:
		rtn.append((alias, mainComponent))
	    else:
		rtn.append((alias, mainComponent + <FONT COLOR="#009900">'_'</FONT> + subComponent))
	    
	return rtn

<STRONG>    def componentgroup</STRONG>(self, name):
	return self.__componentInfo[name][4]

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<STRONG><FONT COLOR="#CC6600">class MegaToplevel</FONT></STRONG>(MegaArchetype):
    <FONT COLOR="#DD0000"># Toplevel megawidget base class.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># The MegaToplevel class inherits everything from the</FONT>
    <FONT COLOR="#DD0000"># MegaArchetype class, and adds a Tkinter Toplevel called the</FONT>
    # <FONT COLOR="#009900">'hull'</FONT> component to represent the body of the megawidget.  The
    <FONT COLOR="#DD0000"># window class name for the hull is set to the most-specific class</FONT>
    <FONT COLOR="#DD0000"># name for the megawidget.  The class acts as the base class of</FONT>
    <FONT COLOR="#DD0000"># megawidgets that are contained in their own toplevel window. </FONT>
    <FONT COLOR="#DD0000"># Derived classes specialise this widget by creating other widget</FONT>
    <FONT COLOR="#DD0000"># components as children of the hull.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># The MegaToplevel class forwards all Tkinter Toplevel methods on</FONT>
    <FONT COLOR="#DD0000"># to the hull component.  For example, methods such as show and</FONT>
    <FONT COLOR="#DD0000"># activate and all the wm methods can be used with a MegaToplevel</FONT>
    <FONT COLOR="#DD0000"># megawidget.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># Components</FONT>
    <FONT COLOR="#DD0000">#   hull             exterior of the megawidget (a Tkinter Toplevel)</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># Options</FONT>
    <FONT COLOR="#DD0000">#   activatecommand  called whenever the toplevel is activated</FONT>
    <FONT COLOR="#DD0000">#   title            window manager title of the toplevel window</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># Use show/withdraw for normal use and activate/deactivate for</FONT>
    <FONT COLOR="#DD0000"># modal dialog use.  If the window is deleted by the window</FONT>
    <FONT COLOR="#DD0000"># manager while being shown normally, the default behaviour is to</FONT>
    <FONT COLOR="#DD0000"># destroy the window.  If the window is deleted by the window</FONT>
    <FONT COLOR="#DD0000"># manager while the window is active (ie:  when used as a modal</FONT>
    <FONT COLOR="#DD0000"># dialog), the window is deactivated.  Use the userdeletefunc()</FONT>
    <FONT COLOR="#DD0000"># and usermodaldeletefunc() methods to override these behaviours. </FONT>
    # Do not call protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>, func) directly if you
    <FONT COLOR="#DD0000"># want to use this toplevel as a modal dialog.</FONT>

    <FONT COLOR="#DD0000"># The currently active windows form a stack with the most recently</FONT>
    <FONT COLOR="#DD0000"># activated window at the top of the stack.  All mouse and</FONT>
    <FONT COLOR="#DD0000"># keyboard events are sent to this top window.  When it</FONT>
    <FONT COLOR="#DD0000"># deactivates, the next window in the stack starts to receive</FONT>
    <FONT COLOR="#DD0000"># events.  &lt;_grabStack&gt; is a list of tuples.  Each tuple contains</FONT>
    <FONT COLOR="#DD0000"># the active widget and a boolean indicating whether the window</FONT>
    <FONT COLOR="#DD0000"># was activated in global mode.</FONT>
    _grabStack = []

<STRONG>    def __init__</STRONG>(self, parent = None, **kw):
	<FONT COLOR="#DD0000"># Define the options for this megawidget.</FONT>
	optiondefs = (
	    (<FONT COLOR="#009900">'activatecommand'</FONT>,  None,  None),
	    (<FONT COLOR="#009900">'title'</FONT>,            None,  self._settitle),
	    (<FONT COLOR="#009900">'hull_class'</FONT>,       self.__class__.__name__,  None),
	)
	self.defineoptions(kw, optiondefs)

	<FONT COLOR="#DD0000"># Initialise the base class (after defining the options).</FONT>
	MegaArchetype.__init__(self, parent, Tkinter.Toplevel)

	<FONT COLOR="#DD0000"># Initialise instance.</FONT>

	self.protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>, self._userDeleteWindow)

	<FONT COLOR="#DD0000"># Initialise instance variables.</FONT>

	self._firstShowing = 1
	<FONT COLOR="#DD0000"># Used by show() to ensure window retains previous position on screen.</FONT>

	<FONT COLOR="#DD0000"># The IntVar() variable to wait on during a modal dialog.</FONT>
	self._wait = None

	# Attribute _active can be <FONT COLOR="#009900">'no'</FONT>, <FONT COLOR="#009900">'yes'</FONT>, or <FONT COLOR="#009900">'waiting'</FONT>.  The
	<FONT COLOR="#DD0000"># latter means that the window has been deiconified but has</FONT>
	<FONT COLOR="#DD0000"># not yet become visible.</FONT>
	self._active = <FONT COLOR="#009900">'no'</FONT>
	self._userDeleteFunc = self.destroy
	self._userModalDeleteFunc = self.deactivate

	<FONT COLOR="#DD0000"># Check keywords and initialise options.</FONT>
	self.initialiseoptions(MegaToplevel)

<STRONG>    def _settitle</STRONG>(self):
	title = self[<FONT COLOR="#009900">'title'</FONT>]
	if title is not None:
	    self.title(title)

<STRONG>    def userdeletefunc</STRONG>(self, func=None):
        if func:
	    self._userDeleteFunc = func
	else:
	    return self._userDeleteFunc

<STRONG>    def usermodaldeletefunc</STRONG>(self, func=None):
        if func:
	    self._userModalDeleteFunc = func
	else:
	    return self._userModalDeleteFunc

<STRONG>    def _userDeleteWindow</STRONG>(self):
	if self.active():
	    self._userModalDeleteFunc()
	else:
	    self._userDeleteFunc()

<STRONG>    def destroy</STRONG>(self):
	<FONT COLOR="#DD0000"># Allow this to be called more than once.</FONT>
	if _hullToMegaWidget.has_key(self._hull):
	    del _hullToMegaWidget[self._hull]
	    self.deactivate()
	    self._hull.destroy()

<STRONG>    def show</STRONG>(self):
	if self.state() == <FONT COLOR="#009900">'normal'</FONT>:
	    self.tkraise()
	else:
	    if self._firstShowing:
	        self._firstShowing = 0
	    else:
		geometry = self.geometry()
		index = string.find(geometry, <FONT COLOR="#009900">'+'</FONT>)
		if index >= 0:
		    self.geometry(geometry[index:])
	    self.deiconify()

<STRONG>    def activate</STRONG>(self, globalMode=0, master=None):
	if self.state() == <FONT COLOR="#009900">'normal'</FONT>:
	    self.withdraw()
	if self._active == <FONT COLOR="#009900">'yes'</FONT>:
	    raise ValueError, <FONT COLOR="#009900">'Window is already active'</FONT>
	if self._active == <FONT COLOR="#009900">'waiting'</FONT>:
	    return

	if master is not None:
	    self.transient(master)

	if len(MegaToplevel._grabStack) > 0:
	    widget = MegaToplevel._grabStack[-1][0]
	    widget.grab_release()
	MegaToplevel._grabStack.append(self, globalMode)

	showbusycursor()

	if self._wait is None:
	    self._wait = Tkinter.IntVar()
	self._wait.set(0)

	self._active = <FONT COLOR="#009900">'waiting'</FONT>

	<FONT COLOR="#DD0000"># Centre the window on the screen. (Actually halfway across and</FONT>
	<FONT COLOR="#DD0000"># one third down.)</FONT>
	self.update_idletasks()

	<FONT COLOR="#DD0000"># I'm not sure what the winfo_vroot[xy] stuff does, but tk_dialog</FONT>
	<FONT COLOR="#DD0000"># does it, so...</FONT>
	<FONT COLOR="#DD0000">#x = (self.winfo_screenwidth() - self.winfo_reqwidth()) / 2</FONT>
	<FONT COLOR="#DD0000">#y = (self.winfo_screenheight() - self.winfo_reqheight()) / 3</FONT>

	x = (self.winfo_screenwidth() - self.winfo_reqwidth()) / 2 \
		- self.winfo_vrootx()
	y = (self.winfo_screenheight() - self.winfo_reqheight()) / 3 \
		- self.winfo_vrooty()
	if x < 0:
	    x = 0
	if y < 0:
	    y = 0
	self.geometry(<FONT COLOR="#009900">'+%s+%s'</FONT> % (x, y))
	self.deiconify()

	self.wait_visibility()
	self._active = <FONT COLOR="#009900">'yes'</FONT>

	while 1:
	    try:
		if globalMode:
		    self.grab_set_global()
		else:
		    self.grab_set()
		break
	    except Tkinter.TclError:
		sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>
		<FONT COLOR="#DD0000"># Another application has grab.  Keep trying until</FONT>
		<FONT COLOR="#DD0000"># grab can succeed.</FONT>
		self.after(100)

	self.focus_set()
	command = self[<FONT COLOR="#009900">'activatecommand'</FONT>]
	if callable(command):
	    command()
	self.wait_variable(self._wait)

	return self._result


    <FONT COLOR="#DD0000"># TBD</FONT>
    <FONT COLOR="#DD0000"># This is how tk_dialog handles the focus and grab:</FONT>
    <FONT COLOR="#DD0000">#     # 7. Set a grab and claim the focus too.</FONT>
    <FONT COLOR="#DD0000"># </FONT>
    <FONT COLOR="#DD0000">#     set oldFocus [focus]</FONT>
    <FONT COLOR="#DD0000">#     set oldGrab [grab current $w]</FONT>
    <FONT COLOR="#DD0000">#     if {$oldGrab != ""} {</FONT>
    <FONT COLOR="#DD0000">#         set grabStatus [grab status $oldGrab]</FONT>
    <FONT COLOR="#DD0000">#     }</FONT>
    <FONT COLOR="#DD0000">#     grab $w</FONT>
    <FONT COLOR="#DD0000">#     if {$default &gt;= 0} {</FONT>
    <FONT COLOR="#DD0000">#         focus $w.button$default</FONT>
    <FONT COLOR="#DD0000">#     } else {</FONT>
    <FONT COLOR="#DD0000">#         focus $w</FONT>
    <FONT COLOR="#DD0000">#     }</FONT>
    <FONT COLOR="#DD0000"># </FONT>
    <FONT COLOR="#DD0000">#     # 8. Wait for the user to respond, then restore the focus and</FONT>
    <FONT COLOR="#DD0000">#     # return the index of the selected button.  Restore the focus</FONT>
    <FONT COLOR="#DD0000">#     # before deleting the window, since otherwise the window manager</FONT>
    <FONT COLOR="#DD0000">#     # may take the focus away so we can't redirect it.  Finally,</FONT>
    <FONT COLOR="#DD0000">#     # restore any grab that was in effect.</FONT>
    <FONT COLOR="#DD0000"># </FONT>
    <FONT COLOR="#DD0000">#     tkwait variable tkPriv(button)</FONT>
    <FONT COLOR="#DD0000">#     catch {focus $oldFocus}</FONT>
    <FONT COLOR="#DD0000">#     catch {</FONT>
    <FONT COLOR="#DD0000">#         # It's possible that the window has already been destroyed,</FONT>
    <FONT COLOR="#DD0000">#         # hence this "catch".  Delete the Destroy handler so that</FONT>
    <FONT COLOR="#DD0000">#         # tkPriv(button) doesn't get reset by it.</FONT>
    <FONT COLOR="#DD0000"># </FONT>
    <FONT COLOR="#DD0000">#         bind $w &lt;Destroy&gt; {}</FONT>
    <FONT COLOR="#DD0000">#         destroy $w</FONT>
    <FONT COLOR="#DD0000">#     }</FONT>
    <FONT COLOR="#DD0000">#     if {$oldGrab != ""} {</FONT>
    <FONT COLOR="#DD0000">#         if {$grabStatus == "global"} {</FONT>
    <FONT COLOR="#DD0000">#             grab -global $oldGrab</FONT>
    <FONT COLOR="#DD0000">#         } else {</FONT>
    <FONT COLOR="#DD0000">#             grab $oldGrab</FONT>
    <FONT COLOR="#DD0000">#         }</FONT>
    <FONT COLOR="#DD0000">#     }</FONT>

<STRONG>    def deactivate</STRONG>(self, result=None):
	if not self.active():
	    return
	self._active = <FONT COLOR="#009900">'no'</FONT>

	<FONT COLOR="#DD0000"># Deactivate any active windows above this on the stack.</FONT>
	while len(MegaToplevel._grabStack) > 0:
	  if MegaToplevel._grabStack[-1][0] == self:
	    break
	  else:
	    MegaToplevel._grabStack[-1][0].deactivate()


	<FONT COLOR="#DD0000"># Clean up this window.</FONT>
	hidebusycursor()
	self.withdraw()
	self.grab_release()

	<FONT COLOR="#DD0000"># Return the grab to the next active window in the stack, if any.</FONT>
	del MegaToplevel._grabStack[-1]
	if len(MegaToplevel._grabStack) > 0:
	    widget, globalMode = MegaToplevel._grabStack[-1]
	    if globalMode:
		widget.grab_set_global()
	    else:
		widget.grab_set()

	self._result = result
	self._wait.set(1)

<STRONG>    def active</STRONG>(self):
	return self._active != <FONT COLOR="#009900">'no'</FONT>

forwardmethods(MegaToplevel, Tkinter.Toplevel, <FONT COLOR="#009900">'_hull'</FONT>)

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<STRONG><FONT COLOR="#CC6600">class MegaWidget</FONT></STRONG>(MegaArchetype):
    <FONT COLOR="#DD0000"># Frame megawidget base class.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># The MegaWidget class inherits everything from the MegaArchetype</FONT>
    # class, and adds a Tkinter Frame called the <FONT COLOR="#009900">'hull'</FONT> component to
    <FONT COLOR="#DD0000"># represent the body of the megawidget.  The window class name for</FONT>
    <FONT COLOR="#DD0000"># the hull is set to the most-specific class name for the</FONT>
    <FONT COLOR="#DD0000"># megawidget.  The class acts as the base class of megawidgets</FONT>
    <FONT COLOR="#DD0000"># that are not contained in their own toplevel window.  Derived</FONT>
    <FONT COLOR="#DD0000"># classes specialize this widget by creating other widget</FONT>
    <FONT COLOR="#DD0000"># components as children of the hull.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># The MegaWidget class forwards all Tkinter Frame methods on to</FONT>
    <FONT COLOR="#DD0000"># the hull component.  For example, methods such as pack and</FONT>
    <FONT COLOR="#DD0000"># configure and all the winfo_ methods can be used with a</FONT>
    <FONT COLOR="#DD0000"># MegaWidget.</FONT>
    <FONT COLOR="#DD0000">#</FONT>
    <FONT COLOR="#DD0000"># Components</FONT>
    <FONT COLOR="#DD0000">#   hull          exterior of the megawidget (a Tkinter Frame)</FONT>

    <FONT COLOR="#DD0000"># Options</FONT>
    <FONT COLOR="#DD0000">#   background    forwarded to hull component</FONT>
    <FONT COLOR="#DD0000">#   cursor        forwarded to hull component</FONT>

<STRONG>    def __init__</STRONG>(self, parent = None, **kw):
	<FONT COLOR="#DD0000"># Define the options for this megawidget.</FONT>
	optiondefs = (
	    (<FONT COLOR="#009900">'hull_class'</FONT>,       self.__class__.__name__,  None),
	)
	self.defineoptions(kw, optiondefs)

	<FONT COLOR="#DD0000"># Initialise the base class (after defining the options).</FONT>
	MegaArchetype.__init__(self, parent, Tkinter.Frame)

<STRONG>    def destroy</STRONG>(self):
	del _hullToMegaWidget[self._hull]
	self._hull.destroy()

forwardmethods(MegaWidget, Tkinter.Frame, <FONT COLOR="#009900">'_hull'</FONT>)

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<FONT COLOR="#DD0000"># Public functions</FONT>
<FONT COLOR="#DD0000">#-----------------</FONT>

<STRONG>def tracetk</STRONG>(root, on, withStackTrace = 0, file=None):
    global _withStackTrace
    _withStackTrace = withStackTrace
    if on:
	if hasattr(root.tk, <FONT COLOR="#009900">'__class__'</FONT>):
	    <FONT COLOR="#DD0000"># Tracing already on</FONT>
	    return
	tk = _TraceTk(root.tk, file)
    else:
	if not hasattr(root.tk, <FONT COLOR="#009900">'__class__'</FONT>):
	    <FONT COLOR="#DD0000"># Tracing already off</FONT>
	    return
	tk = root.tk.getTclInterp()
    _setTkInterps(root, tk)

<STRONG>def showbusycursor</STRONG>():
    __addRootToToplevelBusyCount()
    doUpdate = 0
    for window in _toplevelBusyInfo.keys():
	if window.state() != <FONT COLOR="#009900">'withdrawn'</FONT>:
	    _toplevelBusyInfo[window][0] = _toplevelBusyInfo[window][0] + 1
	    if _haveblt(window):
		if _toplevelBusyInfo[window][0] == 1:
		    _busy_hold(window)

		    <FONT COLOR="#DD0000"># Make sure that no events for the busy window get</FONT>
		    <FONT COLOR="#DD0000"># through to Tkinter, otherwise it will crash in</FONT>
		    # _nametowidget with a <FONT COLOR="#009900">'KeyError: _Busy'</FONT> if there is
		    <FONT COLOR="#DD0000"># a binding on the toplevel window.</FONT>
		    if window._w == <FONT COLOR="#009900">'.'</FONT>:
			busyWindow = <FONT COLOR="#009900">'._Busy'</FONT>
		    else:
			busyWindow = window._w + <FONT COLOR="#009900">'._Busy'</FONT>
		    window.tk.call(<FONT COLOR="#009900">'bindtags'</FONT>, busyWindow, <FONT COLOR="#009900">'Pmw_Dummy_Tag'</FONT>)

		    <FONT COLOR="#DD0000"># Remember previous focus window and set focus to</FONT>
		    <FONT COLOR="#DD0000"># the busy window, which should ignore all events.</FONT>
		    lastFocus = window.tk.call(<FONT COLOR="#009900">'focus'</FONT>)
		    _toplevelBusyInfo[window][1] = \
			    window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, <FONT COLOR="#009900">'-lastfor'</FONT>, window._w)
		    window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, busyWindow)
		    if _toplevelBusyInfo[window][1] != lastFocus:
			window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, lastFocus)

		    doUpdate = 1
    if doUpdate:
	window.update_idletasks()

<STRONG>def hidebusycursor</STRONG>():
    __addRootToToplevelBusyCount()
    for window in _toplevelBusyInfo.keys():
	if _toplevelBusyInfo[window][0] > 0:
	    _toplevelBusyInfo[window][0] = _toplevelBusyInfo[window][0] - 1
	    if _haveblt(window):
		if _toplevelBusyInfo[window][0] == 0:
		    _busy_release(window)
		    lastFocus = window.tk.call(<FONT COLOR="#009900">'focus'</FONT>)
		    try:
			window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, _toplevelBusyInfo[window][1])
		    except Tkinter.TclError:
			<FONT COLOR="#DD0000"># Previous focus widget has been deleted. Set focus</FONT>
			<FONT COLOR="#DD0000"># to toplevel window instead (can't leave focus on</FONT>
			<FONT COLOR="#DD0000"># busy window).</FONT>
			sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>
			window.focus_set()
		    if window._w == <FONT COLOR="#009900">'.'</FONT>:
			busyWindow = <FONT COLOR="#009900">'._Busy'</FONT>
		    else:
			busyWindow = window._w + <FONT COLOR="#009900">'._Busy'</FONT>
		    if lastFocus != busyWindow:
			window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, lastFocus)

<STRONG>def clearbusycursor</STRONG>():
    __addRootToToplevelBusyCount()
    for window in _toplevelBusyInfo.keys():
	if _toplevelBusyInfo[window][0] > 0:
	    _toplevelBusyInfo[window][0] = 0
	    if _haveblt(window):
		_busy_release(window)
		try:
		    window.tk.call(<FONT COLOR="#009900">'focus'</FONT>, _toplevelBusyInfo[window][1])
		except Tkinter.TclError:
		    <FONT COLOR="#DD0000"># Previous focus widget has been deleted. Set focus</FONT>
		    <FONT COLOR="#DD0000"># to toplevel window instead (can't leave focus on</FONT>
		    <FONT COLOR="#DD0000"># busy window).</FONT>
		    sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>
		    window.focus_set()

<STRONG>def busycallback</STRONG>(command, updateFunction = None):
    if not callable(command):
	raise RuntimeError, \
	    <FONT COLOR="#009900">'cannot register non-command busy callback %s %s'</FONT> % \
	        (repr(command), type(command))
    wrapper = _BusyWrapper(command, updateFunction)
    return wrapper.callback

<STRONG>def reporterrorstofile</STRONG>(file = None):
    global _errorReportFile
    _errorReportFile = file

<STRONG>def displayerror</STRONG>(text):
    global _errorWindow

    if _errorWindow is None:
	<FONT COLOR="#DD0000"># The error window has not yet been created.</FONT>
	_errorWindow = _ErrorWindow()

    _errorWindow.showerror(text)

<STRONG>def initialise</STRONG>(root = None, size = None, fontScheme = None, useTkOptionDb = 0):

    <FONT COLOR="#DD0000"># Save flag specifying whether the Tk option database should be</FONT>
    <FONT COLOR="#DD0000"># queried when setting megawidget option default values.</FONT>
    global _useTkOptionDb
    _useTkOptionDb = useTkOptionDb

    <FONT COLOR="#DD0000"># If we haven't been given a root window, use the default or</FONT>
    <FONT COLOR="#DD0000"># create one.</FONT>
    if root is None:
	if Tkinter._default_root is None:
	    root = Tkinter.Tk()
	else:
	    root = Tkinter._default_root

    <FONT COLOR="#DD0000"># Trap Tkinter Toplevel constructors so that a list of Toplevels</FONT>
    <FONT COLOR="#DD0000"># can be maintained.</FONT>
    Tkinter.Toplevel.title = __TkinterToplevelTitle

    <FONT COLOR="#DD0000"># Trap Tkinter widget destruction so that megawidgets can be</FONT>
    <FONT COLOR="#DD0000"># destroyed when their hull widget is destoyed and the list of</FONT>
    <FONT COLOR="#DD0000"># Toplevels can be pruned.</FONT>
    Tkinter.Toplevel.destroy = __TkinterToplevelDestroy
    Tkinter.Frame.destroy = __TkinterFrameDestroy

    <FONT COLOR="#DD0000"># Modify Tkinter's CallWrapper class to improve the display of</FONT>
    <FONT COLOR="#DD0000"># errors which occur in callbacks.</FONT>
    Tkinter.CallWrapper = __TkinterCallWrapper

    <FONT COLOR="#DD0000"># Make sure we get to know when the window manager deletes the</FONT>
    <FONT COLOR="#DD0000"># root window.  Only do this if the protocol has not yet been set. </FONT>
    <FONT COLOR="#DD0000"># This is required if there is a modal dialog displayed and the</FONT>
    <FONT COLOR="#DD0000"># window manager deletes the root window.  Otherwise the</FONT>
    <FONT COLOR="#DD0000"># application will not exit, even though there are no windows.</FONT>
    if root.protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>) == <FONT COLOR="#009900">''</FONT>:
	root.protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>, root.destroy)

    <FONT COLOR="#DD0000"># Set the base font size for the application and set the</FONT>
    <FONT COLOR="#DD0000"># Tk option database font resources.</FONT>
    import PmwLogicalFont
    PmwLogicalFont.initialise(root, size, fontScheme)

    return root

<STRONG>def alignlabels</STRONG>(widgets, sticky = None):
    if len(widgets) == 0:
    	return

    widgets[0].update_idletasks()

    <FONT COLOR="#DD0000"># Determine the size of the maximum length label string.</FONT>
    maxLabelWidth = 0
    for iwid in widgets:
	labelWidth = iwid.grid_bbox(0, 1)[2]
	if labelWidth > maxLabelWidth:
	    maxLabelWidth = labelWidth

    <FONT COLOR="#DD0000"># Adjust the margins for the labels such that the child sites and</FONT>
    <FONT COLOR="#DD0000"># labels line up.</FONT>
    for iwid in widgets:
	if sticky is not None:
	    iwid.component(<FONT COLOR="#009900">'label'</FONT>).grid(sticky=sticky)
	iwid.grid_columnconfigure(0, minsize = maxLabelWidth)
<FONT COLOR="#DD0000">#=============================================================================</FONT>

<FONT COLOR="#DD0000"># Private routines</FONT>
<FONT COLOR="#DD0000">#-----------------</FONT>

<STRONG><FONT COLOR="#CC6600">class _TraceTk</FONT></STRONG>:
<STRONG>    def __init__</STRONG>(self, tclInterp, file):
        self.tclInterp = tclInterp
	if file is None:
	    self.file = sys.stderr
	else:
	    self.file = file
	self.recursionCounter = 0

<STRONG>    def getTclInterp</STRONG>(self):
        return self.tclInterp

<STRONG>    def call</STRONG>(self, *args, **kw):
	file = self.file
	file.write(<FONT COLOR="#009900">'='</FONT> * 60 + <FONT COLOR="#009900">'\n'</FONT>)
	file.write(<FONT COLOR="#009900">'tk call:'</FONT> + str(args) + <FONT COLOR="#009900">'\n'</FONT>)
	self.recursionCounter = self.recursionCounter + 1
	recursionStr = str(self.recursionCounter)
	if self.recursionCounter > 1:
	    file.write(<FONT COLOR="#009900">'recursion: '</FONT> + recursionStr + <FONT COLOR="#009900">'\n'</FONT>)
        result = apply(self.tclInterp.call, args, kw)
	if self.recursionCounter > 1:
	    file.write(<FONT COLOR="#009900">'end recursion: '</FONT> + recursionStr + <FONT COLOR="#009900">'\n'</FONT>)
	self.recursionCounter = self.recursionCounter - 1
	if result:
	    file.write(<FONT COLOR="#009900">'    result:'</FONT> + str(result) + <FONT COLOR="#009900">'\n'</FONT>)
	if _withStackTrace:
	    file.write(<FONT COLOR="#009900">'stack:\n'</FONT>)
	    traceback.print_stack()
	return result

<STRONG>    def __getattr__</STRONG>(self, key):
        return getattr(self.tclInterp, key)

<STRONG>def _setTkInterps</STRONG>(window, tk):
    window.tk = tk
    for child in window.children.values():
      _setTkInterps(child, tk)

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<FONT COLOR="#DD0000"># Functions to display a busy cursor.  Keep a list of all toplevels</FONT>
<FONT COLOR="#DD0000"># and display the busy cursor over them.  The list will contain the Tk</FONT>
<FONT COLOR="#DD0000"># root toplevel window as well as all other toplevel windows.</FONT>
<FONT COLOR="#DD0000"># Also keep a list of the widget which last had focus for each</FONT>
<FONT COLOR="#DD0000"># toplevel.</FONT>

_toplevelBusyInfo = {}

<FONT COLOR="#DD0000"># Pmw needs to know all toplevel windows, so that it can call blt busy</FONT>
<FONT COLOR="#DD0000"># on them.  This is a hack so we get notified when a Tk topevel is</FONT>
# created.  Ideally, the __init__ <FONT COLOR="#009900">'method'</FONT> should be overridden, but
# it is a <FONT COLOR="#009900">'read-only special attribute'</FONT>.  Luckily, title() is always
<FONT COLOR="#DD0000"># called from the Tkinter Toplevel constructor.</FONT>

<STRONG>def __TkinterToplevelTitle</STRONG>(self, *args):
    <FONT COLOR="#DD0000"># If this is being called from the constructor, include this</FONT>
    <FONT COLOR="#DD0000"># Toplevel in the list of toplevels and set the initial</FONT>
    <FONT COLOR="#DD0000"># WM_DELETE_WINDOW protocol to destroy() so that we get to know</FONT>
    <FONT COLOR="#DD0000"># about it.</FONT>
    if not _toplevelBusyInfo.has_key(self):
	_toplevelBusyInfo[self] = [0, None]
	self.protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>, self.destroy)

    return apply(Tkinter.Wm.title, (self,) + args)

_bltImported = 0
<STRONG>def _importBlt</STRONG>(window):
    global _bltImported, _bltOK, _busy_hold, _busy_release
    import PmwBlt
    _bltOK = PmwBlt.haveblt(window)
    _busy_hold = PmwBlt.busy_hold
    _busy_release = PmwBlt.busy_release
    _bltImported = 1

<STRONG>def _haveblt</STRONG>(window):
    if not _bltImported:
	_importBlt(window)
    return _bltOK

<STRONG>def __addRootToToplevelBusyCount</STRONG>():
    <FONT COLOR="#DD0000"># Since we do not know when Tkinter will be initialised, we have</FONT>
    <FONT COLOR="#DD0000"># to include the Tk root window in the list of toplevels at the</FONT>
    <FONT COLOR="#DD0000"># last minute.</FONT>

    root = Tkinter._default_root
    if not _toplevelBusyInfo.has_key(root):
	_toplevelBusyInfo[root] = [0, None]

<STRONG><FONT COLOR="#CC6600">class _BusyWrapper</FONT></STRONG>:
<STRONG>    def __init__</STRONG>(self, command, updateFunction):
	self._command = command
	self._updateFunction = updateFunction

<STRONG>    def callback</STRONG>(self, *args):
	showbusycursor()
	rtn = apply(self._command, args)

	<FONT COLOR="#DD0000"># Call update before hiding the busy windows to clear any</FONT>
	<FONT COLOR="#DD0000"># events that may have occurred over the busy windows.</FONT>
	if callable(self._updateFunction):
	    self._updateFunction()

	hidebusycursor()
	return rtn

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<FONT COLOR="#DD0000"># Modify the Tkinter destroy methods so that it notifies us when a Tk</FONT>
<FONT COLOR="#DD0000"># toplevel or frame is destroyed.</FONT>

# A map from the <FONT COLOR="#009900">'hull'</FONT> component of a megawidget to the megawidget. 
<FONT COLOR="#DD0000"># This is used to clean up a megawidget when its hull is destroyed.</FONT>
_hullToMegaWidget = {}

<STRONG>def __TkinterToplevelDestroy</STRONG>(tkWidget):
    if _hullToMegaWidget.has_key(tkWidget):
      mega = _hullToMegaWidget[tkWidget]
      try:
	  mega.destroy()
      except:
	  _reporterror(mega.destroy, ())
	  sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>
    else:
      del _toplevelBusyInfo[tkWidget]
      Tkinter.Widget.destroy(tkWidget)

<STRONG>def __TkinterFrameDestroy</STRONG>(tkWidget):
    if _hullToMegaWidget.has_key(tkWidget):
      mega = _hullToMegaWidget[tkWidget]
      try:
	  mega.destroy()
      except:
	  _reporterror(mega.destroy, ())
	  sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>
    else:
      Tkinter.Widget.destroy(tkWidget)

<STRONG>def hulltomegawidget</STRONG>(tkWidget):
    return _hullToMegaWidget[tkWidget]

<FONT COLOR="#DD0000">#=============================================================================</FONT>

<FONT COLOR="#DD0000"># Add code to Tkinter to improve the display of errors which occur in</FONT>
<FONT COLOR="#DD0000"># callbacks.</FONT>

<STRONG><FONT COLOR="#CC6600">class __TkinterCallWrapper</FONT></STRONG>:
<STRONG>    def __init__</STRONG>(self, func, subst, widget):
	self.func = func
	self.subst = subst
	self.widget = widget
<STRONG>    def __call__</STRONG>(self, *args):
	try:
	    if self.subst:
		args = apply(self.subst, args)
	    return apply(self.func, args)
	except SystemExit, msg:
	    raise SystemExit, msg
	except:
	    _reporterror(self.func, args)
	    sys.exc_traceback = None   <FONT COLOR="#DD0000"># Clean up object references</FONT>

_eventTypeToName = {
    2 : <FONT COLOR="#009900">'KeyPress'</FONT>,
    3 : <FONT COLOR="#009900">'KeyRelease'</FONT>,
    4 : <FONT COLOR="#009900">'ButtonPress'</FONT>,
    5 : <FONT COLOR="#009900">'ButtonRelease'</FONT>,
    6 : <FONT COLOR="#009900">'MotionNotify'</FONT>,
    7 : <FONT COLOR="#009900">'EnterNotify'</FONT>,
    8 : <FONT COLOR="#009900">'LeaveNotify'</FONT>,
    9 : <FONT COLOR="#009900">'FocusIn'</FONT>,
    10 : <FONT COLOR="#009900">'FocusOut'</FONT>,
    11 : <FONT COLOR="#009900">'KeymapNotify'</FONT>,
    12 : <FONT COLOR="#009900">'Expose'</FONT>,
    13 : <FONT COLOR="#009900">'GraphicsExpose'</FONT>,
    14 : <FONT COLOR="#009900">'NoExpose'</FONT>,
    15 : <FONT COLOR="#009900">'VisibilityNotify'</FONT>,
    16 : <FONT COLOR="#009900">'CreateNotify'</FONT>,
    17 : <FONT COLOR="#009900">'DestroyNotify'</FONT>,
    18 : <FONT COLOR="#009900">'UnmapNotify'</FONT>,
    19 : <FONT COLOR="#009900">'MapNotify'</FONT>,
    20 : <FONT COLOR="#009900">'MapRequest'</FONT>,
    21 : <FONT COLOR="#009900">'ReparentNotify'</FONT>,
    22 : <FONT COLOR="#009900">'ConfigureNotify'</FONT>,
    23 : <FONT COLOR="#009900">'ConfigureRequest'</FONT>,
    24 : <FONT COLOR="#009900">'GravityNotify'</FONT>,
    25 : <FONT COLOR="#009900">'ResizeRequest'</FONT>,
    26 : <FONT COLOR="#009900">'CirculateNotify'</FONT>,
    27 : <FONT COLOR="#009900">'CirculateRequest'</FONT>,
    28 : <FONT COLOR="#009900">'PropertyNotify'</FONT>,
    29 : <FONT COLOR="#009900">'SelectionClear'</FONT>,
    30 : <FONT COLOR="#009900">'SelectionRequest'</FONT>,
    31 : <FONT COLOR="#009900">'SelectionNotify'</FONT>,
    32 : <FONT COLOR="#009900">'ColormapNotify'</FONT>,
    33 : <FONT COLOR="#009900">'ClientMessage'</FONT>,
    34 : <FONT COLOR="#009900">'MappingNotify'</FONT>,
}

<STRONG>def _reporterror</STRONG>(func, args):
    <FONT COLOR="#DD0000"># Save current exception values, just in case a new one occurs.</FONT>
    exc_type, exc_value, exc_traceback = \
      sys.exc_type, sys.exc_value, sys.exc_traceback

    <FONT COLOR="#DD0000"># Give basic information about the callback exception.</FONT>
    if type(exc_type) == types.ClassType:
	<FONT COLOR="#DD0000"># Handle python 1.5 class exceptions.</FONT>
	exc_type = exc_type.__name__
    msg = exc_type + <FONT COLOR="#009900">' Exception in Tk callback\n'</FONT>
    msg = msg + <FONT COLOR="#009900">'  Function: %s (type: %s)\n'</FONT> % (repr(func), type(func))
    msg = msg + <FONT COLOR="#009900">'  Args: %s\n'</FONT> % str(args)

    if type(args) == types.TupleType and len(args) > 0 and \
	    hasattr(args[0], <FONT COLOR="#009900">'type'</FONT>):
        eventArg = 1
    else:
        eventArg = 0

    <FONT COLOR="#DD0000"># If the argument to the callback is an event, add the event type.</FONT>
    if eventArg:
	eventNum = string.atoi(args[0].type)
	msg = msg + <FONT COLOR="#009900">'  Event type: %s\n'</FONT> % _eventTypeToName[eventNum]

    <FONT COLOR="#DD0000"># Add the traceback.</FONT>
    msg = msg + <FONT COLOR="#009900">'Traceback (innermost last):\n'</FONT>
    for tr in traceback.extract_tb(exc_traceback):
	msg = msg + <FONT COLOR="#009900">'  File "%s", line %s, in %s\n'</FONT> % (tr[0], tr[1], tr[2])
	msg = msg + <FONT COLOR="#009900">'    %s\n'</FONT> % tr[3]
    msg = msg + <FONT COLOR="#009900">'%s: %s\n'</FONT> % (exc_type, exc_value)

    <FONT COLOR="#DD0000"># If the argument to the callback is an event, add the event contents.</FONT>
    if eventArg:
	msg = msg + <FONT COLOR="#009900">'\n================================================\n'</FONT>
	msg = msg + <FONT COLOR="#009900">'  Event contents:\n'</FONT>
	keys = args[0].__dict__.keys()
	keys.sort()
	for key in keys:
	    msg = msg + <FONT COLOR="#009900">'    %s: %s\n'</FONT> % (key, args[0].__dict__[key])

    clearbusycursor()
    if _errorReportFile is not None:
	_errorReportFile.write(msg + <FONT COLOR="#009900">'\n'</FONT>)
    else:
	try:
	    displayerror(msg)
	except:
	    exc_type, exc_value, exc_traceback = \
	      sys.exc_type, sys.exc_value, sys.exc_traceback
	    print <FONT COLOR="#009900">'Failed to display error window.'</FONT>
	    print <FONT COLOR="#009900">'Original error was:'</FONT>
	    print msg

_errorReportFile = None
_errorWindow = None

<STRONG><FONT COLOR="#CC6600">class _ErrorWindow</FONT></STRONG>:
<STRONG>    def __init__</STRONG>(self):

	self._errorQueue = []
	self._errorCount = 0
	self._open = 0

	<FONT COLOR="#DD0000"># Create the toplevel window</FONT>
	self._top = Tkinter.Toplevel()
	self._top.protocol(<FONT COLOR="#009900">'WM_DELETE_WINDOW'</FONT>, self._hide)
	self._top.title(<FONT COLOR="#009900">'Error in background function'</FONT>)
	self._top.iconname(<FONT COLOR="#009900">'Background error'</FONT>)

	<FONT COLOR="#DD0000"># Create the text widget and scrollbar in a frame</FONT>
	upperframe = Tkinter.Frame(self._top)

	scrollbar = Tkinter.Scrollbar(upperframe, orient=<FONT COLOR="#009900">'vertical'</FONT>)
	scrollbar.pack(side = <FONT COLOR="#009900">'right'</FONT>, fill = <FONT COLOR="#009900">'y'</FONT>)

	self._text = Tkinter.Text(upperframe, yscrollcommand=scrollbar.set)
	self._text.pack(fill = <FONT COLOR="#009900">'both'</FONT>, expand = 1)
	scrollbar.configure(command=self._text.yview)

	<FONT COLOR="#DD0000"># Create the buttons and label in a frame</FONT>
	lowerframe = Tkinter.Frame(self._top)

	ignore = Tkinter.Button(lowerframe,
	        text = <FONT COLOR="#009900">'Ignore remaining errors'</FONT>, command = self._hide)
	ignore.pack(side=<FONT COLOR="#009900">'left'</FONT>)

	self._nextError = Tkinter.Button(lowerframe,
	        text = <FONT COLOR="#009900">'Show next error'</FONT>, command = self._next)
	self._nextError.pack(side=<FONT COLOR="#009900">'left'</FONT>)

	self._label = Tkinter.Label(lowerframe, relief=<FONT COLOR="#009900">'ridge'</FONT>)
	self._label.pack(side=<FONT COLOR="#009900">'left'</FONT>, fill=<FONT COLOR="#009900">'x'</FONT>, expand=1)

	<FONT COLOR="#DD0000"># Pack the lower frame first so that it does not disappear</FONT>
	<FONT COLOR="#DD0000"># when the window is resized.</FONT>
	lowerframe.pack(side = <FONT COLOR="#009900">'bottom'</FONT>, fill = <FONT COLOR="#009900">'x'</FONT>)
	upperframe.pack(side = <FONT COLOR="#009900">'bottom'</FONT>, fill = <FONT COLOR="#009900">'both'</FONT>, expand = 1)

<STRONG>    def showerror</STRONG>(self, text):
	if self._open:
	    self._errorQueue.append(text)
	else:
	    self._display(text)
	    self._open = 1

	<FONT COLOR="#DD0000"># Display the error window in the same place it was before.</FONT>
	if self._top.state() == <FONT COLOR="#009900">'normal'</FONT>:
	    <FONT COLOR="#DD0000"># If update_idletasks is not called here, the window may</FONT>
	    <FONT COLOR="#DD0000"># be placed partially off the screen.</FONT>
	    self._top.update_idletasks()
	    self._top.tkraise()
	else:
	    geometry = self._top.geometry()
	    index = string.find(geometry, <FONT COLOR="#009900">'+'</FONT>)
	    if index >= 0:
		self._top.geometry(geometry[index:])
	    self._top.deiconify()

	self._updateButtons()

	<FONT COLOR="#DD0000"># Release any grab, so that buttons in the error window work.</FONT>
	if len(MegaToplevel._grabStack) > 0:
	    widget = MegaToplevel._grabStack[-1][0]
	    widget.grab_release()

<STRONG>    def _hide</STRONG>(self):
	self._errorCount = self._errorCount + len(self._errorQueue)
	self._errorQueue = []
	self._top.withdraw()
	self._open = 0

<STRONG>    def _next</STRONG>(self):
	<FONT COLOR="#DD0000"># Display the next error in the queue. </FONT>

	text = self._errorQueue[0]
	del self._errorQueue[0]

	self._display(text)
	self._updateButtons()

<STRONG>    def _display</STRONG>(self, text):
	self._errorCount = self._errorCount + 1
	text = <FONT COLOR="#009900">'Error: %d\n%s'</FONT> % (self._errorCount, text)
	self._text.delete(<FONT COLOR="#009900">'1.0'</FONT>, <FONT COLOR="#009900">'end'</FONT>)
	self._text.insert(<FONT COLOR="#009900">'end'</FONT>, text)

<STRONG>    def _updateButtons</STRONG>(self):
	numQueued = len(self._errorQueue)
	if numQueued > 0:
	    self._label.configure(text=<FONT COLOR="#009900">'%d more errors'</FONT> % numQueued)
	    self._nextError.configure(state=<FONT COLOR="#009900">'normal'</FONT>)
	else:
	    self._label.configure(text=<FONT COLOR="#009900">'No more errors'</FONT>)
	    self._nextError.configure(state=<FONT COLOR="#009900">'disabled'</FONT>)

</PRE>

 </BODY> </HTML>