1 """
2 @version: 0.95(2010-06-12)
3 @note:
4 EasyGui provides an easy-to-use interface for simple GUI interaction
5 with a user. It does not require the programmer to know anything about
6 tkinter, frames, widgets, callbacks or lambda. All GUI interactions are
7 invoked by simple function calls that return results.
8
9
10 @note:
11 WARNING about using EasyGui with IDLE
12
13 You may encounter problems using IDLE to run programs that use EasyGui. Try it
14 and find out. EasyGui is a collection of Tkinter routines that run their own
15 event loops. IDLE is also a Tkinter application, with its own event loop. The
16 two may conflict, with unpredictable results. If you find that you have
17 problems, try running your EasyGui program outside of IDLE.
18
19 Note that EasyGui requires Tk release 8.0 or greater.
20 """
21 egversion = __doc__.split()[1]
22
23 __all__ = ['ynbox'
24 , 'ccbox'
25 , 'boolbox'
26 , 'indexbox'
27 , 'msgbox'
28 , 'buttonbox'
29 , 'integerbox'
30 , 'multenterbox'
31 , 'enterbox'
32 , 'exceptionbox'
33 , 'choicebox'
34 , 'codebox'
35 , 'textbox'
36 , 'diropenbox'
37 , 'fileopenbox'
38 , 'filesavebox'
39 , 'passwordbox'
40 , 'multpasswordbox'
41 , 'multchoicebox'
42 , 'abouteasygui'
43 , 'egversion'
44 , 'egdemo'
45 , 'EgStore'
46 ]
47
48 import sys, os, string, types, pickle,traceback
49
50
51
52
53 """
54 From the python documentation:
55
56 sys.hexversion contains the version number encoded as a single integer. This is
57 guaranteed to increase with each version, including proper support for non-
58 production releases. For example, to test that the Python interpreter is at
59 least version 1.5.2, use:
60
61 if sys.hexversion >= 0x010502F0:
62 # use some advanced feature
63 ...
64 else:
65 # use an alternative implementation or warn the user
66 ...
67 """
68 if sys.hexversion >= 0x020600F0: runningPython26 = True
69 else: runningPython26 = False
70
71 if sys.hexversion >= 0x030000F0: runningPython3 = True
72 else: runningPython3 = False
73
74 if runningPython3:
75 from tkinter import *
76 import tkinter.filedialog as tk_FileDialog
77 from io import StringIO
78 else:
79 from Tkinter import *
80 import tkFileDialog as tk_FileDialog
81 from StringIO import StringIO
82
84 args = [str(arg) for arg in args]
85 args = " ".join(args)
86 sys.stdout.write(args)
87
91
92 say = writeln
93
94
95 if TkVersion < 8.0 :
96 stars = "*"*75
97 writeln("""\n\n\n""" + stars + """
98 You are running Tk version: """ + str(TkVersion) + """
99 You must be using Tk version 8.0 or greater to use EasyGui.
100 Terminating.
101 """ + stars + """\n\n\n""")
102 sys.exit(0)
103
106
107 rootWindowPosition = "+300+200"
108
109 PROPORTIONAL_FONT_FAMILY = ("MS", "Sans", "Serif")
110 MONOSPACE_FONT_FAMILY = ("Courier")
111
112 PROPORTIONAL_FONT_SIZE = 10
113 MONOSPACE_FONT_SIZE = 9
114 TEXT_ENTRY_FONT_SIZE = 12
115
116
117 STANDARD_SELECTION_EVENTS = ["Return", "Button-1", "space"]
118
119
120 __choiceboxMultipleSelect = None
121 __widgetTexts = None
122 __replyButtonText = None
123 __choiceboxResults = None
124 __firstWidget = None
125 __enterboxText = None
126 __enterboxDefaultText=""
127 __multenterboxText = ""
128 choiceboxChoices = None
129 choiceboxWidget = None
130 entryWidget = None
131 boxRoot = None
132 ImageErrorMsg = (
133 "\n\n---------------------------------------------\n"
134 "Error: %s\n%s")
135
136
137
138
139
140
141
142 -def ynbox(msg="Shall I continue?"
143 , title=" "
144 , choices=("Yes", "No")
145 , image=None
146 ):
147 """
148 Display a msgbox with choices of Yes and No.
149
150 The default is "Yes".
151
152 The returned value is calculated this way::
153 if the first choice ("Yes") is chosen, or if the dialog is cancelled:
154 return 1
155 else:
156 return 0
157
158 If invoked without a msg argument, displays a generic request for a confirmation
159 that the user wishes to continue. So it can be used this way::
160 if ynbox(): pass # continue
161 else: sys.exit(0) # exit the program
162
163 @arg msg: the msg to be displayed.
164 @arg title: the window title
165 @arg choices: a list or tuple of the choices to be displayed
166 """
167 return boolbox(msg, title, choices, image=image)
168
169
170
171
172
173 -def ccbox(msg="Shall I continue?"
174 , title=" "
175 , choices=("Continue", "Cancel")
176 , image=None
177 ):
178 """
179 Display a msgbox with choices of Continue and Cancel.
180
181 The default is "Continue".
182
183 The returned value is calculated this way::
184 if the first choice ("Continue") is chosen, or if the dialog is cancelled:
185 return 1
186 else:
187 return 0
188
189 If invoked without a msg argument, displays a generic request for a confirmation
190 that the user wishes to continue. So it can be used this way::
191
192 if ccbox():
193 pass # continue
194 else:
195 sys.exit(0) # exit the program
196
197 @arg msg: the msg to be displayed.
198 @arg title: the window title
199 @arg choices: a list or tuple of the choices to be displayed
200 """
201 return boolbox(msg, title, choices, image=image)
202
203
204
205
206
207 -def boolbox(msg="Shall I continue?"
208 , title=" "
209 , choices=("Yes","No")
210 , image=None
211 ):
212 """
213 Display a boolean msgbox.
214
215 The default is the first choice.
216
217 The returned value is calculated this way::
218 if the first choice is chosen, or if the dialog is cancelled:
219 returns 1
220 else:
221 returns 0
222 """
223 reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
224 if reply == choices[0]: return 1
225 else: return 0
226
227
228
229
230
231 -def indexbox(msg="Shall I continue?"
232 , title=" "
233 , choices=("Yes","No")
234 , image=None
235 ):
236 """
237 Display a buttonbox with the specified choices.
238 Return the index of the choice selected.
239 """
240 reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
241 index = -1
242 for choice in choices:
243 index = index + 1
244 if reply == choice: return index
245 raise AssertionError(
246 "There is a program logic error in the EasyGui code for indexbox.")
247
248
249
250
251
252 -def msgbox(msg="(Your message goes here)", title=" ", ok_button="OK",image=None,root=None):
253 """
254 Display a messagebox
255 """
256 if type(ok_button) != type("OK"):
257 raise AssertionError("The 'ok_button' argument to msgbox must be a string.")
258
259 return buttonbox(msg=msg, title=title, choices=[ok_button], image=image,root=root)
260
261
262
263
264
365
366
367
368
369
370 -def integerbox(msg=""
371 , title=" "
372 , default=""
373 , lowerbound=0
374 , upperbound=99
375 , image = None
376 , root = None
377 , **invalidKeywordArguments
378 ):
379 """
380 Show a box in which a user can enter an integer.
381
382 In addition to arguments for msg and title, this function accepts
383 integer arguments for "default", "lowerbound", and "upperbound".
384
385 The default argument may be None.
386
387 When the user enters some text, the text is checked to verify that it
388 can be converted to an integer between the lowerbound and upperbound.
389
390 If it can be, the integer (not the text) is returned.
391
392 If it cannot, then an error msg is displayed, and the integerbox is
393 redisplayed.
394
395 If the user cancels the operation, None is returned.
396
397 NOTE that the "argLowerBound" and "argUpperBound" arguments are no longer
398 supported. They have been replaced by "upperbound" and "lowerbound".
399 """
400 if "argLowerBound" in invalidKeywordArguments:
401 raise AssertionError(
402 "\nintegerbox no longer supports the 'argLowerBound' argument.\n"
403 + "Use 'lowerbound' instead.\n\n")
404 if "argUpperBound" in invalidKeywordArguments:
405 raise AssertionError(
406 "\nintegerbox no longer supports the 'argUpperBound' argument.\n"
407 + "Use 'upperbound' instead.\n\n")
408
409 if default != "":
410 if type(default) != type(1):
411 raise AssertionError(
412 "integerbox received a non-integer value for "
413 + "default of " + dq(str(default)) , "Error")
414
415 if type(lowerbound) != type(1):
416 raise AssertionError(
417 "integerbox received a non-integer value for "
418 + "lowerbound of " + dq(str(lowerbound)) , "Error")
419
420 if type(upperbound) != type(1):
421 raise AssertionError(
422 "integerbox received a non-integer value for "
423 + "upperbound of " + dq(str(upperbound)) , "Error")
424
425 if msg == "":
426 msg = ("Enter an integer between " + str(lowerbound)
427 + " and "
428 + str(upperbound)
429 )
430
431 while 1:
432 reply = enterbox(msg, title, str(default), image=image, root=root)
433 if reply == None: return None
434
435 try:
436 reply = int(reply)
437 except:
438 msgbox ("The value that you entered:\n\t%s\nis not an integer." % dq(str(reply))
439 , "Error")
440 continue
441
442 if reply < lowerbound:
443 msgbox ("The value that you entered is less than the lower bound of "
444 + str(lowerbound) + ".", "Error")
445 continue
446
447 if reply > upperbound:
448 msgbox ("The value that you entered is greater than the upper bound of "
449 + str(upperbound) + ".", "Error")
450 continue
451
452
453
454 return reply
455
456
457
458
459 -def multenterbox(msg="Fill in values for the fields."
460 , title=" "
461 , fields=()
462 , values=()
463 ):
464 r"""
465 Show screen with multiple data entry fields.
466
467 If there are fewer values than names, the list of values is padded with
468 empty strings until the number of values is the same as the number of names.
469
470 If there are more values than names, the list of values
471 is truncated so that there are as many values as names.
472
473 Returns a list of the values of the fields,
474 or None if the user cancels the operation.
475
476 Here is some example code, that shows how values returned from
477 multenterbox can be checked for validity before they are accepted::
478 ----------------------------------------------------------------------
479 msg = "Enter your personal information"
480 title = "Credit Card Application"
481 fieldNames = ["Name","Street Address","City","State","ZipCode"]
482 fieldValues = [] # we start with blanks for the values
483 fieldValues = multenterbox(msg,title, fieldNames)
484
485 # make sure that none of the fields was left blank
486 while 1:
487 if fieldValues == None: break
488 errmsg = ""
489 for i in range(len(fieldNames)):
490 if fieldValues[i].strip() == "":
491 errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
492 if errmsg == "":
493 break # no problems found
494 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
495
496 writeln("Reply was: %s" % str(fieldValues))
497 ----------------------------------------------------------------------
498
499 @arg msg: the msg to be displayed.
500 @arg title: the window title
501 @arg fields: a list of fieldnames.
502 @arg values: a list of field values
503 """
504 return __multfillablebox(msg,title,fields,values,None)
505
506
507
508
509
510 -def multpasswordbox(msg="Fill in values for the fields."
511 , title=" "
512 , fields=tuple()
513 ,values=tuple()
514 ):
515 r"""
516 Same interface as multenterbox. But in multpassword box,
517 the last of the fields is assumed to be a password, and
518 is masked with asterisks.
519
520 Example
521 =======
522
523 Here is some example code, that shows how values returned from
524 multpasswordbox can be checked for validity before they are accepted::
525 msg = "Enter logon information"
526 title = "Demo of multpasswordbox"
527 fieldNames = ["Server ID", "User ID", "Password"]
528 fieldValues = [] # we start with blanks for the values
529 fieldValues = multpasswordbox(msg,title, fieldNames)
530
531 # make sure that none of the fields was left blank
532 while 1:
533 if fieldValues == None: break
534 errmsg = ""
535 for i in range(len(fieldNames)):
536 if fieldValues[i].strip() == "":
537 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
538 if errmsg == "": break # no problems found
539 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
540
541 writeln("Reply was: %s" % str(fieldValues))
542 """
543 return __multfillablebox(msg,title,fields,values,"*")
544
551
553 boxRoot.event_generate("<Tab>")
554
556 boxRoot.event_generate("<Shift-Tab>")
557
558
559
560
561 -def __multfillablebox(msg="Fill in values for the fields."
562 , title=" "
563 , fields=()
564 , values=()
565 , mask = None
566 ):
567 global boxRoot, __multenterboxText, __multenterboxDefaultText, cancelButton, entryWidget, okButton
568
569 choices = ["OK", "Cancel"]
570 if len(fields) == 0: return None
571
572 fields = list(fields[:])
573 values = list(values[:])
574
575 if len(values) == len(fields): pass
576 elif len(values) > len(fields):
577 fields = fields[0:len(values)]
578 else:
579 while len(values) < len(fields):
580 values.append("")
581
582 boxRoot = Tk()
583
584 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
585 boxRoot.title(title)
586 boxRoot.iconname('Dialog')
587 boxRoot.geometry(rootWindowPosition)
588 boxRoot.bind("<Escape>", __multenterboxCancel)
589
590
591 messageFrame = Frame(master=boxRoot)
592 messageFrame.pack(side=TOP, fill=BOTH)
593
594
595 messageWidget = Message(messageFrame, width="4.5i", text=msg)
596 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
597 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
598
599 global entryWidgets
600 entryWidgets = []
601
602 lastWidgetIndex = len(fields) - 1
603
604 for widgetIndex in range(len(fields)):
605 argFieldName = fields[widgetIndex]
606 argFieldValue = values[widgetIndex]
607 entryFrame = Frame(master=boxRoot)
608 entryFrame.pack(side=TOP, fill=BOTH)
609
610
611 labelWidget = Label(entryFrame, text=argFieldName)
612 labelWidget.pack(side=LEFT)
613
614 entryWidget = Entry(entryFrame, width=40,highlightthickness=2)
615 entryWidgets.append(entryWidget)
616 entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
617 entryWidget.pack(side=RIGHT, padx="3m")
618
619 bindArrows(entryWidget)
620
621 entryWidget.bind("<Return>", __multenterboxGetText)
622 entryWidget.bind("<Escape>", __multenterboxCancel)
623
624
625
626 if widgetIndex == lastWidgetIndex:
627 if mask:
628 entryWidgets[widgetIndex].configure(show=mask)
629
630
631 entryWidgets[widgetIndex].insert(0,argFieldValue)
632 widgetIndex += 1
633
634
635 buttonsFrame = Frame(master=boxRoot)
636 buttonsFrame.pack(side=BOTTOM, fill=BOTH)
637
638 okButton = Button(buttonsFrame, takefocus=1, text="OK")
639 bindArrows(okButton)
640 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
641
642
643 commandButton = okButton
644 handler = __multenterboxGetText
645 for selectionEvent in STANDARD_SELECTION_EVENTS:
646 commandButton.bind("<%s>" % selectionEvent, handler)
647
648
649
650 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
651 bindArrows(cancelButton)
652 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
653
654
655 commandButton = cancelButton
656 handler = __multenterboxCancel
657 for selectionEvent in STANDARD_SELECTION_EVENTS:
658 commandButton.bind("<%s>" % selectionEvent, handler)
659
660
661
662 entryWidgets[0].focus_force()
663 boxRoot.mainloop()
664
665
666 boxRoot.destroy()
667 return __multenterboxText
668
669
670
671
672
674 global __multenterboxText
675
676 __multenterboxText = []
677 for entryWidget in entryWidgets:
678 __multenterboxText.append(entryWidget.get())
679 boxRoot.quit()
680
681
686
687
688
689
690
691 -def enterbox(msg="Enter something."
692 , title=" "
693 , default=""
694 , strip=True
695 , image=None
696 , root=None
697 ):
698 """
699 Show a box in which a user can enter some text.
700
701 You may optionally specify some default text, which will appear in the
702 enterbox when it is displayed.
703
704 Returns the text that the user entered, or None if he cancels the operation.
705
706 By default, enterbox strips its result (i.e. removes leading and trailing
707 whitespace). (If you want it not to strip, use keyword argument: strip=False.)
708 This makes it easier to test the results of the call::
709
710 reply = enterbox(....)
711 if reply:
712 ...
713 else:
714 ...
715 """
716 result = __fillablebox(msg, title, default=default, mask=None,image=image,root=root)
717 if result and strip:
718 result = result.strip()
719 return result
720
721
722 -def passwordbox(msg="Enter your password."
723 , title=" "
724 , default=""
725 , image=None
726 , root=None
727 ):
728 """
729 Show a box in which a user can enter a password.
730 The text is masked with asterisks, so the password is not displayed.
731 Returns the text that the user entered, or None if he cancels the operation.
732 """
733 return __fillablebox(msg, title, default, mask="*",image=image,root=root)
734
735
736 -def __fillablebox(msg
737 , title=""
738 , default=""
739 , mask=None
740 , image=None
741 , root=None
742 ):
743 """
744 Show a box in which a user can enter some text.
745 You may optionally specify some default text, which will appear in the
746 enterbox when it is displayed.
747 Returns the text that the user entered, or None if he cancels the operation.
748 """
749
750 global boxRoot, __enterboxText, __enterboxDefaultText
751 global cancelButton, entryWidget, okButton
752
753 if title == None: title == ""
754 if default == None: default = ""
755 __enterboxDefaultText = default
756 __enterboxText = __enterboxDefaultText
757
758 if root:
759 root.withdraw()
760 boxRoot = Toplevel(master=root)
761 boxRoot.withdraw()
762 else:
763 boxRoot = Tk()
764 boxRoot.withdraw()
765
766 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
767 boxRoot.title(title)
768 boxRoot.iconname('Dialog')
769 boxRoot.geometry(rootWindowPosition)
770 boxRoot.bind("<Escape>", __enterboxCancel)
771
772 if image:
773 image = os.path.normpath(image)
774 junk,ext = os.path.splitext(image)
775 if ext.lower() == ".gif":
776 if os.path.exists(image):
777 pass
778 else:
779 msg += ImageErrorMsg % (image, "Image file not found.")
780 image = None
781 else:
782 msg += ImageErrorMsg % (image, "Image file is not a .gif file.")
783 image = None
784
785 messageFrame = Frame(master=boxRoot)
786 messageFrame.pack(side=TOP, fill=BOTH)
787
788
789 if image:
790 imageFrame = Frame(master=boxRoot)
791 imageFrame.pack(side=TOP, fill=BOTH)
792 image = PhotoImage(file=image)
793 label = Label(imageFrame,image=image)
794 label.image = image
795 label.pack(side=TOP, expand=YES, fill=X, padx='1m', pady='1m')
796
797
798 entryFrame = Frame(master=boxRoot)
799 entryFrame.pack(side=TOP, fill=BOTH)
800
801
802 buttonsFrame = Frame(master=boxRoot)
803 buttonsFrame.pack(side=TOP, fill=BOTH)
804
805
806 messageWidget = Message(messageFrame, width="4.5i", text=msg)
807 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
808 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
809
810
811 entryWidget = Entry(entryFrame, width=40)
812 bindArrows(entryWidget)
813 entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
814 if mask:
815 entryWidget.configure(show=mask)
816 entryWidget.pack(side=LEFT, padx="3m")
817 entryWidget.bind("<Return>", __enterboxGetText)
818 entryWidget.bind("<Escape>", __enterboxCancel)
819
820 entryWidget.insert(0,__enterboxDefaultText)
821
822
823 okButton = Button(buttonsFrame, takefocus=1, text="OK")
824 bindArrows(okButton)
825 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
826
827
828 commandButton = okButton
829 handler = __enterboxGetText
830 for selectionEvent in STANDARD_SELECTION_EVENTS:
831 commandButton.bind("<%s>" % selectionEvent, handler)
832
833
834
835 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
836 bindArrows(cancelButton)
837 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
838
839
840 commandButton = cancelButton
841 handler = __enterboxCancel
842 for selectionEvent in STANDARD_SELECTION_EVENTS:
843 commandButton.bind("<%s>" % selectionEvent, handler)
844
845
846 entryWidget.focus_force()
847 boxRoot.deiconify()
848 boxRoot.mainloop()
849
850
851 if root: root.deiconify()
852 boxRoot.destroy()
853 return __enterboxText
854
855
860
861
866
867
872
874 """ don't allow WindowManager close
875 """
876 x = Tk()
877 x.withdraw()
878 x.bell()
879 x.destroy()
880
881
882
883
884
885
886 -def multchoicebox(msg="Pick as many items as you like."
887 , title=" "
888 , choices=()
889 , **kwargs
890 ):
891 """
892 Present the user with a list of choices.
893 allow him to select multiple items and return them in a list.
894 if the user doesn't choose anything from the list, return the empty list.
895 return None if he cancelled selection.
896
897 @arg msg: the msg to be displayed.
898 @arg title: the window title
899 @arg choices: a list or tuple of the choices to be displayed
900 """
901 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
902
903 global __choiceboxMultipleSelect
904 __choiceboxMultipleSelect = 1
905 return __choicebox(msg, title, choices)
906
907
908
909
910
911 -def choicebox(msg="Pick something."
912 , title=" "
913 , choices=()
914 ):
915 """
916 Present the user with a list of choices.
917 return the choice that he selects.
918 return None if he cancels the selection selection.
919
920 @arg msg: the msg to be displayed.
921 @arg title: the window title
922 @arg choices: a list or tuple of the choices to be displayed
923 """
924 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
925
926 global __choiceboxMultipleSelect
927 __choiceboxMultipleSelect = 0
928 return __choicebox(msg,title,choices)
929
930
931
932
933
938 """
939 internal routine to support choicebox() and multchoicebox()
940 """
941 global boxRoot, __choiceboxResults, choiceboxWidget, defaultText
942 global choiceboxWidget, choiceboxChoices
943
944
945
946
947
948
949 choices = list(choices[:])
950 if len(choices) == 0:
951 choices = ["Program logic error - no choices were specified."]
952 defaultButtons = ["OK", "Cancel"]
953
954
955 for index in range(len(choices)):
956 choices[index] = str(choices[index])
957
958 lines_to_show = min(len(choices), 20)
959 lines_to_show = 20
960
961 if title == None: title = ""
962
963
964
965 __choiceboxResults = None
966
967 boxRoot = Tk()
968 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
969 screen_width = boxRoot.winfo_screenwidth()
970 screen_height = boxRoot.winfo_screenheight()
971 root_width = int((screen_width * 0.8))
972 root_height = int((screen_height * 0.5))
973 root_xpos = int((screen_width * 0.1))
974 root_ypos = int((screen_height * 0.05))
975
976 boxRoot.title(title)
977 boxRoot.iconname('Dialog')
978 rootWindowPosition = "+0+0"
979 boxRoot.geometry(rootWindowPosition)
980 boxRoot.expand=NO
981 boxRoot.minsize(root_width, root_height)
982 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
983 boxRoot.geometry(rootWindowPosition)
984
985
986 message_and_buttonsFrame = Frame(master=boxRoot)
987 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
988
989 messageFrame = Frame(message_and_buttonsFrame)
990 messageFrame.pack(side=LEFT, fill=X, expand=YES)
991
992
993 buttonsFrame = Frame(message_and_buttonsFrame)
994 buttonsFrame.pack(side=RIGHT, expand=NO, pady=0)
995
996
997 choiceboxFrame = Frame(master=boxRoot)
998 choiceboxFrame.pack(side=BOTTOM, fill=BOTH, expand=YES)
999
1000
1001
1002
1003 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
1004 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1005 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
1006
1007
1008 choiceboxWidget = Listbox(choiceboxFrame
1009 , height=lines_to_show
1010 , borderwidth="1m"
1011 , relief="flat"
1012 , bg="white"
1013 )
1014
1015 if __choiceboxMultipleSelect:
1016 choiceboxWidget.configure(selectmode=MULTIPLE)
1017
1018 choiceboxWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1019
1020
1021 rightScrollbar = Scrollbar(choiceboxFrame, orient=VERTICAL, command=choiceboxWidget.yview)
1022 choiceboxWidget.configure(yscrollcommand = rightScrollbar.set)
1023
1024
1025 bottomScrollbar = Scrollbar(choiceboxFrame, orient=HORIZONTAL, command=choiceboxWidget.xview)
1026 choiceboxWidget.configure(xscrollcommand = bottomScrollbar.set)
1027
1028
1029
1030
1031
1032 bottomScrollbar.pack(side=BOTTOM, fill = X)
1033 rightScrollbar.pack(side=RIGHT, fill = Y)
1034
1035 choiceboxWidget.pack(side=LEFT, padx="1m", pady="1m", expand=YES, fill=BOTH)
1036
1037
1038
1039
1040
1041
1042 for index in range(len(choices)):
1043 choices[index] == str(choices[index])
1044
1045 if runningPython3:
1046 choices.sort(key=str.lower)
1047 else:
1048 choices.sort( lambda x,y: cmp(x.lower(), y.lower()))
1049
1050 lastInserted = None
1051 choiceboxChoices = []
1052 for choice in choices:
1053 if choice == lastInserted: pass
1054 else:
1055 choiceboxWidget.insert(END, choice)
1056 choiceboxChoices.append(choice)
1057 lastInserted = choice
1058
1059 boxRoot.bind('<Any-Key>', KeyboardListener)
1060
1061
1062 if len(choices) > 0:
1063 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1064 bindArrows(okButton)
1065 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1066
1067
1068 commandButton = okButton
1069 handler = __choiceboxGetChoice
1070 for selectionEvent in STANDARD_SELECTION_EVENTS:
1071 commandButton.bind("<%s>" % selectionEvent, handler)
1072
1073
1074 choiceboxWidget.bind("<Return>", __choiceboxGetChoice)
1075 choiceboxWidget.bind("<Double-Button-1>", __choiceboxGetChoice)
1076 else:
1077
1078 choiceboxWidget.bind("<Return>", __choiceboxCancel)
1079 choiceboxWidget.bind("<Double-Button-1>", __choiceboxCancel)
1080
1081 cancelButton = Button(buttonsFrame, takefocus=YES, text="Cancel", height=1, width=6)
1082 bindArrows(cancelButton)
1083 cancelButton.pack(expand=NO, side=BOTTOM, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1084
1085
1086 commandButton = cancelButton
1087 handler = __choiceboxCancel
1088 for selectionEvent in STANDARD_SELECTION_EVENTS:
1089 commandButton.bind("<%s>" % selectionEvent, handler)
1090
1091
1092
1093 if len(choices) > 0 and __choiceboxMultipleSelect:
1094 selectionButtonsFrame = Frame(messageFrame)
1095 selectionButtonsFrame.pack(side=RIGHT, fill=Y, expand=NO)
1096
1097 selectAllButton = Button(selectionButtonsFrame, text="Select All", height=1, width=6)
1098 bindArrows(selectAllButton)
1099
1100 selectAllButton.bind("<Button-1>",__choiceboxSelectAll)
1101 selectAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1102
1103 clearAllButton = Button(selectionButtonsFrame, text="Clear All", height=1, width=6)
1104 bindArrows(clearAllButton)
1105 clearAllButton.bind("<Button-1>",__choiceboxClearAll)
1106 clearAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1107
1108
1109
1110 boxRoot.bind("<Escape>", __choiceboxCancel)
1111
1112
1113
1114 choiceboxWidget.select_set(0)
1115 choiceboxWidget.focus_force()
1116
1117
1118 boxRoot.mainloop()
1119
1120 boxRoot.destroy()
1121 return __choiceboxResults
1122
1123
1137
1138
1142
1146
1147
1148
1154
1155
1204
1205
1206
1207
1217
1218
1219
1220
1222 """
1223 Display a box that gives information about
1224 an exception that has just been raised.
1225
1226 The caller may optionally pass in a title for the window, or a
1227 msg to accompany the error information.
1228
1229 Note that you do not need to (and cannot) pass an exception object
1230 as an argument. The latest exception will automatically be used.
1231 """
1232 if title == None: title = "Error Report"
1233 if msg == None:
1234 msg = "An error (exception) has occurred in the program."
1235
1236 codebox(msg, title, exception_format())
1237
1238
1239
1240
1241
1242 -def codebox(msg=""
1243 , title=" "
1244 , text=""
1245 ):
1246 """
1247 Display some text in a monospaced font, with no line wrapping.
1248 This function is suitable for displaying code and text that is
1249 formatted using spaces.
1250
1251 The text parameter should be a string, or a list or tuple of lines to be
1252 displayed in the textbox.
1253 """
1254 return textbox(msg, title, text, codebox=1 )
1255
1256
1257
1258
1259 -def textbox(msg=""
1260 , title=" "
1261 , text=""
1262 , codebox=0
1263 ):
1264 """
1265 Display some text in a proportional font with line wrapping at word breaks.
1266 This function is suitable for displaying general written text.
1267
1268 The text parameter should be a string, or a list or tuple of lines to be
1269 displayed in the textbox.
1270 """
1271
1272 if msg == None: msg = ""
1273 if title == None: title = ""
1274
1275 global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
1276 global rootWindowPosition
1277 choices = ["OK"]
1278 __replyButtonText = choices[0]
1279
1280
1281 boxRoot = Tk()
1282
1283 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
1284
1285 screen_width = boxRoot.winfo_screenwidth()
1286 screen_height = boxRoot.winfo_screenheight()
1287 root_width = int((screen_width * 0.8))
1288 root_height = int((screen_height * 0.5))
1289 root_xpos = int((screen_width * 0.1))
1290 root_ypos = int((screen_height * 0.05))
1291
1292 boxRoot.title(title)
1293 boxRoot.iconname('Dialog')
1294 rootWindowPosition = "+0+0"
1295 boxRoot.geometry(rootWindowPosition)
1296 boxRoot.expand=NO
1297 boxRoot.minsize(root_width, root_height)
1298 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
1299 boxRoot.geometry(rootWindowPosition)
1300
1301 mainframe = Frame(master=boxRoot)
1302 mainframe.pack(side=TOP, fill=BOTH, expand=YES)
1303
1304
1305
1306 textboxFrame = Frame(mainframe, borderwidth=3)
1307 textboxFrame.pack(side=BOTTOM , fill=BOTH, expand=YES)
1308
1309 message_and_buttonsFrame = Frame(mainframe)
1310 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
1311
1312 messageFrame = Frame(message_and_buttonsFrame)
1313 messageFrame.pack(side=LEFT, fill=X, expand=YES)
1314
1315 buttonsFrame = Frame(message_and_buttonsFrame)
1316 buttonsFrame.pack(side=RIGHT, expand=NO)
1317
1318
1319
1320
1321 if codebox:
1322 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1323 textArea = Text(textboxFrame,height=25,width=character_width, padx="2m", pady="1m")
1324 textArea.configure(wrap=NONE)
1325 textArea.configure(font=(MONOSPACE_FONT_FAMILY, MONOSPACE_FONT_SIZE))
1326
1327 else:
1328 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1329 textArea = Text(
1330 textboxFrame
1331 , height=25
1332 , width=character_width
1333 , padx="2m"
1334 , pady="1m"
1335 )
1336 textArea.configure(wrap=WORD)
1337 textArea.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1338
1339
1340
1341 mainframe.bind("<Next>" , textArea.yview_scroll( 1,PAGES))
1342 mainframe.bind("<Prior>", textArea.yview_scroll(-1,PAGES))
1343
1344 mainframe.bind("<Right>", textArea.xview_scroll( 1,PAGES))
1345 mainframe.bind("<Left>" , textArea.xview_scroll(-1,PAGES))
1346
1347 mainframe.bind("<Down>", textArea.yview_scroll( 1,UNITS))
1348 mainframe.bind("<Up>" , textArea.yview_scroll(-1,UNITS))
1349
1350
1351
1352 rightScrollbar = Scrollbar(textboxFrame, orient=VERTICAL, command=textArea.yview)
1353 textArea.configure(yscrollcommand = rightScrollbar.set)
1354
1355
1356 bottomScrollbar = Scrollbar(textboxFrame, orient=HORIZONTAL, command=textArea.xview)
1357 textArea.configure(xscrollcommand = bottomScrollbar.set)
1358
1359
1360
1361
1362
1363
1364
1365
1366 if codebox:
1367 bottomScrollbar.pack(side=BOTTOM, fill=X)
1368 rightScrollbar.pack(side=RIGHT, fill=Y)
1369
1370 textArea.pack(side=LEFT, fill=BOTH, expand=YES)
1371
1372
1373
1374 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
1375 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1376 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
1377
1378
1379 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1380 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1381
1382
1383 commandButton = okButton
1384 handler = __textboxOK
1385 for selectionEvent in ["Return","Button-1","Escape"]:
1386 commandButton.bind("<%s>" % selectionEvent, handler)
1387
1388
1389
1390 try:
1391
1392 if type(text) == type("abc"): pass
1393 else:
1394 try:
1395 text = "".join(text)
1396 except:
1397 msgbox("Exception when trying to convert "+ str(type(text)) + " to text in textArea")
1398 sys.exit(16)
1399 textArea.insert(END,text, "normal")
1400
1401 except:
1402 msgbox("Exception when trying to load the textArea.")
1403 sys.exit(16)
1404
1405 try:
1406 okButton.focus_force()
1407 except:
1408 msgbox("Exception when trying to put focus on okButton.")
1409 sys.exit(16)
1410
1411 boxRoot.mainloop()
1412
1413
1414 areaText = textArea.get(0.0,END)
1415 boxRoot.destroy()
1416 return areaText
1417
1418
1419
1420
1421 -def __textboxOK(event):
1422 global boxRoot
1423 boxRoot.quit()
1424
1425
1426
1427
1428
1429
1430 -def diropenbox(msg=None
1431 , title=None
1432 , default=None
1433 ):
1434 """
1435 A dialog to get a directory name.
1436 Note that the msg argument, if specified, is ignored.
1437
1438 Returns the name of a directory, or None if user chose to cancel.
1439
1440 If the "default" argument specifies a directory name, and that
1441 directory exists, then the dialog box will start with that directory.
1442 """
1443 title=getFileDialogTitle(msg,title)
1444 boxRoot = Tk()
1445 boxRoot.withdraw()
1446 if not default: default = None
1447 f = tk_FileDialog.askdirectory(
1448 parent=boxRoot
1449 , title=title
1450 , initialdir=default
1451 , initialfile=None
1452 )
1453 boxRoot.destroy()
1454 if not f: return None
1455 return os.path.normpath(f)
1456
1457
1458
1459
1460
1461
1465 if msg and title: return "%s - %s" % (title,msg)
1466 if msg and not title: return str(msg)
1467 if title and not msg: return str(title)
1468 return None
1469
1470
1471
1472
1475 if len(filemask) == 0:
1476 raise AssertionError('Filetype argument is empty.')
1477
1478 self.masks = []
1479
1480 if type(filemask) == type("abc"):
1481 self.initializeFromString(filemask)
1482
1483 elif type(filemask) == type([]):
1484 if len(filemask) < 2:
1485 raise AssertionError('Invalid filemask.\n'
1486 +'List contains less than 2 members: "%s"' % filemask)
1487 else:
1488 self.name = filemask[-1]
1489 self.masks = list(filemask[:-1] )
1490 else:
1491 raise AssertionError('Invalid filemask: "%s"' % filemask)
1492
1494 if self.name == other.name: return True
1495 return False
1496
1497 - def add(self,other):
1498 for mask in other.masks:
1499 if mask in self.masks: pass
1500 else: self.masks.append(mask)
1501
1503 return (self.name,tuple(self.masks))
1504
1506 if self.name == "All files": return True
1507 return False
1508
1510
1511 self.ext = os.path.splitext(filemask)[1]
1512 if self.ext == "" : self.ext = ".*"
1513 if self.ext == ".": self.ext = ".*"
1514 self.name = self.getName()
1515 self.masks = ["*" + self.ext]
1516
1518 e = self.ext
1519 if e == ".*" : return "All files"
1520 if e == ".txt": return "Text files"
1521 if e == ".py" : return "Python files"
1522 if e == ".pyc" : return "Python files"
1523 if e == ".xls": return "Excel files"
1524 if e.startswith("."):
1525 return e[1:].upper() + " files"
1526 return e.upper() + " files"
1527
1528
1529
1530
1531
1532 -def fileopenbox(msg=None
1533 , title=None
1534 , default="*"
1535 , filetypes=None
1536 ):
1537 """
1538 A dialog to get a file name.
1539
1540 About the "default" argument
1541 ============================
1542 The "default" argument specifies a filepath that (normally)
1543 contains one or more wildcards.
1544 fileopenbox will display only files that match the default filepath.
1545 If omitted, defaults to "*" (all files in the current directory).
1546
1547 WINDOWS EXAMPLE::
1548 ...default="c:/myjunk/*.py"
1549 will open in directory c:\myjunk\ and show all Python files.
1550
1551 WINDOWS EXAMPLE::
1552 ...default="c:/myjunk/test*.py"
1553 will open in directory c:\myjunk\ and show all Python files
1554 whose names begin with "test".
1555
1556
1557 Note that on Windows, fileopenbox automatically changes the path
1558 separator to the Windows path separator (backslash).
1559
1560 About the "filetypes" argument
1561 ==============================
1562 If specified, it should contain a list of items,
1563 where each item is either::
1564 - a string containing a filemask # e.g. "*.txt"
1565 - a list of strings, where all of the strings except the last one
1566 are filemasks (each beginning with "*.",
1567 such as "*.txt" for text files, "*.py" for Python files, etc.).
1568 and the last string contains a filetype description
1569
1570 EXAMPLE::
1571 filetypes = ["*.css", ["*.htm", "*.html", "HTML files"] ]
1572
1573 NOTE THAT
1574 =========
1575
1576 If the filetypes list does not contain ("All files","*"),
1577 it will be added.
1578
1579 If the filetypes list does not contain a filemask that includes
1580 the extension of the "default" argument, it will be added.
1581 For example, if default="*abc.py"
1582 and no filetypes argument was specified, then
1583 "*.py" will automatically be added to the filetypes argument.
1584
1585 @rtype: string or None
1586 @return: the name of a file, or None if user chose to cancel
1587
1588 @arg msg: the msg to be displayed.
1589 @arg title: the window title
1590 @arg default: filepath with wildcards
1591 @arg filetypes: filemasks that a user can choose, e.g. "*.txt"
1592 """
1593 boxRoot = Tk()
1594 boxRoot.withdraw()
1595
1596 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1597
1598
1599
1600
1601
1602
1603
1604 if (initialfile.find("*") < 0) and (initialfile.find("?") < 0):
1605 initialfile = None
1606 elif initialbase == "*":
1607 initialfile = None
1608
1609 f = tk_FileDialog.askopenfilename(parent=boxRoot
1610 , title=getFileDialogTitle(msg,title)
1611 , initialdir=initialdir
1612 , initialfile=initialfile
1613 , filetypes=filetypes
1614 )
1615
1616 boxRoot.destroy()
1617
1618 if not f: return None
1619 return os.path.normpath(f)
1620
1621
1622
1623
1624
1625 -def filesavebox(msg=None
1626 , title=None
1627 , default=""
1628 , filetypes=None
1629 ):
1630 """
1631 A file to get the name of a file to save.
1632 Returns the name of a file, or None if user chose to cancel.
1633
1634 The "default" argument should contain a filename (i.e. the
1635 current name of the file to be saved). It may also be empty,
1636 or contain a filemask that includes wildcards.
1637
1638 The "filetypes" argument works like the "filetypes" argument to
1639 fileopenbox.
1640 """
1641
1642 boxRoot = Tk()
1643 boxRoot.withdraw()
1644
1645 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1646
1647 f = tk_FileDialog.asksaveasfilename(parent=boxRoot
1648 , title=getFileDialogTitle(msg,title)
1649 , initialfile=initialfile
1650 , initialdir=initialdir
1651 , filetypes=filetypes
1652 )
1653 boxRoot.destroy()
1654 if not f: return None
1655 return os.path.normpath(f)
1656
1657
1658
1659
1660
1661
1662
1664 if not default: default = os.path.join(".","*")
1665 initialdir, initialfile = os.path.split(default)
1666 if not initialdir : initialdir = "."
1667 if not initialfile: initialfile = "*"
1668 initialbase, initialext = os.path.splitext(initialfile)
1669 initialFileTypeObject = FileTypeObject(initialfile)
1670
1671 allFileTypeObject = FileTypeObject("*")
1672 ALL_filetypes_was_specified = False
1673
1674 if not filetypes: filetypes= []
1675 filetypeObjects = []
1676
1677 for filemask in filetypes:
1678 fto = FileTypeObject(filemask)
1679
1680 if fto.isAll():
1681 ALL_filetypes_was_specified = True
1682
1683 if fto == initialFileTypeObject:
1684 initialFileTypeObject.add(fto)
1685 else:
1686 filetypeObjects.append(fto)
1687
1688
1689
1690
1691 if ALL_filetypes_was_specified:
1692 pass
1693 elif allFileTypeObject == initialFileTypeObject:
1694 pass
1695 else:
1696 filetypeObjects.insert(0,allFileTypeObject)
1697
1698
1699
1700
1701
1702 if len(filetypeObjects) == 0:
1703 filetypeObjects.append(initialFileTypeObject)
1704
1705 if initialFileTypeObject in (filetypeObjects[0], filetypeObjects[-1]):
1706 pass
1707 else:
1708 if runningPython26:
1709 filetypeObjects.append(initialFileTypeObject)
1710 else:
1711 filetypeObjects.insert(0,initialFileTypeObject)
1712
1713 filetypes = [fto.toTuple() for fto in filetypeObjects]
1714
1715 return initialbase, initialfile, initialdir, filetypes
1716
1717
1718
1719
1720
1721
1729
1730
1759
1760
1761
1762
1763
1764
1766 r"""
1767 A class to support persistent storage.
1768
1769 You can use EgStore to support the storage and retrieval
1770 of user settings for an EasyGui application.
1771
1772
1773 # Example A
1774 #-----------------------------------------------------------------------
1775 # define a class named Settings as a subclass of EgStore
1776 #-----------------------------------------------------------------------
1777 class Settings(EgStore):
1778
1779 def __init__(self, filename): # filename is required
1780 #-------------------------------------------------
1781 # Specify default/initial values for variables that
1782 # this particular application wants to remember.
1783 #-------------------------------------------------
1784 self.userId = ""
1785 self.targetServer = ""
1786
1787 #-------------------------------------------------
1788 # For subclasses of EgStore, these must be
1789 # the last two statements in __init__
1790 #-------------------------------------------------
1791 self.filename = filename # this is required
1792 self.restore() # restore values from the storage file if possible
1793
1794
1795
1796 # Example B
1797 #-----------------------------------------------------------------------
1798 # create settings, a persistent Settings object
1799 #-----------------------------------------------------------------------
1800 settingsFile = "myApp_settings.txt"
1801 settings = Settings(settingsFile)
1802
1803 user = "obama_barak"
1804 server = "whitehouse1"
1805 settings.userId = user
1806 settings.targetServer = server
1807 settings.store() # persist the settings
1808
1809 # run code that gets a new value for userId, and persist the settings
1810 user = "biden_joe"
1811 settings.userId = user
1812 settings.store()
1813
1814
1815 # Example C
1816 #-----------------------------------------------------------------------
1817 # recover the Settings instance, change an attribute, and store it again.
1818 #-----------------------------------------------------------------------
1819 settings = Settings(settingsFile)
1820 settings.userId = "vanrossum_g"
1821 settings.store()
1822
1823 """
1825 raise NotImplementedError()
1826
1828 """
1829 Set the values of whatever attributes are recoverable
1830 from the pickle file.
1831
1832 Populate the attributes (the __dict__) of the EgStore object
1833 from the attributes (the __dict__) of the pickled object.
1834
1835 If the pickled object has attributes that have been initialized
1836 in the EgStore object, then those attributes of the EgStore object
1837 will be replaced by the values of the corresponding attributes
1838 in the pickled object.
1839
1840 If the pickled object is missing some attributes that have
1841 been initialized in the EgStore object, then those attributes
1842 of the EgStore object will retain the values that they were
1843 initialized with.
1844
1845 If the pickled object has some attributes that were not
1846 initialized in the EgStore object, then those attributes
1847 will be ignored.
1848
1849 IN SUMMARY:
1850
1851 After the recover() operation, the EgStore object will have all,
1852 and only, the attributes that it had when it was initialized.
1853
1854 Where possible, those attributes will have values recovered
1855 from the pickled object.
1856 """
1857 if not os.path.exists(self.filename): return self
1858 if not os.path.isfile(self.filename): return self
1859
1860 try:
1861 f = open(self.filename,"rb")
1862 unpickledObject = pickle.load(f)
1863 f.close()
1864
1865 for key in list(self.__dict__.keys()):
1866 default = self.__dict__[key]
1867 self.__dict__[key] = unpickledObject.__dict__.get(key,default)
1868 except:
1869 pass
1870
1871 return self
1872
1874 """
1875 Save the attributes of the EgStore object to a pickle file.
1876 Note that if the directory for the pickle file does not already exist,
1877 the store operation will fail.
1878 """
1879 f = open(self.filename, "wb")
1880 pickle.dump(self, f)
1881 f.close()
1882
1883
1885 """
1886 Delete my persistent file (i.e. pickle file), if it exists.
1887 """
1888 if os.path.isfile(self.filename):
1889 os.remove(self.filename)
1890 return
1891
1893 """
1894 return my contents as a string in an easy-to-read format.
1895 """
1896
1897 longest_key_length = 0
1898 keys = []
1899 for key in self.__dict__.keys():
1900 keys.append(key)
1901 longest_key_length = max(longest_key_length, len(key))
1902
1903 keys.sort()
1904 lines = []
1905 for key in keys:
1906 value = self.__dict__[key]
1907 key = key.ljust(longest_key_length)
1908 lines.append("%s : %s\n" % (key,repr(value)) )
1909 return "".join(lines)
1910
1911
1912
1913
1914
1915
1916
1917
1918
1920 """
1921 Run the EasyGui demo.
1922 """
1923
1924 writeln("\n" * 100)
1925
1926 intro_message = ("Pick the kind of box that you wish to demo.\n"
1927 + "\n * Python version " + sys.version
1928 + "\n * EasyGui version " + egversion
1929 + "\n * Tk version " + str(TkVersion)
1930 )
1931
1932
1933
1934
1935 while 1:
1936 choices = [
1937 "msgbox",
1938 "buttonbox",
1939 "buttonbox(image) -- a buttonbox that displays an image",
1940 "choicebox",
1941 "multchoicebox",
1942 "textbox",
1943 "ynbox",
1944 "ccbox",
1945 "enterbox",
1946 "enterbox(image) -- an enterbox that displays an image",
1947 "exceptionbox",
1948 "codebox",
1949 "integerbox",
1950 "boolbox",
1951 "indexbox",
1952 "filesavebox",
1953 "fileopenbox",
1954 "passwordbox",
1955 "multenterbox",
1956 "multpasswordbox",
1957 "diropenbox",
1958 "About EasyGui",
1959 " Help"
1960 ]
1961 choice = choicebox(msg=intro_message
1962 , title="EasyGui " + egversion
1963 , choices=choices)
1964
1965 if not choice: return
1966
1967 reply = choice.split()
1968
1969 if reply[0] == "msgbox":
1970 reply = msgbox("short msg", "This is a long title")
1971 writeln("Reply was: %s" % repr(reply))
1972
1973 elif reply[0] == "About":
1974 reply = abouteasygui()
1975
1976 elif reply[0] == "Help":
1977 _demo_help()
1978
1979 elif reply[0] == "buttonbox":
1980 reply = buttonbox()
1981 writeln("Reply was: %s" % repr(reply))
1982
1983 title = "Demo of Buttonbox with many, many buttons!"
1984 msg = "This buttonbox shows what happens when you specify too many buttons."
1985 reply = buttonbox(msg=msg, title=title, choices=choices)
1986 writeln("Reply was: %s" % repr(reply))
1987
1988 elif reply[0] == "buttonbox(image)":
1989 _demo_buttonbox_with_image()
1990
1991 elif reply[0] == "boolbox":
1992 reply = boolbox()
1993 writeln("Reply was: %s" % repr(reply))
1994
1995 elif reply[0] == "enterbox":
1996 image = "python_and_check_logo.gif"
1997 message = "Enter the name of your best friend."\
1998 "\n(Result will be stripped.)"
1999 reply = enterbox(message, "Love!", " Suzy Smith ")
2000 writeln("Reply was: %s" % repr(reply))
2001
2002 message = "Enter the name of your best friend."\
2003 "\n(Result will NOT be stripped.)"
2004 reply = enterbox(message, "Love!", " Suzy Smith ",strip=False)
2005 writeln("Reply was: %s" % repr(reply))
2006
2007 reply = enterbox("Enter the name of your worst enemy:", "Hate!")
2008 writeln("Reply was: %s" % repr(reply))
2009
2010 elif reply[0] == "enterbox(image)":
2011 image = "python_and_check_logo.gif"
2012 message = "What kind of snake is this?"
2013 reply = enterbox(message, "Quiz",image=image)
2014 writeln("Reply was: %s" % repr(reply))
2015
2016 elif reply[0] == "exceptionbox":
2017 try:
2018 thisWillCauseADivideByZeroException = 1/0
2019 except:
2020 exceptionbox()
2021
2022 elif reply[0] == "integerbox":
2023 reply = integerbox(
2024 "Enter a number between 3 and 333",
2025 "Demo: integerbox WITH a default value",
2026 222, 3, 333)
2027 writeln("Reply was: %s" % repr(reply))
2028
2029 reply = integerbox(
2030 "Enter a number between 0 and 99",
2031 "Demo: integerbox WITHOUT a default value"
2032 )
2033 writeln("Reply was: %s" % repr(reply))
2034
2035 elif reply[0] == "diropenbox" : _demo_diropenbox()
2036 elif reply[0] == "fileopenbox": _demo_fileopenbox()
2037 elif reply[0] == "filesavebox": _demo_filesavebox()
2038
2039 elif reply[0] == "indexbox":
2040 title = reply[0]
2041 msg = "Demo of " + reply[0]
2042 choices = ["Choice1", "Choice2", "Choice3", "Choice4"]
2043 reply = indexbox(msg, title, choices)
2044 writeln("Reply was: %s" % repr(reply))
2045
2046 elif reply[0] == "passwordbox":
2047 reply = passwordbox("Demo of password box WITHOUT default"
2048 + "\n\nEnter your secret password", "Member Logon")
2049 writeln("Reply was: %s" % str(reply))
2050
2051 reply = passwordbox("Demo of password box WITH default"
2052 + "\n\nEnter your secret password", "Member Logon", "alfie")
2053 writeln("Reply was: %s" % str(reply))
2054
2055 elif reply[0] == "multenterbox":
2056 msg = "Enter your personal information"
2057 title = "Credit Card Application"
2058 fieldNames = ["Name","Street Address","City","State","ZipCode"]
2059 fieldValues = []
2060 fieldValues = multenterbox(msg,title, fieldNames)
2061
2062
2063 while 1:
2064 if fieldValues == None: break
2065 errmsg = ""
2066 for i in range(len(fieldNames)):
2067 if fieldValues[i].strip() == "":
2068 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2069 if errmsg == "": break
2070 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
2071
2072 writeln("Reply was: %s" % str(fieldValues))
2073
2074 elif reply[0] == "multpasswordbox":
2075 msg = "Enter logon information"
2076 title = "Demo of multpasswordbox"
2077 fieldNames = ["Server ID", "User ID", "Password"]
2078 fieldValues = []
2079 fieldValues = multpasswordbox(msg,title, fieldNames)
2080
2081
2082 while 1:
2083 if fieldValues == None: break
2084 errmsg = ""
2085 for i in range(len(fieldNames)):
2086 if fieldValues[i].strip() == "":
2087 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2088 if errmsg == "": break
2089 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
2090
2091 writeln("Reply was: %s" % str(fieldValues))
2092
2093 elif reply[0] == "ynbox":
2094 title = "Demo of ynbox"
2095 msg = "Were you expecting the Spanish Inquisition?"
2096 reply = ynbox(msg, title)
2097 writeln("Reply was: %s" % repr(reply))
2098 if reply:
2099 msgbox("NOBODY expects the Spanish Inquisition!", "Wrong!")
2100
2101 elif reply[0] == "ccbox":
2102 title = "Demo of ccbox"
2103 reply = ccbox(msg,title)
2104 writeln("Reply was: %s" % repr(reply))
2105
2106 elif reply[0] == "choicebox":
2107 title = "Demo of choicebox"
2108 longchoice = "This is an example of a very long option which you may or may not wish to choose."*2
2109 listChoices = ["nnn", "ddd", "eee", "fff", "aaa", longchoice
2110 , "aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk", "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq", "rrr", "sss", "ttt", "uuu", "vvv"]
2111
2112 msg = "Pick something. " + ("A wrapable sentence of text ?! "*30) + "\nA separate line of text."*6
2113 reply = choicebox(msg=msg, choices=listChoices)
2114 writeln("Reply was: %s" % repr(reply))
2115
2116 msg = "Pick something. "
2117 reply = choicebox(msg=msg, title=title, choices=listChoices)
2118 writeln("Reply was: %s" % repr(reply))
2119
2120 msg = "Pick something. "
2121 reply = choicebox(msg="The list of choices is empty!", choices=[])
2122 writeln("Reply was: %s" % repr(reply))
2123
2124 elif reply[0] == "multchoicebox":
2125 listChoices = ["aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk"
2126 , "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq"
2127 , "rrr", "sss", "ttt", "uuu", "vvv"]
2128
2129 msg = "Pick as many choices as you wish."
2130 reply = multchoicebox(msg,"Demo of multchoicebox", listChoices)
2131 writeln("Reply was: %s" % repr(reply))
2132
2133 elif reply[0] == "textbox": _demo_textbox(reply[0])
2134 elif reply[0] == "codebox": _demo_codebox(reply[0])
2135
2136 else:
2137 msgbox("Choice\n\n" + choice + "\n\nis not recognized", "Program Logic Error")
2138 return
2139
2140
2141 -def _demo_textbox(reply):
2142 text_snippet = ((\
2143 """It was the best of times, and it was the worst of times. The rich ate cake, and the poor had cake recommended to them, but wished only for enough cash to buy bread. The time was ripe for revolution! """ \
2144 *5)+"\n\n")*10
2145 title = "Demo of textbox"
2146 msg = "Here is some sample text. " * 16
2147 reply = textbox(msg, "Text Sample", text_snippet)
2148 writeln("Reply was: %s" % str(reply))
2149
2151 code_snippet = ("dafsdfa dasflkj pp[oadsij asdfp;ij asdfpjkop asdfpok asdfpok asdfpok"*3) +"\n"+\
2152 """# here is some dummy Python code
2153 for someItem in myListOfStuff:
2154 do something(someItem)
2155 do something()
2156 do something()
2157 if somethingElse(someItem):
2158 doSomethingEvenMoreInteresting()
2159
2160 """*16
2161 msg = "Here is some sample code. " * 16
2162 reply = codebox(msg, "Code Sample", code_snippet)
2163 writeln("Reply was: %s" % repr(reply))
2164
2165
2186
2187
2189 savedStdout = sys.stdout
2190 sys.stdout = capturedOutput = StringIO()
2191 help("easygui")
2192 sys.stdout = savedStdout
2193 codebox("EasyGui Help",text=capturedOutput.getvalue())
2194
2196 filename = "myNewFile.txt"
2197 title = "File SaveAs"
2198 msg ="Save file as:"
2199
2200 f = filesavebox(msg,title,default=filename)
2201 writeln("You chose to save file: %s" % f)
2202
2204 title = "Demo of diropenbox"
2205 msg = "Pick the directory that you wish to open."
2206 d = diropenbox(msg, title)
2207 writeln("You chose directory...: %s" % d)
2208
2209 d = diropenbox(msg, title,default="./")
2210 writeln("You chose directory...: %s" % d)
2211
2212 d = diropenbox(msg, title,default="c:/")
2213 writeln("You chose directory...: %s" % d)
2214
2215
2217 msg = "Python files"
2218 title = "Open files"
2219 default="*.py"
2220 f = fileopenbox(msg,title,default=default)
2221 writeln("You chose to open file: %s" % f)
2222
2223 default="./*.gif"
2224 filetypes = ["*.jpg",["*.zip","*.tgs","*.gz", "Archive files"],["*.htm", "*.html","HTML files"]]
2225 f = fileopenbox(msg,title,default=default,filetypes=filetypes)
2226 writeln("You chose to open file: %s" % f)
2227
2228 """#deadcode -- testing ----------------------------------------
2229 f = fileopenbox(None,None,default=default)
2230 writeln("You chose to open file: %s" % f)
2231
2232 f = fileopenbox(None,title,default=default)
2233 writeln("You chose to open file: %s" % f)
2234
2235 f = fileopenbox(msg,None,default=default)
2236 writeln("You chose to open file: %s" % f)
2237
2238 f = fileopenbox(default=default)
2239 writeln("You chose to open file: %s" % f)
2240
2241 f = fileopenbox(default=None)
2242 writeln("You chose to open file: %s" % f)
2243 #----------------------------------------------------deadcode """
2244
2245
2248
2249 EASYGUI_ABOUT_INFORMATION = '''
2250 ========================================================================
2251 0.95(2010-06-12)
2252 ========================================================================
2253
2254 ENHANCEMENTS
2255 ------------------------------------------------------
2256 * Previous versions of EasyGui could display only .gif image files using the
2257 msgbox "image" argument. This version can now display all image-file formats
2258 supported by PIL the Python Imaging Library) if PIL is installed.
2259 If msgbox is asked to open a non-gif image file, it attempts to import
2260 PIL and to use PIL to convert the image file to a displayable format.
2261 If PIL cannot be imported (probably because PIL is not installed)
2262 EasyGui displays an error message saying that PIL must be installed in order
2263 to display the image file.
2264
2265 Note that
2266 http://www.pythonware.com/products/pil/
2267 says that PIL doesn't yet support Python 3.x.
2268
2269
2270 ========================================================================
2271 0.94(2010-06-06)
2272 ========================================================================
2273
2274 ENHANCEMENTS
2275 ------------------------------------------------------
2276 * The codebox and textbox functions now return the contents of the box, rather
2277 than simply the name of the button ("Yes"). This makes it possible to use
2278 codebox and textbox as data-entry widgets. A big "thank you!" to Dominic
2279 Comtois for requesting this feature, patiently explaining his requirement,
2280 and helping to discover the tkinter techniques to implement it.
2281
2282 NOTE THAT in theory this change breaks backward compatibility. But because
2283 (in previous versions of EasyGui) the value returned by codebox and textbox
2284 was meaningless, no application should have been checking it. So in actual
2285 practice, this change should not break backward compatibility.
2286
2287 * Added support for SPACEBAR to command buttons. Now, when keyboard
2288 focus is on a command button, a press of the SPACEBAR will act like
2289 a press of the ENTER key; it will activate the command button.
2290
2291 * Added support for keyboard navigation with the arrow keys (up,down,left,right)
2292 to the fields and buttons in enterbox, multenterbox and multpasswordbox,
2293 and to the buttons in choicebox and all buttonboxes.
2294
2295 * added highlightthickness=2 to entry fields in multenterbox and
2296 multpasswordbox. Now it is easier to tell which entry field has
2297 keyboard focus.
2298
2299
2300 BUG FIXES
2301 ------------------------------------------------------
2302 * In EgStore, the pickle file is now opened with "rb" and "wb" rather than
2303 with "r" and "w". This change is necessary for compatibility with Python 3+.
2304 Thanks to Marshall Mattingly for reporting this problem and providing the fix.
2305
2306 * In integerbox, the actual argument names did not match the names described
2307 in the docstring. Thanks to Daniel Zingaro of at University of Toronto for
2308 reporting this problem.
2309
2310 * In integerbox, the "argLowerBound" and "argUpperBound" arguments have been
2311 renamed to "lowerbound" and "upperbound" and the docstring has been corrected.
2312
2313 NOTE THAT THIS CHANGE TO THE ARGUMENT-NAMES BREAKS BACKWARD COMPATIBILITY.
2314 If argLowerBound or argUpperBound are used, an AssertionError with an
2315 explanatory error message is raised.
2316
2317 * In choicebox, the signature to choicebox incorrectly showed choicebox as
2318 accepting a "buttons" argument. The signature has been fixed.
2319
2320
2321 ========================================================================
2322 0.93(2009-07-07)
2323 ========================================================================
2324
2325 ENHANCEMENTS
2326 ------------------------------------------------------
2327
2328 * Added exceptionbox to display stack trace of exceptions
2329
2330 * modified names of some font-related constants to make it
2331 easier to customize them
2332
2333
2334 ========================================================================
2335 0.92(2009-06-22)
2336 ========================================================================
2337
2338 ENHANCEMENTS
2339 ------------------------------------------------------
2340
2341 * Added EgStore class to to provide basic easy-to-use persistence.
2342
2343 BUG FIXES
2344 ------------------------------------------------------
2345
2346 * Fixed a bug that was preventing Linux users from copying text out of
2347 a textbox and a codebox. This was not a problem for Windows users.
2348
2349 '''
2350
2357
2358
2359
2360 if __name__ == '__main__':
2361 if True:
2362 egdemo()
2363 else:
2364
2365 root = Tk()
2366 msg = """This is a test of a main Tk() window in which we will place an easygui msgbox.
2367 It will be an interesting experiment.\n\n"""
2368 messageWidget = Message(root, text=msg, width=1000)
2369 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2370 messageWidget = Message(root, text=msg, width=1000)
2371 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2372
2373
2374 msgbox("this is a test of passing in boxRoot", root=root)
2375 msgbox("this is a second test of passing in boxRoot", root=root)
2376
2377 reply = enterbox("Enter something", root=root)
2378 writeln("You wrote:", reply)
2379
2380 reply = enterbox("Enter something else", root=root)
2381 writeln("You wrote:", reply)
2382 root.destroy()
2383