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