1 """
2 @version: 0.94(2010-06-06)
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
345
346
347
348
349
350 -def integerbox(msg=""
351 , title=" "
352 , default=""
353 , lowerbound=0
354 , upperbound=99
355 , image = None
356 , root = None
357 , **invalidKeywordArguments
358 ):
359 """
360 Show a box in which a user can enter an integer.
361
362 In addition to arguments for msg and title, this function accepts
363 integer arguments for "default", "lowerbound", and "upperbound".
364
365 The default argument may be None.
366
367 When the user enters some text, the text is checked to verify that it
368 can be converted to an integer between the lowerbound and upperbound.
369
370 If it can be, the integer (not the text) is returned.
371
372 If it cannot, then an error msg is displayed, and the integerbox is
373 redisplayed.
374
375 If the user cancels the operation, None is returned.
376
377 NOTE that the "argLowerBound" and "argUpperBound" arguments are no longer
378 supported. They have been replaced by "upperbound" and "lowerbound".
379 """
380 if "argLowerBound" in invalidKeywordArguments:
381 raise AssertionError(
382 "\nintegerbox no longer supports the 'argLowerBound' argument.\n"
383 + "Use 'lowerbound' instead.\n\n")
384 if "argUpperBound" in invalidKeywordArguments:
385 raise AssertionError(
386 "\nintegerbox no longer supports the 'argUpperBound' argument.\n"
387 + "Use 'upperbound' instead.\n\n")
388
389 if default != "":
390 if type(default) != type(1):
391 raise AssertionError(
392 "integerbox received a non-integer value for "
393 + "default of " + dq(str(default)) , "Error")
394
395 if type(lowerbound) != type(1):
396 raise AssertionError(
397 "integerbox received a non-integer value for "
398 + "lowerbound of " + dq(str(lowerbound)) , "Error")
399
400 if type(upperbound) != type(1):
401 raise AssertionError(
402 "integerbox received a non-integer value for "
403 + "upperbound of " + dq(str(upperbound)) , "Error")
404
405 if msg == "":
406 msg = ("Enter an integer between " + str(lowerbound)
407 + " and "
408 + str(upperbound)
409 )
410
411 while 1:
412 reply = enterbox(msg, title, str(default), image=image, root=root)
413 if reply == None: return None
414
415 try:
416 reply = int(reply)
417 except:
418 msgbox ("The value that you entered:\n\t%s\nis not an integer." % dq(str(reply))
419 , "Error")
420 continue
421
422 if reply < lowerbound:
423 msgbox ("The value that you entered is less than the lower bound of "
424 + str(lowerbound) + ".", "Error")
425 continue
426
427 if reply > upperbound:
428 msgbox ("The value that you entered is greater than the upper bound of "
429 + str(upperbound) + ".", "Error")
430 continue
431
432
433
434 return reply
435
436
437
438
439 -def multenterbox(msg="Fill in values for the fields."
440 , title=" "
441 , fields=()
442 , values=()
443 ):
444 r"""
445 Show screen with multiple data entry fields.
446
447 If there are fewer values than names, the list of values is padded with
448 empty strings until the number of values is the same as the number of names.
449
450 If there are more values than names, the list of values
451 is truncated so that there are as many values as names.
452
453 Returns a list of the values of the fields,
454 or None if the user cancels the operation.
455
456 Here is some example code, that shows how values returned from
457 multenterbox can be checked for validity before they are accepted::
458 ----------------------------------------------------------------------
459 msg = "Enter your personal information"
460 title = "Credit Card Application"
461 fieldNames = ["Name","Street Address","City","State","ZipCode"]
462 fieldValues = [] # we start with blanks for the values
463 fieldValues = multenterbox(msg,title, fieldNames)
464
465 # make sure that none of the fields was left blank
466 while 1:
467 if fieldValues == None: break
468 errmsg = ""
469 for i in range(len(fieldNames)):
470 if fieldValues[i].strip() == "":
471 errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
472 if errmsg == "":
473 break # no problems found
474 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
475
476 writeln("Reply was: %s" % str(fieldValues))
477 ----------------------------------------------------------------------
478
479 @arg msg: the msg to be displayed.
480 @arg title: the window title
481 @arg fields: a list of fieldnames.
482 @arg values: a list of field values
483 """
484 return __multfillablebox(msg,title,fields,values,None)
485
486
487
488
489
490 -def multpasswordbox(msg="Fill in values for the fields."
491 , title=" "
492 , fields=tuple()
493 ,values=tuple()
494 ):
495 r"""
496 Same interface as multenterbox. But in multpassword box,
497 the last of the fields is assumed to be a password, and
498 is masked with asterisks.
499
500 Example
501 =======
502
503 Here is some example code, that shows how values returned from
504 multpasswordbox can be checked for validity before they are accepted::
505 msg = "Enter logon information"
506 title = "Demo of multpasswordbox"
507 fieldNames = ["Server ID", "User ID", "Password"]
508 fieldValues = [] # we start with blanks for the values
509 fieldValues = multpasswordbox(msg,title, fieldNames)
510
511 # make sure that none of the fields was left blank
512 while 1:
513 if fieldValues == None: break
514 errmsg = ""
515 for i in range(len(fieldNames)):
516 if fieldValues[i].strip() == "":
517 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
518 if errmsg == "": break # no problems found
519 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
520
521 writeln("Reply was: %s" % str(fieldValues))
522 """
523 return __multfillablebox(msg,title,fields,values,"*")
524
531
533 boxRoot.event_generate("<Tab>")
534
536 boxRoot.event_generate("<Shift-Tab>")
537
538
539
540
541 -def __multfillablebox(msg="Fill in values for the fields."
542 , title=" "
543 , fields=()
544 , values=()
545 , mask = None
546 ):
547 global boxRoot, __multenterboxText, __multenterboxDefaultText, cancelButton, entryWidget, okButton
548
549 choices = ["OK", "Cancel"]
550 if len(fields) == 0: return None
551
552 fields = list(fields[:])
553 values = list(values[:])
554
555 if len(values) == len(fields): pass
556 elif len(values) > len(fields):
557 fields = fields[0:len(values)]
558 else:
559 while len(values) < len(fields):
560 values.append("")
561
562 boxRoot = Tk()
563
564 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
565 boxRoot.title(title)
566 boxRoot.iconname('Dialog')
567 boxRoot.geometry(rootWindowPosition)
568 boxRoot.bind("<Escape>", __multenterboxCancel)
569
570
571 messageFrame = Frame(master=boxRoot)
572 messageFrame.pack(side=TOP, fill=BOTH)
573
574
575 messageWidget = Message(messageFrame, width="4.5i", text=msg)
576 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
577 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
578
579 global entryWidgets
580 entryWidgets = []
581
582 lastWidgetIndex = len(fields) - 1
583
584 for widgetIndex in range(len(fields)):
585 argFieldName = fields[widgetIndex]
586 argFieldValue = values[widgetIndex]
587 entryFrame = Frame(master=boxRoot)
588 entryFrame.pack(side=TOP, fill=BOTH)
589
590
591 labelWidget = Label(entryFrame, text=argFieldName)
592 labelWidget.pack(side=LEFT)
593
594 entryWidget = Entry(entryFrame, width=40,highlightthickness=2)
595 entryWidgets.append(entryWidget)
596 entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
597 entryWidget.pack(side=RIGHT, padx="3m")
598
599 bindArrows(entryWidget)
600
601 entryWidget.bind("<Return>", __multenterboxGetText)
602 entryWidget.bind("<Escape>", __multenterboxCancel)
603
604
605
606 if widgetIndex == lastWidgetIndex:
607 if mask:
608 entryWidgets[widgetIndex].configure(show=mask)
609
610
611 entryWidgets[widgetIndex].insert(0,argFieldValue)
612 widgetIndex += 1
613
614
615 buttonsFrame = Frame(master=boxRoot)
616 buttonsFrame.pack(side=BOTTOM, fill=BOTH)
617
618 okButton = Button(buttonsFrame, takefocus=1, text="OK")
619 bindArrows(okButton)
620 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
621
622
623 commandButton = okButton
624 handler = __multenterboxGetText
625 for selectionEvent in STANDARD_SELECTION_EVENTS:
626 commandButton.bind("<%s>" % selectionEvent, handler)
627
628
629
630 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
631 bindArrows(cancelButton)
632 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
633
634
635 commandButton = cancelButton
636 handler = __multenterboxCancel
637 for selectionEvent in STANDARD_SELECTION_EVENTS:
638 commandButton.bind("<%s>" % selectionEvent, handler)
639
640
641
642 entryWidgets[0].focus_force()
643 boxRoot.mainloop()
644
645
646 boxRoot.destroy()
647 return __multenterboxText
648
649
650
651
652
654 global __multenterboxText
655
656 __multenterboxText = []
657 for entryWidget in entryWidgets:
658 __multenterboxText.append(entryWidget.get())
659 boxRoot.quit()
660
661
666
667
668
669
670
671 -def enterbox(msg="Enter something."
672 , title=" "
673 , default=""
674 , strip=True
675 , image=None
676 , root=None
677 ):
678 """
679 Show a box in which a user can enter some text.
680
681 You may optionally specify some default text, which will appear in the
682 enterbox when it is displayed.
683
684 Returns the text that the user entered, or None if he cancels the operation.
685
686 By default, enterbox strips its result (i.e. removes leading and trailing
687 whitespace). (If you want it not to strip, use keyword argument: strip=False.)
688 This makes it easier to test the results of the call::
689
690 reply = enterbox(....)
691 if reply:
692 ...
693 else:
694 ...
695 """
696 result = __fillablebox(msg, title, default=default, mask=None,image=image,root=root)
697 if result and strip:
698 result = result.strip()
699 return result
700
701
702 -def passwordbox(msg="Enter your password."
703 , title=" "
704 , default=""
705 , image=None
706 , root=None
707 ):
708 """
709 Show a box in which a user can enter a password.
710 The text is masked with asterisks, so the password is not displayed.
711 Returns the text that the user entered, or None if he cancels the operation.
712 """
713 return __fillablebox(msg, title, default, mask="*",image=image,root=root)
714
715
716 -def __fillablebox(msg
717 , title=""
718 , default=""
719 , mask=None
720 , image=None
721 , root=None
722 ):
723 """
724 Show a box in which a user can enter some text.
725 You may optionally specify some default text, which will appear in the
726 enterbox when it is displayed.
727 Returns the text that the user entered, or None if he cancels the operation.
728 """
729
730 global boxRoot, __enterboxText, __enterboxDefaultText
731 global cancelButton, entryWidget, okButton
732
733 if title == None: title == ""
734 if default == None: default = ""
735 __enterboxDefaultText = default
736 __enterboxText = __enterboxDefaultText
737
738 if root:
739 root.withdraw()
740 boxRoot = Toplevel(master=root)
741 boxRoot.withdraw()
742 else:
743 boxRoot = Tk()
744 boxRoot.withdraw()
745
746 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
747 boxRoot.title(title)
748 boxRoot.iconname('Dialog')
749 boxRoot.geometry(rootWindowPosition)
750 boxRoot.bind("<Escape>", __enterboxCancel)
751
752 if image:
753 image = os.path.normpath(image)
754 junk,ext = os.path.splitext(image)
755 if ext.lower() == ".gif":
756 if os.path.exists(image):
757 pass
758 else:
759 msg += ImageErrorMsg % (image, "Image file not found.")
760 image = None
761 else:
762 msg += ImageErrorMsg % (image, "Image file is not a .gif file.")
763 image = None
764
765 messageFrame = Frame(master=boxRoot)
766 messageFrame.pack(side=TOP, fill=BOTH)
767
768
769 if image:
770 imageFrame = Frame(master=boxRoot)
771 imageFrame.pack(side=TOP, fill=BOTH)
772 image = PhotoImage(file=image)
773 label = Label(imageFrame,image=image)
774 label.image = image
775 label.pack(side=TOP, expand=YES, fill=X, padx='1m', pady='1m')
776
777
778 entryFrame = Frame(master=boxRoot)
779 entryFrame.pack(side=TOP, fill=BOTH)
780
781
782 buttonsFrame = Frame(master=boxRoot)
783 buttonsFrame.pack(side=TOP, fill=BOTH)
784
785
786 messageWidget = Message(messageFrame, width="4.5i", text=msg)
787 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
788 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
789
790
791 entryWidget = Entry(entryFrame, width=40)
792 bindArrows(entryWidget)
793 entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
794 if mask:
795 entryWidget.configure(show=mask)
796 entryWidget.pack(side=LEFT, padx="3m")
797 entryWidget.bind("<Return>", __enterboxGetText)
798 entryWidget.bind("<Escape>", __enterboxCancel)
799
800 entryWidget.insert(0,__enterboxDefaultText)
801
802
803 okButton = Button(buttonsFrame, takefocus=1, text="OK")
804 bindArrows(okButton)
805 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
806
807
808 commandButton = okButton
809 handler = __enterboxGetText
810 for selectionEvent in STANDARD_SELECTION_EVENTS:
811 commandButton.bind("<%s>" % selectionEvent, handler)
812
813
814
815 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
816 bindArrows(cancelButton)
817 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
818
819
820 commandButton = cancelButton
821 handler = __enterboxCancel
822 for selectionEvent in STANDARD_SELECTION_EVENTS:
823 commandButton.bind("<%s>" % selectionEvent, handler)
824
825
826 entryWidget.focus_force()
827 boxRoot.deiconify()
828 boxRoot.mainloop()
829
830
831 if root: root.deiconify()
832 boxRoot.destroy()
833 return __enterboxText
834
835
840
841
846
847
852
854 """ don't allow WindowManager close
855 """
856 x = Tk()
857 x.withdraw()
858 x.bell()
859 x.destroy()
860
861
862
863
864
865
866 -def multchoicebox(msg="Pick as many items as you like."
867 , title=" "
868 , choices=()
869 , **kwargs
870 ):
871 """
872 Present the user with a list of choices.
873 allow him to select multiple items and return them in a list.
874 if the user doesn't choose anything from the list, return the empty list.
875 return None if he cancelled selection.
876
877 @arg msg: the msg to be displayed.
878 @arg title: the window title
879 @arg choices: a list or tuple of the choices to be displayed
880 """
881 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
882
883 global __choiceboxMultipleSelect
884 __choiceboxMultipleSelect = 1
885 return __choicebox(msg, title, choices)
886
887
888
889
890
891 -def choicebox(msg="Pick something."
892 , title=" "
893 , choices=()
894 ):
895 """
896 Present the user with a list of choices.
897 return the choice that he selects.
898 return None if he cancels the selection selection.
899
900 @arg msg: the msg to be displayed.
901 @arg title: the window title
902 @arg choices: a list or tuple of the choices to be displayed
903 """
904 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
905
906 global __choiceboxMultipleSelect
907 __choiceboxMultipleSelect = 0
908 return __choicebox(msg,title,choices)
909
910
911
912
913
918 """
919 internal routine to support choicebox() and multchoicebox()
920 """
921 global boxRoot, __choiceboxResults, choiceboxWidget, defaultText
922 global choiceboxWidget, choiceboxChoices
923
924
925
926
927
928
929 choices = list(choices[:])
930 if len(choices) == 0:
931 choices = ["Program logic error - no choices were specified."]
932 defaultButtons = ["OK", "Cancel"]
933
934
935 for index in range(len(choices)):
936 choices[index] = str(choices[index])
937
938 lines_to_show = min(len(choices), 20)
939 lines_to_show = 20
940
941 if title == None: title = ""
942
943
944
945 __choiceboxResults = None
946
947 boxRoot = Tk()
948 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
949 screen_width = boxRoot.winfo_screenwidth()
950 screen_height = boxRoot.winfo_screenheight()
951 root_width = int((screen_width * 0.8))
952 root_height = int((screen_height * 0.5))
953 root_xpos = int((screen_width * 0.1))
954 root_ypos = int((screen_height * 0.05))
955
956 boxRoot.title(title)
957 boxRoot.iconname('Dialog')
958 rootWindowPosition = "+0+0"
959 boxRoot.geometry(rootWindowPosition)
960 boxRoot.expand=NO
961 boxRoot.minsize(root_width, root_height)
962 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
963 boxRoot.geometry(rootWindowPosition)
964
965
966 message_and_buttonsFrame = Frame(master=boxRoot)
967 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
968
969 messageFrame = Frame(message_and_buttonsFrame)
970 messageFrame.pack(side=LEFT, fill=X, expand=YES)
971
972
973 buttonsFrame = Frame(message_and_buttonsFrame)
974 buttonsFrame.pack(side=RIGHT, expand=NO, pady=0)
975
976
977 choiceboxFrame = Frame(master=boxRoot)
978 choiceboxFrame.pack(side=BOTTOM, fill=BOTH, expand=YES)
979
980
981
982
983 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
984 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
985 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
986
987
988 choiceboxWidget = Listbox(choiceboxFrame
989 , height=lines_to_show
990 , borderwidth="1m"
991 , relief="flat"
992 , bg="white"
993 )
994
995 if __choiceboxMultipleSelect:
996 choiceboxWidget.configure(selectmode=MULTIPLE)
997
998 choiceboxWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
999
1000
1001 rightScrollbar = Scrollbar(choiceboxFrame, orient=VERTICAL, command=choiceboxWidget.yview)
1002 choiceboxWidget.configure(yscrollcommand = rightScrollbar.set)
1003
1004
1005 bottomScrollbar = Scrollbar(choiceboxFrame, orient=HORIZONTAL, command=choiceboxWidget.xview)
1006 choiceboxWidget.configure(xscrollcommand = bottomScrollbar.set)
1007
1008
1009
1010
1011
1012 bottomScrollbar.pack(side=BOTTOM, fill = X)
1013 rightScrollbar.pack(side=RIGHT, fill = Y)
1014
1015 choiceboxWidget.pack(side=LEFT, padx="1m", pady="1m", expand=YES, fill=BOTH)
1016
1017
1018
1019
1020
1021
1022 for index in range(len(choices)):
1023 choices[index] == str(choices[index])
1024
1025 if runningPython3:
1026 choices.sort(key=str.lower)
1027 else:
1028 choices.sort( lambda x,y: cmp(x.lower(), y.lower()))
1029
1030 lastInserted = None
1031 choiceboxChoices = []
1032 for choice in choices:
1033 if choice == lastInserted: pass
1034 else:
1035 choiceboxWidget.insert(END, choice)
1036 choiceboxChoices.append(choice)
1037 lastInserted = choice
1038
1039 boxRoot.bind('<Any-Key>', KeyboardListener)
1040
1041
1042 if len(choices) > 0:
1043 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1044 bindArrows(okButton)
1045 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1046
1047
1048 commandButton = okButton
1049 handler = __choiceboxGetChoice
1050 for selectionEvent in STANDARD_SELECTION_EVENTS:
1051 commandButton.bind("<%s>" % selectionEvent, handler)
1052
1053
1054 choiceboxWidget.bind("<Return>", __choiceboxGetChoice)
1055 choiceboxWidget.bind("<Double-Button-1>", __choiceboxGetChoice)
1056 else:
1057
1058 choiceboxWidget.bind("<Return>", __choiceboxCancel)
1059 choiceboxWidget.bind("<Double-Button-1>", __choiceboxCancel)
1060
1061 cancelButton = Button(buttonsFrame, takefocus=YES, text="Cancel", height=1, width=6)
1062 bindArrows(cancelButton)
1063 cancelButton.pack(expand=NO, side=BOTTOM, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1064
1065
1066 commandButton = cancelButton
1067 handler = __choiceboxCancel
1068 for selectionEvent in STANDARD_SELECTION_EVENTS:
1069 commandButton.bind("<%s>" % selectionEvent, handler)
1070
1071
1072
1073 if len(choices) > 0 and __choiceboxMultipleSelect:
1074 selectionButtonsFrame = Frame(messageFrame)
1075 selectionButtonsFrame.pack(side=RIGHT, fill=Y, expand=NO)
1076
1077 selectAllButton = Button(selectionButtonsFrame, text="Select All", height=1, width=6)
1078 bindArrows(selectAllButton)
1079
1080 selectAllButton.bind("<Button-1>",__choiceboxSelectAll)
1081 selectAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1082
1083 clearAllButton = Button(selectionButtonsFrame, text="Clear All", height=1, width=6)
1084 bindArrows(clearAllButton)
1085 clearAllButton.bind("<Button-1>",__choiceboxClearAll)
1086 clearAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1087
1088
1089
1090 boxRoot.bind("<Escape>", __choiceboxCancel)
1091
1092
1093
1094 choiceboxWidget.select_set(0)
1095 choiceboxWidget.focus_force()
1096
1097
1098 boxRoot.mainloop()
1099
1100 boxRoot.destroy()
1101 return __choiceboxResults
1102
1103
1117
1118
1122
1126
1127
1128
1134
1135
1184
1185
1186
1187
1197
1198
1199
1200
1202 """
1203 Display a box that gives information about
1204 an exception that has just been raised.
1205
1206 The caller may optionally pass in a title for the window, or a
1207 msg to accompany the error information.
1208
1209 Note that you do not need to (and cannot) pass an exception object
1210 as an argument. The latest exception will automatically be used.
1211 """
1212 if title == None: title = "Error Report"
1213 if msg == None:
1214 msg = "An error (exception) has occurred in the program."
1215
1216 codebox(msg, title, exception_format())
1217
1218
1219
1220
1221
1222 -def codebox(msg=""
1223 , title=" "
1224 , text=""
1225 ):
1226 """
1227 Display some text in a monospaced font, with no line wrapping.
1228 This function is suitable for displaying code and text that is
1229 formatted using spaces.
1230
1231 The text parameter should be a string, or a list or tuple of lines to be
1232 displayed in the textbox.
1233 """
1234 return textbox(msg, title, text, codebox=1 )
1235
1236
1237
1238
1239 -def textbox(msg=""
1240 , title=" "
1241 , text=""
1242 , codebox=0
1243 ):
1244 """
1245 Display some text in a proportional font with line wrapping at word breaks.
1246 This function is suitable for displaying general written text.
1247
1248 The text parameter should be a string, or a list or tuple of lines to be
1249 displayed in the textbox.
1250 """
1251
1252 if msg == None: msg = ""
1253 if title == None: title = ""
1254
1255 global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
1256 global rootWindowPosition
1257 choices = ["OK"]
1258 __replyButtonText = choices[0]
1259
1260
1261 boxRoot = Tk()
1262
1263 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
1264
1265 screen_width = boxRoot.winfo_screenwidth()
1266 screen_height = boxRoot.winfo_screenheight()
1267 root_width = int((screen_width * 0.8))
1268 root_height = int((screen_height * 0.5))
1269 root_xpos = int((screen_width * 0.1))
1270 root_ypos = int((screen_height * 0.05))
1271
1272 boxRoot.title(title)
1273 boxRoot.iconname('Dialog')
1274 rootWindowPosition = "+0+0"
1275 boxRoot.geometry(rootWindowPosition)
1276 boxRoot.expand=NO
1277 boxRoot.minsize(root_width, root_height)
1278 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
1279 boxRoot.geometry(rootWindowPosition)
1280
1281 mainframe = Frame(master=boxRoot)
1282 mainframe.pack(side=TOP, fill=BOTH, expand=YES)
1283
1284
1285
1286 textboxFrame = Frame(mainframe, borderwidth=3)
1287 textboxFrame.pack(side=BOTTOM , fill=BOTH, expand=YES)
1288
1289 message_and_buttonsFrame = Frame(mainframe)
1290 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
1291
1292 messageFrame = Frame(message_and_buttonsFrame)
1293 messageFrame.pack(side=LEFT, fill=X, expand=YES)
1294
1295 buttonsFrame = Frame(message_and_buttonsFrame)
1296 buttonsFrame.pack(side=RIGHT, expand=NO)
1297
1298
1299
1300
1301 if codebox:
1302 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1303 textArea = Text(textboxFrame,height=25,width=character_width, padx="2m", pady="1m")
1304 textArea.configure(wrap=NONE)
1305 textArea.configure(font=(MONOSPACE_FONT_FAMILY, MONOSPACE_FONT_SIZE))
1306
1307 else:
1308 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1309 textArea = Text(
1310 textboxFrame
1311 , height=25
1312 , width=character_width
1313 , padx="2m"
1314 , pady="1m"
1315 )
1316 textArea.configure(wrap=WORD)
1317 textArea.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1318
1319
1320
1321 mainframe.bind("<Next>" , textArea.yview_scroll( 1,PAGES))
1322 mainframe.bind("<Prior>", textArea.yview_scroll(-1,PAGES))
1323
1324 mainframe.bind("<Right>", textArea.xview_scroll( 1,PAGES))
1325 mainframe.bind("<Left>" , textArea.xview_scroll(-1,PAGES))
1326
1327 mainframe.bind("<Down>", textArea.yview_scroll( 1,UNITS))
1328 mainframe.bind("<Up>" , textArea.yview_scroll(-1,UNITS))
1329
1330
1331
1332 rightScrollbar = Scrollbar(textboxFrame, orient=VERTICAL, command=textArea.yview)
1333 textArea.configure(yscrollcommand = rightScrollbar.set)
1334
1335
1336 bottomScrollbar = Scrollbar(textboxFrame, orient=HORIZONTAL, command=textArea.xview)
1337 textArea.configure(xscrollcommand = bottomScrollbar.set)
1338
1339
1340
1341
1342
1343
1344
1345
1346 if codebox:
1347 bottomScrollbar.pack(side=BOTTOM, fill=X)
1348 rightScrollbar.pack(side=RIGHT, fill=Y)
1349
1350 textArea.pack(side=LEFT, fill=BOTH, expand=YES)
1351
1352
1353
1354 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
1355 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1356 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
1357
1358
1359 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1360 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1361
1362
1363 commandButton = okButton
1364 handler = __textboxOK
1365 for selectionEvent in ["Return","Button-1","Escape"]:
1366 commandButton.bind("<%s>" % selectionEvent, handler)
1367
1368
1369
1370 try:
1371
1372 if type(text) == type("abc"): pass
1373 else:
1374 try:
1375 text = "".join(text)
1376 except:
1377 msgbox("Exception when trying to convert "+ str(type(text)) + " to text in textArea")
1378 sys.exit(16)
1379 textArea.insert(END,text, "normal")
1380
1381 except:
1382 msgbox("Exception when trying to load the textArea.")
1383 sys.exit(16)
1384
1385 try:
1386 okButton.focus_force()
1387 except:
1388 msgbox("Exception when trying to put focus on okButton.")
1389 sys.exit(16)
1390
1391 boxRoot.mainloop()
1392
1393
1394 areaText = textArea.get(0.0,END)
1395 boxRoot.destroy()
1396 return areaText
1397
1398
1399
1400
1401 -def __textboxOK(event):
1402 global boxRoot
1403 boxRoot.quit()
1404
1405
1406
1407
1408
1409
1410 -def diropenbox(msg=None
1411 , title=None
1412 , default=None
1413 ):
1414 """
1415 A dialog to get a directory name.
1416 Note that the msg argument, if specified, is ignored.
1417
1418 Returns the name of a directory, or None if user chose to cancel.
1419
1420 If the "default" argument specifies a directory name, and that
1421 directory exists, then the dialog box will start with that directory.
1422 """
1423 title=getFileDialogTitle(msg,title)
1424 boxRoot = Tk()
1425 boxRoot.withdraw()
1426 if not default: default = None
1427 f = tk_FileDialog.askdirectory(
1428 parent=boxRoot
1429 , title=title
1430 , initialdir=default
1431 , initialfile=None
1432 )
1433 boxRoot.destroy()
1434 if not f: return None
1435 return os.path.normpath(f)
1436
1437
1438
1439
1440
1441
1445 if msg and title: return "%s - %s" % (title,msg)
1446 if msg and not title: return str(msg)
1447 if title and not msg: return str(title)
1448 return None
1449
1450
1451
1452
1455 if len(filemask) == 0:
1456 raise AssertionError('Filetype argument is empty.')
1457
1458 self.masks = []
1459
1460 if type(filemask) == type("abc"):
1461 self.initializeFromString(filemask)
1462
1463 elif type(filemask) == type([]):
1464 if len(filemask) < 2:
1465 raise AssertionError('Invalid filemask.\n'
1466 +'List contains less than 2 members: "%s"' % filemask)
1467 else:
1468 self.name = filemask[-1]
1469 self.masks = list(filemask[:-1] )
1470 else:
1471 raise AssertionError('Invalid filemask: "%s"' % filemask)
1472
1474 if self.name == other.name: return True
1475 return False
1476
1477 - def add(self,other):
1478 for mask in other.masks:
1479 if mask in self.masks: pass
1480 else: self.masks.append(mask)
1481
1483 return (self.name,tuple(self.masks))
1484
1486 if self.name == "All files": return True
1487 return False
1488
1490
1491 self.ext = os.path.splitext(filemask)[1]
1492 if self.ext == "" : self.ext = ".*"
1493 if self.ext == ".": self.ext = ".*"
1494 self.name = self.getName()
1495 self.masks = ["*" + self.ext]
1496
1498 e = self.ext
1499 if e == ".*" : return "All files"
1500 if e == ".txt": return "Text files"
1501 if e == ".py" : return "Python files"
1502 if e == ".pyc" : return "Python files"
1503 if e == ".xls": return "Excel files"
1504 if e.startswith("."):
1505 return e[1:].upper() + " files"
1506 return e.upper() + " files"
1507
1508
1509
1510
1511
1512 -def fileopenbox(msg=None
1513 , title=None
1514 , default="*"
1515 , filetypes=None
1516 ):
1517 """
1518 A dialog to get a file name.
1519
1520 About the "default" argument
1521 ============================
1522 The "default" argument specifies a filepath that (normally)
1523 contains one or more wildcards.
1524 fileopenbox will display only files that match the default filepath.
1525 If omitted, defaults to "*" (all files in the current directory).
1526
1527 WINDOWS EXAMPLE::
1528 ...default="c:/myjunk/*.py"
1529 will open in directory c:\myjunk\ and show all Python files.
1530
1531 WINDOWS EXAMPLE::
1532 ...default="c:/myjunk/test*.py"
1533 will open in directory c:\myjunk\ and show all Python files
1534 whose names begin with "test".
1535
1536
1537 Note that on Windows, fileopenbox automatically changes the path
1538 separator to the Windows path separator (backslash).
1539
1540 About the "filetypes" argument
1541 ==============================
1542 If specified, it should contain a list of items,
1543 where each item is either::
1544 - a string containing a filemask # e.g. "*.txt"
1545 - a list of strings, where all of the strings except the last one
1546 are filemasks (each beginning with "*.",
1547 such as "*.txt" for text files, "*.py" for Python files, etc.).
1548 and the last string contains a filetype description
1549
1550 EXAMPLE::
1551 filetypes = ["*.css", ["*.htm", "*.html", "HTML files"] ]
1552
1553 NOTE THAT
1554 =========
1555
1556 If the filetypes list does not contain ("All files","*"),
1557 it will be added.
1558
1559 If the filetypes list does not contain a filemask that includes
1560 the extension of the "default" argument, it will be added.
1561 For example, if default="*abc.py"
1562 and no filetypes argument was specified, then
1563 "*.py" will automatically be added to the filetypes argument.
1564
1565 @rtype: string or None
1566 @return: the name of a file, or None if user chose to cancel
1567
1568 @arg msg: the msg to be displayed.
1569 @arg title: the window title
1570 @arg default: filepath with wildcards
1571 @arg filetypes: filemasks that a user can choose, e.g. "*.txt"
1572 """
1573 boxRoot = Tk()
1574 boxRoot.withdraw()
1575
1576 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1577
1578
1579
1580
1581
1582
1583
1584 if (initialfile.find("*") < 0) and (initialfile.find("?") < 0):
1585 initialfile = None
1586 elif initialbase == "*":
1587 initialfile = None
1588
1589 f = tk_FileDialog.askopenfilename(parent=boxRoot
1590 , title=getFileDialogTitle(msg,title)
1591 , initialdir=initialdir
1592 , initialfile=initialfile
1593 , filetypes=filetypes
1594 )
1595
1596 boxRoot.destroy()
1597
1598 if not f: return None
1599 return os.path.normpath(f)
1600
1601
1602
1603
1604
1605 -def filesavebox(msg=None
1606 , title=None
1607 , default=""
1608 , filetypes=None
1609 ):
1610 """
1611 A file to get the name of a file to save.
1612 Returns the name of a file, or None if user chose to cancel.
1613
1614 The "default" argument should contain a filename (i.e. the
1615 current name of the file to be saved). It may also be empty,
1616 or contain a filemask that includes wildcards.
1617
1618 The "filetypes" argument works like the "filetypes" argument to
1619 fileopenbox.
1620 """
1621
1622 boxRoot = Tk()
1623 boxRoot.withdraw()
1624
1625 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1626
1627 f = tk_FileDialog.asksaveasfilename(parent=boxRoot
1628 , title=getFileDialogTitle(msg,title)
1629 , initialfile=initialfile
1630 , initialdir=initialdir
1631 , filetypes=filetypes
1632 )
1633 boxRoot.destroy()
1634 if not f: return None
1635 return os.path.normpath(f)
1636
1637
1638
1639
1640
1641
1642
1644 if not default: default = os.path.join(".","*")
1645 initialdir, initialfile = os.path.split(default)
1646 if not initialdir : initialdir = "."
1647 if not initialfile: initialfile = "*"
1648 initialbase, initialext = os.path.splitext(initialfile)
1649 initialFileTypeObject = FileTypeObject(initialfile)
1650
1651 allFileTypeObject = FileTypeObject("*")
1652 ALL_filetypes_was_specified = False
1653
1654 if not filetypes: filetypes= []
1655 filetypeObjects = []
1656
1657 for filemask in filetypes:
1658 fto = FileTypeObject(filemask)
1659
1660 if fto.isAll():
1661 ALL_filetypes_was_specified = True
1662
1663 if fto == initialFileTypeObject:
1664 initialFileTypeObject.add(fto)
1665 else:
1666 filetypeObjects.append(fto)
1667
1668
1669
1670
1671 if ALL_filetypes_was_specified:
1672 pass
1673 elif allFileTypeObject == initialFileTypeObject:
1674 pass
1675 else:
1676 filetypeObjects.insert(0,allFileTypeObject)
1677
1678
1679
1680
1681
1682 if len(filetypeObjects) == 0:
1683 filetypeObjects.append(initialFileTypeObject)
1684
1685 if initialFileTypeObject in (filetypeObjects[0], filetypeObjects[-1]):
1686 pass
1687 else:
1688 if runningPython26:
1689 filetypeObjects.append(initialFileTypeObject)
1690 else:
1691 filetypeObjects.insert(0,initialFileTypeObject)
1692
1693 filetypes = [fto.toTuple() for fto in filetypeObjects]
1694
1695 return initialbase, initialfile, initialdir, filetypes
1696
1697
1698
1699
1700
1701
1709
1710
1739
1740
1741
1742
1743
1744
1746 r"""
1747 A class to support persistent storage.
1748
1749 You can use EgStore to support the storage and retrieval
1750 of user settings for an EasyGui application.
1751
1752
1753 # Example A
1754 #-----------------------------------------------------------------------
1755 # define a class named Settings as a subclass of EgStore
1756 #-----------------------------------------------------------------------
1757 class Settings(EgStore):
1758
1759 def __init__(self, filename): # filename is required
1760 #-------------------------------------------------
1761 # Specify default/initial values for variables that
1762 # this particular application wants to remember.
1763 #-------------------------------------------------
1764 self.userId = ""
1765 self.targetServer = ""
1766
1767 #-------------------------------------------------
1768 # For subclasses of EgStore, these must be
1769 # the last two statements in __init__
1770 #-------------------------------------------------
1771 self.filename = filename # this is required
1772 self.restore() # restore values from the storage file if possible
1773
1774
1775
1776 # Example B
1777 #-----------------------------------------------------------------------
1778 # create settings, a persistent Settings object
1779 #-----------------------------------------------------------------------
1780 settingsFile = "myApp_settings.txt"
1781 settings = Settings(settingsFile)
1782
1783 user = "obama_barak"
1784 server = "whitehouse1"
1785 settings.userId = user
1786 settings.targetServer = server
1787 settings.store() # persist the settings
1788
1789 # run code that gets a new value for userId, and persist the settings
1790 user = "biden_joe"
1791 settings.userId = user
1792 settings.store()
1793
1794
1795 # Example C
1796 #-----------------------------------------------------------------------
1797 # recover the Settings instance, change an attribute, and store it again.
1798 #-----------------------------------------------------------------------
1799 settings = Settings(settingsFile)
1800 settings.userId = "vanrossum_g"
1801 settings.store()
1802
1803 """
1805 raise NotImplementedError()
1806
1808 """
1809 Set the values of whatever attributes are recoverable
1810 from the pickle file.
1811
1812 Populate the attributes (the __dict__) of the EgStore object
1813 from the attributes (the __dict__) of the pickled object.
1814
1815 If the pickled object has attributes that have been initialized
1816 in the EgStore object, then those attributes of the EgStore object
1817 will be replaced by the values of the corresponding attributes
1818 in the pickled object.
1819
1820 If the pickled object is missing some attributes that have
1821 been initialized in the EgStore object, then those attributes
1822 of the EgStore object will retain the values that they were
1823 initialized with.
1824
1825 If the pickled object has some attributes that were not
1826 initialized in the EgStore object, then those attributes
1827 will be ignored.
1828
1829 IN SUMMARY:
1830
1831 After the recover() operation, the EgStore object will have all,
1832 and only, the attributes that it had when it was initialized.
1833
1834 Where possible, those attributes will have values recovered
1835 from the pickled object.
1836 """
1837 if not os.path.exists(self.filename): return self
1838 if not os.path.isfile(self.filename): return self
1839
1840 try:
1841 f = open(self.filename,"rb")
1842 unpickledObject = pickle.load(f)
1843 f.close()
1844
1845 for key in list(self.__dict__.keys()):
1846 default = self.__dict__[key]
1847 self.__dict__[key] = unpickledObject.__dict__.get(key,default)
1848 except:
1849 pass
1850
1851 return self
1852
1854 """
1855 Save the attributes of the EgStore object to a pickle file.
1856 Note that if the directory for the pickle file does not already exist,
1857 the store operation will fail.
1858 """
1859 f = open(self.filename, "wb")
1860 pickle.dump(self, f)
1861 f.close()
1862
1863
1865 """
1866 Delete my persistent file (i.e. pickle file), if it exists.
1867 """
1868 if os.path.isfile(self.filename):
1869 os.remove(self.filename)
1870 return
1871
1873 """
1874 return my contents as a string in an easy-to-read format.
1875 """
1876
1877 longest_key_length = 0
1878 keys = []
1879 for key in self.__dict__.keys():
1880 keys.append(key)
1881 longest_key_length = max(longest_key_length, len(key))
1882
1883 keys.sort()
1884 lines = []
1885 for key in keys:
1886 value = self.__dict__[key]
1887 key = key.ljust(longest_key_length)
1888 lines.append("%s : %s\n" % (key,repr(value)) )
1889 return "".join(lines)
1890
1891
1892
1893
1894
1895
1896
1897
1898
1900 """
1901 Run the EasyGui demo.
1902 """
1903
1904 writeln("\n" * 100)
1905
1906 intro_message = ("Pick the kind of box that you wish to demo.\n"
1907 + "\n * Python version " + sys.version
1908 + "\n * EasyGui version " + egversion
1909 + "\n * Tk version " + str(TkVersion)
1910 )
1911
1912
1913
1914
1915 while 1:
1916 choices = [
1917 "msgbox",
1918 "buttonbox",
1919 "buttonbox(image) -- a buttonbox that displays an image",
1920 "choicebox",
1921 "multchoicebox",
1922 "textbox",
1923 "ynbox",
1924 "ccbox",
1925 "enterbox",
1926 "enterbox(image) -- an enterbox that displays an image",
1927 "exceptionbox",
1928 "codebox",
1929 "integerbox",
1930 "boolbox",
1931 "indexbox",
1932 "filesavebox",
1933 "fileopenbox",
1934 "passwordbox",
1935 "multenterbox",
1936 "multpasswordbox",
1937 "diropenbox",
1938 "About EasyGui",
1939 " Help"
1940 ]
1941 choice = choicebox(msg=intro_message
1942 , title="EasyGui " + egversion
1943 , choices=choices)
1944
1945 if not choice: return
1946
1947 reply = choice.split()
1948
1949 if reply[0] == "msgbox":
1950 reply = msgbox("short msg", "This is a long title")
1951 writeln("Reply was: %s" % repr(reply))
1952
1953 elif reply[0] == "About":
1954 reply = abouteasygui()
1955
1956 elif reply[0] == "Help":
1957 _demo_help()
1958
1959 elif reply[0] == "buttonbox":
1960 reply = buttonbox()
1961 writeln("Reply was: %s" % repr(reply))
1962
1963 title = "Demo of Buttonbox with many, many buttons!"
1964 msg = "This buttonbox shows what happens when you specify too many buttons."
1965 reply = buttonbox(msg=msg, title=title, choices=choices)
1966 writeln("Reply was: %s" % repr(reply))
1967
1968 elif reply[0] == "buttonbox(image)":
1969 _demo_buttonbox_with_image()
1970
1971 elif reply[0] == "boolbox":
1972 reply = boolbox()
1973 writeln("Reply was: %s" % repr(reply))
1974
1975 elif reply[0] == "enterbox":
1976 image = "python_and_check_logo.gif"
1977 message = "Enter the name of your best friend."\
1978 "\n(Result will be stripped.)"
1979 reply = enterbox(message, "Love!", " Suzy Smith ")
1980 writeln("Reply was: %s" % repr(reply))
1981
1982 message = "Enter the name of your best friend."\
1983 "\n(Result will NOT be stripped.)"
1984 reply = enterbox(message, "Love!", " Suzy Smith ",strip=False)
1985 writeln("Reply was: %s" % repr(reply))
1986
1987 reply = enterbox("Enter the name of your worst enemy:", "Hate!")
1988 writeln("Reply was: %s" % repr(reply))
1989
1990 elif reply[0] == "enterbox(image)":
1991 image = "python_and_check_logo.gif"
1992 message = "What kind of snake is this?"
1993 reply = enterbox(message, "Quiz",image=image)
1994 writeln("Reply was: %s" % repr(reply))
1995
1996 elif reply[0] == "exceptionbox":
1997 try:
1998 thisWillCauseADivideByZeroException = 1/0
1999 except:
2000 exceptionbox()
2001
2002 elif reply[0] == "integerbox":
2003 reply = integerbox(
2004 "Enter a number between 3 and 333",
2005 "Demo: integerbox WITH a default value",
2006 222, 3, 333)
2007 writeln("Reply was: %s" % repr(reply))
2008
2009 reply = integerbox(
2010 "Enter a number between 0 and 99",
2011 "Demo: integerbox WITHOUT a default value"
2012 )
2013 writeln("Reply was: %s" % repr(reply))
2014
2015 elif reply[0] == "diropenbox" : _demo_diropenbox()
2016 elif reply[0] == "fileopenbox": _demo_fileopenbox()
2017 elif reply[0] == "filesavebox": _demo_filesavebox()
2018
2019 elif reply[0] == "indexbox":
2020 title = reply[0]
2021 msg = "Demo of " + reply[0]
2022 choices = ["Choice1", "Choice2", "Choice3", "Choice4"]
2023 reply = indexbox(msg, title, choices)
2024 writeln("Reply was: %s" % repr(reply))
2025
2026 elif reply[0] == "passwordbox":
2027 reply = passwordbox("Demo of password box WITHOUT default"
2028 + "\n\nEnter your secret password", "Member Logon")
2029 writeln("Reply was: %s" % str(reply))
2030
2031 reply = passwordbox("Demo of password box WITH default"
2032 + "\n\nEnter your secret password", "Member Logon", "alfie")
2033 writeln("Reply was: %s" % str(reply))
2034
2035 elif reply[0] == "multenterbox":
2036 msg = "Enter your personal information"
2037 title = "Credit Card Application"
2038 fieldNames = ["Name","Street Address","City","State","ZipCode"]
2039 fieldValues = []
2040 fieldValues = multenterbox(msg,title, fieldNames)
2041
2042
2043 while 1:
2044 if fieldValues == None: break
2045 errmsg = ""
2046 for i in range(len(fieldNames)):
2047 if fieldValues[i].strip() == "":
2048 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2049 if errmsg == "": break
2050 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
2051
2052 writeln("Reply was: %s" % str(fieldValues))
2053
2054 elif reply[0] == "multpasswordbox":
2055 msg = "Enter logon information"
2056 title = "Demo of multpasswordbox"
2057 fieldNames = ["Server ID", "User ID", "Password"]
2058 fieldValues = []
2059 fieldValues = multpasswordbox(msg,title, fieldNames)
2060
2061
2062 while 1:
2063 if fieldValues == None: break
2064 errmsg = ""
2065 for i in range(len(fieldNames)):
2066 if fieldValues[i].strip() == "":
2067 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2068 if errmsg == "": break
2069 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
2070
2071 writeln("Reply was: %s" % str(fieldValues))
2072
2073 elif reply[0] == "ynbox":
2074 title = "Demo of ynbox"
2075 msg = "Were you expecting the Spanish Inquisition?"
2076 reply = ynbox(msg, title)
2077 writeln("Reply was: %s" % repr(reply))
2078 if reply:
2079 msgbox("NOBODY expects the Spanish Inquisition!", "Wrong!")
2080
2081 elif reply[0] == "ccbox":
2082 title = "Demo of ccbox"
2083 reply = ccbox(msg,title)
2084 writeln("Reply was: %s" % repr(reply))
2085
2086 elif reply[0] == "choicebox":
2087 title = "Demo of choicebox"
2088 longchoice = "This is an example of a very long option which you may or may not wish to choose."*2
2089 listChoices = ["nnn", "ddd", "eee", "fff", "aaa", longchoice
2090 , "aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk", "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq", "rrr", "sss", "ttt", "uuu", "vvv"]
2091
2092 msg = "Pick something. " + ("A wrapable sentence of text ?! "*30) + "\nA separate line of text."*6
2093 reply = choicebox(msg=msg, choices=listChoices)
2094 writeln("Reply was: %s" % repr(reply))
2095
2096 msg = "Pick something. "
2097 reply = choicebox(msg=msg, title=title, choices=listChoices)
2098 writeln("Reply was: %s" % repr(reply))
2099
2100 msg = "Pick something. "
2101 reply = choicebox(msg="The list of choices is empty!", choices=[])
2102 writeln("Reply was: %s" % repr(reply))
2103
2104 elif reply[0] == "multchoicebox":
2105 listChoices = ["aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk"
2106 , "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq"
2107 , "rrr", "sss", "ttt", "uuu", "vvv"]
2108
2109 msg = "Pick as many choices as you wish."
2110 reply = multchoicebox(msg,"Demo of multchoicebox", listChoices)
2111 writeln("Reply was: %s" % repr(reply))
2112
2113 elif reply[0] == "textbox": _demo_textbox(reply[0])
2114 elif reply[0] == "codebox": _demo_codebox(reply[0])
2115
2116 else:
2117 msgbox("Choice\n\n" + choice + "\n\nis not recognized", "Program Logic Error")
2118 return
2119
2120
2121 -def _demo_textbox(reply):
2122 text_snippet = ((\
2123 """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! """ \
2124 *5)+"\n\n")*10
2125 title = "Demo of textbox"
2126 msg = "Here is some sample text. " * 16
2127 reply = textbox(msg, "Text Sample", text_snippet)
2128 writeln("Reply was: %s" % str(reply))
2129
2131 code_snippet = ("dafsdfa dasflkj pp[oadsij asdfp;ij asdfpjkop asdfpok asdfpok asdfpok"*3) +"\n"+\
2132 """# here is some dummy Python code
2133 for someItem in myListOfStuff:
2134 do something(someItem)
2135 do something()
2136 do something()
2137 if somethingElse(someItem):
2138 doSomethingEvenMoreInteresting()
2139
2140 """*16
2141 msg = "Here is some sample code. " * 16
2142 reply = codebox(msg, "Code Sample", code_snippet)
2143 writeln("Reply was: %s" % repr(reply))
2144
2145
2166
2167
2169 savedStdout = sys.stdout
2170 sys.stdout = capturedOutput = StringIO()
2171 help("easygui")
2172 sys.stdout = savedStdout
2173 codebox("EasyGui Help",text=capturedOutput.getvalue())
2174
2176 filename = "myNewFile.txt"
2177 title = "File SaveAs"
2178 msg ="Save file as:"
2179
2180 f = filesavebox(msg,title,default=filename)
2181 writeln("You chose to save file: %s" % f)
2182
2184 title = "Demo of diropenbox"
2185 msg = "Pick the directory that you wish to open."
2186 d = diropenbox(msg, title)
2187 writeln("You chose directory...: %s" % d)
2188
2189 d = diropenbox(msg, title,default="./")
2190 writeln("You chose directory...: %s" % d)
2191
2192 d = diropenbox(msg, title,default="c:/")
2193 writeln("You chose directory...: %s" % d)
2194
2195
2197 msg = "Python files"
2198 title = "Open files"
2199 default="*.py"
2200 f = fileopenbox(msg,title,default=default)
2201 writeln("You chose to open file: %s" % f)
2202
2203 default="./*.gif"
2204 filetypes = ["*.jpg",["*.zip","*.tgs","*.gz", "Archive files"],["*.htm", "*.html","HTML files"]]
2205 f = fileopenbox(msg,title,default=default,filetypes=filetypes)
2206 writeln("You chose to open file: %s" % f)
2207
2208 """#deadcode -- testing ----------------------------------------
2209 f = fileopenbox(None,None,default=default)
2210 writeln("You chose to open file: %s" % f)
2211
2212 f = fileopenbox(None,title,default=default)
2213 writeln("You chose to open file: %s" % f)
2214
2215 f = fileopenbox(msg,None,default=default)
2216 writeln("You chose to open file: %s" % f)
2217
2218 f = fileopenbox(default=default)
2219 writeln("You chose to open file: %s" % f)
2220
2221 f = fileopenbox(default=None)
2222 writeln("You chose to open file: %s" % f)
2223 #----------------------------------------------------deadcode """
2224
2225
2228
2229 EASYGUI_ABOUT_INFORMATION = '''
2230 ========================================================================
2231 0.94(2010-06-06)
2232 ========================================================================
2233
2234 ENHANCEMENTS
2235 ------------------------------------------------------
2236
2237 * The codebox and textbox functions now return the contents of the box, rather
2238 than simply the name of the button ("Yes"). This makes it possible to use
2239 codebox and textbox as data-entry widgets. A big "thank you!" to Dominic
2240 Comtois for requesting this feature, patiently explaining his requirement,
2241 and helping to discover the tkinter techniques to implement it.
2242
2243 NOTE THAT in theory this change breaks backward compatibility. But because
2244 (in previous versions of EasyGui) the value returned by codebox and textbox
2245 was meaningless, no application should have been checking it. So in actual
2246 practice, this change should not break backward compatibility.
2247
2248 * Added support for SPACEBAR to command buttons. Now, when keyboard
2249 focus is on a command button, a press of the SPACEBAR will act like
2250 a press of the ENTER key; it will activate the command button.
2251
2252 * Added support for keyboard navigation with the arrow keys (up,down,left,right)
2253 to the fields and buttons in enterbox, multenterbox and multpasswordbox,
2254 and to the buttons in choicebox and all buttonboxes.
2255
2256 * added highlightthickness=2 to entry fields in multenterbox and
2257 multpasswordbox. Now it is easier to tell which entry field has
2258 keyboard focus.
2259
2260
2261 BUG FIXES
2262 ------------------------------------------------------
2263 * In EgStore, the pickle file is now opened with "rb" and "wb" rather than
2264 with "r" and "w". This change is necessary for compatibility with Python 3+.
2265 Thanks to Marshall Mattingly for reporting this problem and providing the fix.
2266
2267 * In integerbox, the actual argument names did not match the names described
2268 in the docstring. Thanks to Daniel Zingaro of at University of Toronto for
2269 reporting this problem.
2270
2271 * In integerbox, the "argLowerBound" and "argUpperBound" arguments have been
2272 renamed to "lowerbound" and "upperbound" and the docstring has been corrected.
2273
2274 NOTE THAT THIS CHANGE TO THE ARGUMENT-NAMES BREAKS BACKWARD COMPATIBILITY.
2275 If argLowerBound or argUpperBound are used, an AssertionError with an
2276 explanatory error message is raised.
2277
2278 * In choicebox, the signature to choicebox incorrectly showed choicebox as
2279 accepting a "buttons" argument. The signature has been fixed.
2280
2281
2282 ========================================================================
2283 0.93(2009-07-07)
2284 ========================================================================
2285
2286 ENHANCEMENTS
2287 ------------------------------------------------------
2288
2289 * Added exceptionbox to display stack trace of exceptions
2290 * modified names of some font-related constants to make it
2291 easier to customize them
2292
2293
2294 ========================================================================
2295 0.92(2009-06-22)
2296 ========================================================================
2297
2298 ENHANCEMENTS
2299 ------------------------------------------------------
2300
2301 * Added EgStore class to to provide basic easy-to-use persistence.
2302
2303 BUG FIXES
2304 ------------------------------------------------------
2305
2306 * Fixed a bug that was preventing Linux users from copying text out of
2307 a textbox and a codebox. This was not a problem for Windows users.
2308
2309
2310 ========================================================================
2311 version: 0.91(2009-04-25)
2312 ========================================================================
2313
2314 ENHANCEMENTS
2315 ------------------------------------------------------
2316
2317 * exposed egdemo(). Now you can write a program like this:
2318
2319 from easygui import *
2320 egdemo() # run the easygui demo
2321
2322
2323 BUG FIXES
2324 ------------------------------------------------------
2325
2326 * Removed some fileopenbox test code that was accidentally left in,
2327 causing egdemo to crash.
2328
2329 * Made EasyGui "version 2.5 aware" so that it will use the correct
2330 default filemask in fileopenbox and filesavebox under Python 2.5
2331 and earlier.
2332
2333 * fixed bugs in filesavebox by converting it to use the same code as
2334 fileopenbox for handling the "default" and "filetypes" args.
2335
2336
2337 ========================================================================
2338 version: 0.90(2009-04-20)
2339 ========================================================================
2340
2341 ENHANCEMENTS
2342 ------------------------------------------------------
2343
2344 * Enhanced fileopenbox to allow it to accept multiple
2345 extensions for a single file type, e.g.:
2346 ... filetype = [[".htm",".html", "HTML filetypes"]]
2347 Previously it accepted only one.
2348
2349
2350 BUG FIXES
2351 ------------------------------------------------------
2352
2353 * Fixed a bug in fileopenbox (it was not displaying initialfile).
2354
2355 Thanks to Matthias Meier in the lovely city of Schwerin, Germany
2356 (http://en.wikipedia.org/wiki/Schwerin) for reporting the problem
2357 and supplying the basic elements of the fix.
2358
2359 * In fileopenbox, removed acceptance of a 1-tuple as a filetype.
2360
2361 This was an unnecessary and confusing feature. In theory,
2362 this change will break backward compatibility ... but
2363 there are probably no programs that used this feature.
2364
2365
2366 NOTES
2367 ----------------------------------------------------------
2368
2369 In some cases (e.g. ... default="junk.txt")
2370 there are minor differences in the display of fileopenbox
2371 between Python version 2.5 on the one hand,
2372 and Python version 2.6 and higher on the other.
2373 Python versions 2.6+ behave correctly.
2374
2375
2376 ========================================================================
2377 version 0.89(2009-04-09)
2378 ========================================================================
2379
2380 BUG FIX
2381 ------------------------------------------------------
2382 * An enhancement in version 0.88 (which added an import of StringIO)
2383 broke compatibility with Python 3.
2384
2385 "In Python 3, the StringIO and cStringIO modules are gone.
2386 Instead, import the io module and use io.StringIO or io.BytesIO
2387 for text and data respectively."
2388
2389 Many thanks to Alan Gauld, author of the "Learn to Program" web site
2390 (http://www.alan-g.me.uk/l2p) for reporting this problem and
2391 supplying the fix.
2392
2393 ========================================================================
2394 version 0.88(2009-04-06)
2395 ========================================================================
2396
2397 ENHANCEMENTS
2398 ------------------------------------------------------
2399 * Support for display of images has been extended to
2400 the various flavors of enterbox. An example of
2401 an enterbox with an image was added to the EasyGui demo.
2402
2403 MODIFICATIONS
2404 ------------------------------------------------------
2405 * In EasyGui demo, HELP output is now captured and displayed
2406 in a codebox rather than being written to stdout.
2407
2408 BUG FIX
2409 ------------------------------------------------------
2410 * Fixed a bug in fileopenbox.
2411 Thanks to Dennis A. Wolf for both the bug report and the fix.
2412
2413
2414 ========================================================================
2415 version 0.87(2009-03-18)
2416 ========================================================================
2417
2418 ENHANCEMENTS
2419 ------------------------------------------------------
2420 * EasyGui will now run under Python version 3.*
2421 as well as version 2.*
2422
2423 BUG FIXES
2424 ------------------------------------------------------
2425 * added support for "root" argument to msgbox, buttonbox, and
2426 enterbox. This feature is not yet documented or very well
2427 tested -- it should be considered experimental/beta.
2428 '''
2429
2436
2437
2438
2439 if __name__ == '__main__':
2440 if True:
2441 egdemo()
2442 else:
2443
2444 root = Tk()
2445 msg = """This is a test of a main Tk() window in which we will place an easygui msgbox.
2446 It will be an interesting experiment.\n\n"""
2447 messageWidget = Message(root, text=msg, width=1000)
2448 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2449 messageWidget = Message(root, text=msg, width=1000)
2450 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2451
2452
2453 msgbox("this is a test of passing in boxRoot", root=root)
2454 msgbox("this is a second test of passing in boxRoot", root=root)
2455
2456 reply = enterbox("Enter something", root=root)
2457 writeln("You wrote:", reply)
2458
2459 reply = enterbox("Enter something else", root=root)
2460 writeln("You wrote:", reply)
2461 root.destroy()
2462