1 """
2 @version: 0.93(2009-07-07)
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 the unpredictable results. If you find that you have
17 problems, try running your 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
93 if TkVersion < 8.0 :
94 stars = "*"*75
95 writeln("""\n\n\n""" + stars + """
96 You are running Tk version: """ + str(TkVersion) + """
97 You must be using Tk version 8.0 or greater to use EasyGui.
98 Terminating.
99 """ + stars + """\n\n\n""")
100 sys.exit(0)
101
104
105 rootWindowPosition = "+300+200"
106
107 PROPORTIONAL_FONT_FAMILY = ("MS", "Sans", "Serif")
108 MONOSPACE_FONT_FAMILY = ("Courier")
109
110 PROPORTIONAL_FONT_SIZE = 10
111 MONOSPACE_FONT_SIZE = 9
112 TEXT_ENTRY_FONT_SIZE = 12
113
114
115
116 __choiceboxMultipleSelect = None
117 __widgetTexts = None
118 __replyButtonText = None
119 __choiceboxResults = None
120 __firstWidget = None
121 __enterboxText = None
122 __enterboxDefaultText=""
123 __multenterboxText = ""
124 choiceboxChoices = None
125 choiceboxWidget = None
126 entryWidget = None
127 boxRoot = None
128 ImageErrorMsg = (
129 "\n\n---------------------------------------------\n"
130 "Error: %s\n%s")
131
132
133
134
135
136
137
138 -def ynbox(msg="Shall I continue?"
139 , title=" "
140 , choices=("Yes", "No")
141 , image=None
142 ):
143 """
144 Display a msgbox with choices of Yes and No.
145
146 The default is "Yes".
147
148 The returned value is calculated this way::
149 if the first choice ("Yes") is chosen, or if the dialog is cancelled:
150 return 1
151 else:
152 return 0
153
154 If invoked without a msg argument, displays a generic request for a confirmation
155 that the user wishes to continue. So it can be used this way::
156 if ynbox(): pass # continue
157 else: sys.exit(0) # exit the program
158
159 @arg msg: the msg to be displayed.
160 @arg title: the window title
161 @arg choices: a list or tuple of the choices to be displayed
162 """
163 return boolbox(msg, title, choices, image=image)
164
165
166
167
168
169 -def ccbox(msg="Shall I continue?"
170 , title=" "
171 , choices=("Continue", "Cancel")
172 , image=None
173 ):
174 """
175 Display a msgbox with choices of Continue and Cancel.
176
177 The default is "Continue".
178
179 The returned value is calculated this way::
180 if the first choice ("Continue") is chosen, or if the dialog is cancelled:
181 return 1
182 else:
183 return 0
184
185 If invoked without a msg argument, displays a generic request for a confirmation
186 that the user wishes to continue. So it can be used this way::
187
188 if ccbox():
189 pass # continue
190 else:
191 sys.exit(0) # exit the program
192
193 @arg msg: the msg to be displayed.
194 @arg title: the window title
195 @arg choices: a list or tuple of the choices to be displayed
196 """
197 return boolbox(msg, title, choices, image=image)
198
199
200
201
202
203 -def boolbox(msg="Shall I continue?"
204 , title=" "
205 , choices=("Yes","No")
206 , image=None
207 ):
208 """
209 Display a boolean msgbox.
210
211 The default is the first choice.
212
213 The returned value is calculated this way::
214 if the first choice is chosen, or if the dialog is cancelled:
215 returns 1
216 else:
217 returns 0
218 """
219 reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
220 if reply == choices[0]: return 1
221 else: return 0
222
223
224
225
226
227 -def indexbox(msg="Shall I continue?"
228 , title=" "
229 , choices=("Yes","No")
230 , image=None
231 ):
232 """
233 Display a buttonbox with the specified choices.
234 Return the index of the choice selected.
235 """
236 reply = buttonbox(msg=msg, choices=choices, title=title, image=image)
237 index = -1
238 for choice in choices:
239 index = index + 1
240 if reply == choice: return index
241 raise AssertionError(
242 "There is a program logic error in the EasyGui code for indexbox.")
243
244
245
246
247
248 -def msgbox(msg="(Your message goes here)", title=" ", ok_button="OK",image=None,root=None):
249 """
250 Display a messagebox
251 """
252 if type(ok_button) != type("OK"):
253 raise AssertionError("The 'ok_button' argument to msgbox must be a string.")
254
255 return buttonbox(msg=msg, title=title, choices=[ok_button], image=image,root=root)
256
257
258
259
260
341
342
343
344
345
346 -def integerbox(msg=""
347 , title=" "
348 , default=""
349 , argLowerBound=0
350 , argUpperBound=99
351 , image = None
352 , root = None
353 ):
354 """
355 Show a box in which a user can enter an integer.
356
357 In addition to arguments for msg and title, this function accepts
358 integer arguments for default_value, lowerbound, and upperbound.
359
360 The default_value argument may be None.
361
362 When the user enters some text, the text is checked to verify
363 that it can be converted to an integer between the lowerbound and upperbound.
364
365 If it can be, the integer (not the text) is returned.
366
367 If it cannot, then an error msg is displayed, and the integerbox is
368 redisplayed.
369
370 If the user cancels the operation, None is returned.
371 """
372 if default != "":
373 if type(default) != type(1):
374 raise AssertionError(
375 "integerbox received a non-integer value for "
376 + "default of " + dq(str(default)) , "Error")
377
378 if type(argLowerBound) != type(1):
379 raise AssertionError(
380 "integerbox received a non-integer value for "
381 + "argLowerBound of " + dq(str(argLowerBound)) , "Error")
382
383 if type(argUpperBound) != type(1):
384 raise AssertionError(
385 "integerbox received a non-integer value for "
386 + "argUpperBound of " + dq(str(argUpperBound)) , "Error")
387
388 if msg == "":
389 msg = ("Enter an integer between " + str(argLowerBound)
390 + " and "
391 + str(argUpperBound)
392 )
393
394 while 1:
395 reply = enterbox(msg, title, str(default), image=image, root=root)
396 if reply == None: return None
397
398 try:
399 reply = int(reply)
400 except:
401 msgbox ("The value that you entered:\n\t%s\nis not an integer." % dq(str(reply))
402 , "Error")
403 continue
404
405 if reply < argLowerBound:
406 msgbox ("The value that you entered is less than the lower bound of "
407 + str(argLowerBound) + ".", "Error")
408 continue
409
410 if reply > argUpperBound:
411 msgbox ("The value that you entered is greater than the upper bound of "
412 + str(argUpperBound) + ".", "Error")
413 continue
414
415
416
417 return reply
418
419
420
421
422 -def multenterbox(msg="Fill in values for the fields."
423 , title=" "
424 , fields=()
425 , values=()
426 ):
427 r"""
428 Show screen with multiple data entry fields.
429
430 If there are fewer values than names, the list of values is padded with
431 empty strings until the number of values is the same as the number of names.
432
433 If there are more values than names, the list of values
434 is truncated so that there are as many values as names.
435
436 Returns a list of the values of the fields,
437 or None if the user cancels the operation.
438
439 Here is some example code, that shows how values returned from
440 multenterbox can be checked for validity before they are accepted::
441 ----------------------------------------------------------------------
442 msg = "Enter your personal information"
443 title = "Credit Card Application"
444 fieldNames = ["Name","Street Address","City","State","ZipCode"]
445 fieldValues = [] # we start with blanks for the values
446 fieldValues = multenterbox(msg,title, fieldNames)
447
448 # make sure that none of the fields was left blank
449 while 1:
450 if fieldValues == None: break
451 errmsg = ""
452 for i in range(len(fieldNames)):
453 if fieldValues[i].strip() == "":
454 errmsg += ('"%s" is a required field.\n\n' % fieldNames[i])
455 if errmsg == "":
456 break # no problems found
457 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
458
459 writeln("Reply was: %s" % str(fieldValues))
460 ----------------------------------------------------------------------
461
462 @arg msg: the msg to be displayed.
463 @arg title: the window title
464 @arg fields: a list of fieldnames.
465 @arg values: a list of field values
466 """
467 return __multfillablebox(msg,title,fields,values,None)
468
469
470
471
472
473 -def multpasswordbox(msg="Fill in values for the fields."
474 , title=" "
475 , fields=tuple()
476 ,values=tuple()
477 ):
478 r"""
479 Same interface as multenterbox. But in multpassword box,
480 the last of the fields is assumed to be a password, and
481 is masked with asterisks.
482
483 Example
484 =======
485
486 Here is some example code, that shows how values returned from
487 multpasswordbox can be checked for validity before they are accepted::
488 msg = "Enter logon information"
489 title = "Demo of multpasswordbox"
490 fieldNames = ["Server ID", "User ID", "Password"]
491 fieldValues = [] # we start with blanks for the values
492 fieldValues = multpasswordbox(msg,title, fieldNames)
493
494 # make sure that none of the fields was left blank
495 while 1:
496 if fieldValues == None: break
497 errmsg = ""
498 for i in range(len(fieldNames)):
499 if fieldValues[i].strip() == "":
500 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
501 if errmsg == "": break # no problems found
502 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
503
504 writeln("Reply was: %s" % str(fieldValues))
505 """
506 return __multfillablebox(msg,title,fields,values,"*")
507
508
509
510
511 -def __multfillablebox(msg="Fill in values for the fields."
512 , title=" "
513 , fields=()
514 , values=()
515 , mask = None
516 ):
517 global boxRoot, __multenterboxText, __multenterboxDefaultText, cancelButton, entryWidget, okButton
518
519 choices = ["OK", "Cancel"]
520 if len(fields) == 0: return None
521
522 fields = list(fields[:])
523 values = list(values[:])
524
525 if len(values) == len(fields): pass
526 elif len(values) > len(fields):
527 fields = fields[0:len(values)]
528 else:
529 while len(values) < len(fields):
530 values.append("")
531
532 boxRoot = Tk()
533 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
534 boxRoot.title(title)
535 boxRoot.iconname('Dialog')
536 boxRoot.geometry(rootWindowPosition)
537 boxRoot.bind("<Escape>", __multenterboxCancel)
538
539
540 messageFrame = Frame(master=boxRoot)
541 messageFrame.pack(side=TOP, fill=BOTH)
542
543
544 messageWidget = Message(messageFrame, width="4.5i", text=msg)
545 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
546 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
547
548 global entryWidgets
549 entryWidgets = []
550
551 lastWidgetIndex = len(fields) - 1
552
553 for widgetIndex in range(len(fields)):
554 argFieldName = fields[widgetIndex]
555 argFieldValue = values[widgetIndex]
556 entryFrame = Frame(master=boxRoot)
557 entryFrame.pack(side=TOP, fill=BOTH)
558
559
560 labelWidget = Label(entryFrame, text=argFieldName)
561 labelWidget.pack(side=LEFT)
562
563 entryWidgets.append(Entry(entryFrame, width=40))
564 entryWidgets[widgetIndex].configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
565 entryWidgets[widgetIndex].pack(side=RIGHT, padx="3m")
566 entryWidgets[widgetIndex].bind("<Return>", __multenterboxGetText)
567 entryWidgets[widgetIndex].bind("<Escape>", __multenterboxCancel)
568
569
570
571 if widgetIndex == lastWidgetIndex:
572 if mask:
573 entryWidgets[widgetIndex].configure(show=mask)
574
575
576 entryWidgets[widgetIndex].insert(0,argFieldValue)
577 widgetIndex += 1
578
579
580 buttonsFrame = Frame(master=boxRoot)
581 buttonsFrame.pack(side=BOTTOM, fill=BOTH)
582
583 okButton = Button(buttonsFrame, takefocus=1, text="OK")
584 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
585 okButton.bind("<Return>" , __multenterboxGetText)
586 okButton.bind("<Button-1>", __multenterboxGetText)
587
588
589 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
590 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
591 cancelButton.bind("<Return>" , __multenterboxCancel)
592 cancelButton.bind("<Button-1>", __multenterboxCancel)
593
594
595 entryWidgets[0].focus_force()
596 boxRoot.mainloop()
597
598
599 boxRoot.destroy()
600 return __multenterboxText
601
602
603
604
606 global __multenterboxText
607
608 __multenterboxText = []
609 for entryWidget in entryWidgets:
610 __multenterboxText.append(entryWidget.get())
611 boxRoot.quit()
612
613
618
619
620
621
622
623 -def enterbox(msg="Enter something."
624 , title=" "
625 , default=""
626 , strip=True
627 , image=None
628 , root=None
629 ):
630 """
631 Show a box in which a user can enter some text.
632
633 You may optionally specify some default text, which will appear in the
634 enterbox when it is displayed.
635
636 Returns the text that the user entered, or None if he cancels the operation.
637
638 By default, enterbox strips its result (i.e. removes leading and trailing
639 whitespace). (If you want it not to strip, use keyword argument: strip=False.)
640 This makes it easier to test the results of the call::
641
642 reply = enterbox(....)
643 if reply:
644 ...
645 else:
646 ...
647 """
648 result = __fillablebox(msg, title, default=default, mask=None,image=image,root=root)
649 if result and strip:
650 result = result.strip()
651 return result
652
653
654 -def passwordbox(msg="Enter your password."
655 , title=" "
656 , default=""
657 , image=None
658 , root=None
659 ):
660 """
661 Show a box in which a user can enter a password.
662 The text is masked with asterisks, so the password is not displayed.
663 Returns the text that the user entered, or None if he cancels the operation.
664 """
665 return __fillablebox(msg, title, default, mask="*",image=image,root=root)
666
667
668 -def __fillablebox(msg
669 , title=""
670 , default=""
671 , mask=None
672 , image=None
673 , root=None
674 ):
675 """
676 Show a box in which a user can enter some text.
677 You may optionally specify some default text, which will appear in the
678 enterbox when it is displayed.
679 Returns the text that the user entered, or None if he cancels the operation.
680 """
681
682 global boxRoot, __enterboxText, __enterboxDefaultText
683 global cancelButton, entryWidget, okButton
684
685 if title == None: title == ""
686 if default == None: default = ""
687 __enterboxDefaultText = default
688 __enterboxText = __enterboxDefaultText
689
690 if root:
691 root.withdraw()
692 boxRoot = Toplevel(master=root)
693 boxRoot.withdraw()
694 else:
695 boxRoot = Tk()
696 boxRoot.withdraw()
697
698 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
699 boxRoot.title(title)
700 boxRoot.iconname('Dialog')
701 boxRoot.geometry(rootWindowPosition)
702 boxRoot.bind("<Escape>", __enterboxCancel)
703
704 if image:
705 image = os.path.normpath(image)
706 junk,ext = os.path.splitext(image)
707 if ext.lower() == ".gif":
708 if os.path.exists(image):
709 pass
710 else:
711 msg += ImageErrorMsg % (image, "Image file not found.")
712 image = None
713 else:
714 msg += ImageErrorMsg % (image, "Image file is not a .gif file.")
715 image = None
716
717 messageFrame = Frame(master=boxRoot)
718 messageFrame.pack(side=TOP, fill=BOTH)
719
720
721 if image:
722 imageFrame = Frame(master=boxRoot)
723 imageFrame.pack(side=TOP, fill=BOTH)
724 image = PhotoImage(file=image)
725 label = Label(imageFrame,image=image)
726 label.image = image
727 label.pack(side=TOP, expand=YES, fill=X, padx='1m', pady='1m')
728
729
730 entryFrame = Frame(master=boxRoot)
731 entryFrame.pack(side=TOP, fill=BOTH)
732
733
734 buttonsFrame = Frame(master=boxRoot)
735 buttonsFrame.pack(side=TOP, fill=BOTH)
736
737
738 messageWidget = Message(messageFrame, width="4.5i", text=msg)
739 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
740 messageWidget.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
741
742
743 entryWidget = Entry(entryFrame, width=40)
744 entryWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,TEXT_ENTRY_FONT_SIZE))
745 if mask:
746 entryWidget.configure(show=mask)
747 entryWidget.pack(side=LEFT, padx="3m")
748 entryWidget.bind("<Return>", __enterboxGetText)
749 entryWidget.bind("<Escape>", __enterboxCancel)
750
751 entryWidget.insert(0,__enterboxDefaultText)
752
753
754 okButton = Button(buttonsFrame, takefocus=1, text="OK")
755 okButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
756 okButton.bind("<Return>" , __enterboxGetText)
757 okButton.bind("<Button-1>", __enterboxGetText)
758
759 ''' dead code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
760 # ------------------ (possible) restore button -------------------------------
761 if default != None:
762 # make a button to restore the default text
763 restoreButton = Button(buttonsFrame, takefocus=1, text="Restore default")
764 restoreButton.pack(expand=1, side=LEFT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
765 restoreButton.bind("<Return>" , __enterboxRestore)
766 restoreButton.bind("<Button-1>", __enterboxRestore)
767 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dead code '''
768
769
770 cancelButton = Button(buttonsFrame, takefocus=1, text="Cancel")
771 cancelButton.pack(expand=1, side=RIGHT, padx='3m', pady='3m', ipadx='2m', ipady='1m')
772 cancelButton.bind("<Return>" , __enterboxCancel)
773 cancelButton.bind("<Button-1>", __enterboxCancel)
774
775
776 entryWidget.focus_force()
777 boxRoot.deiconify()
778 boxRoot.mainloop()
779
780
781 if root: root.deiconify()
782 boxRoot.destroy()
783 return __enterboxText
784
785
790
791
796
797
802
804 """ don't allow WindowManager close
805 """
806 x = Tk()
807 x.withdraw()
808 x.bell()
809 x.destroy()
810
811
812
813
814
815
816 -def multchoicebox(msg="Pick as many items as you like."
817 , title=" "
818 , choices=()
819 , **kwargs
820 ):
821 """
822 Present the user with a list of choices.
823 allow him to select multiple items and return them in a list.
824 if the user doesn't choose anything from the list, return the empty list.
825 return None if he cancelled selection.
826
827 @arg msg: the msg to be displayed.
828 @arg title: the window title
829 @arg choices: a list or tuple of the choices to be displayed
830 """
831 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
832
833 global __choiceboxMultipleSelect
834 __choiceboxMultipleSelect = 1
835 return __choicebox(msg, title, choices)
836
837
838
839
840
841 -def choicebox(msg="Pick something."
842 , title=" "
843 , choices=()
844 , buttons=()
845 ):
846 """
847 Present the user with a list of choices.
848 return the choice that he selects.
849 return None if he cancels the selection selection.
850
851 @arg msg: the msg to be displayed.
852 @arg title: the window title
853 @arg choices: a list or tuple of the choices to be displayed
854 """
855 if len(choices) == 0: choices = ["Program logic error - no choices were specified."]
856
857 global __choiceboxMultipleSelect
858 __choiceboxMultipleSelect = 0
859 return __choicebox(msg,title,choices,buttons)
860
861
862
863
864
865 -def __choicebox(msg
866 , title
867 , choices
868 , buttons=()
869 ):
870 """
871 internal routine to support choicebox() and multchoicebox()
872 """
873 global boxRoot, __choiceboxResults, choiceboxWidget, defaultText
874 global choiceboxWidget, choiceboxChoices
875
876
877
878
879
880
881 choices = list(choices[:])
882 if len(choices) == 0:
883 choices = ["Program logic error - no choices were specified."]
884 defaultButtons = ["OK", "Cancel"]
885
886
887 for index in range(len(choices)):
888 choices[index] = str(choices[index])
889
890 if buttons:
891 if type(buttons) == type("abc"):
892 choiceboxButtons = [buttons]
893 else:
894 choiceboxButtons = buttons
895 else:
896 choiceboxButtons = defaultButtons
897
898 lines_to_show = min(len(choices), 20)
899 lines_to_show = 20
900
901 if title == None: title = ""
902
903
904
905 __choiceboxResults = None
906
907 boxRoot = Tk()
908 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
909 screen_width = boxRoot.winfo_screenwidth()
910 screen_height = boxRoot.winfo_screenheight()
911 root_width = int((screen_width * 0.8))
912 root_height = int((screen_height * 0.5))
913 root_xpos = int((screen_width * 0.1))
914 root_ypos = int((screen_height * 0.05))
915
916 boxRoot.title(title)
917 boxRoot.iconname('Dialog')
918 rootWindowPosition = "+0+0"
919 boxRoot.geometry(rootWindowPosition)
920 boxRoot.expand=NO
921 boxRoot.minsize(root_width, root_height)
922 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
923 boxRoot.geometry(rootWindowPosition)
924
925
926 message_and_buttonsFrame = Frame(master=boxRoot)
927 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
928
929 messageFrame = Frame(message_and_buttonsFrame)
930 messageFrame.pack(side=LEFT, fill=X, expand=YES)
931
932
933 buttonsFrame = Frame(message_and_buttonsFrame)
934 buttonsFrame.pack(side=RIGHT, expand=NO, pady=0)
935
936
937 choiceboxFrame = Frame(master=boxRoot)
938 choiceboxFrame.pack(side=BOTTOM, fill=BOTH, expand=YES)
939
940
941
942
943 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
944 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
945 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
946
947
948 choiceboxWidget = Listbox(choiceboxFrame
949 , height=lines_to_show
950 , borderwidth="1m"
951 , relief="flat"
952 , bg="white"
953 )
954
955 if __choiceboxMultipleSelect:
956 choiceboxWidget.configure(selectmode=MULTIPLE)
957
958 choiceboxWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
959
960
961 rightScrollbar = Scrollbar(choiceboxFrame, orient=VERTICAL, command=choiceboxWidget.yview)
962 choiceboxWidget.configure(yscrollcommand = rightScrollbar.set)
963
964
965 bottomScrollbar = Scrollbar(choiceboxFrame, orient=HORIZONTAL, command=choiceboxWidget.xview)
966 choiceboxWidget.configure(xscrollcommand = bottomScrollbar.set)
967
968
969
970
971
972 bottomScrollbar.pack(side=BOTTOM, fill = X)
973 rightScrollbar.pack(side=RIGHT, fill = Y)
974
975 choiceboxWidget.pack(side=LEFT, padx="1m", pady="1m", expand=YES, fill=BOTH)
976
977
978
979
980
981
982 for index in range(len(choices)):
983 choices[index] == str(choices[index])
984
985 if runningPython3:
986 choices.sort(key=str.lower)
987 else:
988 choices.sort( lambda x,y: cmp(x.lower(), y.lower()))
989
990 lastInserted = None
991 choiceboxChoices = []
992 for choice in choices:
993 if choice == lastInserted: pass
994 else:
995 choiceboxWidget.insert(END, choice)
996 choiceboxChoices.append(choice)
997 lastInserted = choice
998
999 boxRoot.bind('<Any-Key>', KeyboardListener)
1000
1001
1002 if len(choices) > 0:
1003 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1004 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1005 okButton.bind("<Return>", __choiceboxGetChoice)
1006 okButton.bind("<Button-1>",__choiceboxGetChoice)
1007
1008
1009 choiceboxWidget.bind("<Return>", __choiceboxGetChoice)
1010 choiceboxWidget.bind("<Double-Button-1>", __choiceboxGetChoice)
1011 else:
1012
1013 choiceboxWidget.bind("<Return>", __choiceboxCancel)
1014 choiceboxWidget.bind("<Double-Button-1>", __choiceboxCancel)
1015
1016 cancelButton = Button(buttonsFrame, takefocus=YES, text="Cancel", height=1, width=6)
1017 cancelButton.pack(expand=NO, side=BOTTOM, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1018 cancelButton.bind("<Return>", __choiceboxCancel)
1019 cancelButton.bind("<Button-1>", __choiceboxCancel)
1020
1021
1022 if len(choices) > 0 and __choiceboxMultipleSelect:
1023 selectionButtonsFrame = Frame(messageFrame)
1024 selectionButtonsFrame.pack(side=RIGHT, fill=Y, expand=NO)
1025
1026 selectAllButton = Button(selectionButtonsFrame, text="Select All", height=1, width=6)
1027 selectAllButton.bind("<Button-1>",__choiceboxSelectAll)
1028 selectAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1029
1030 clearAllButton = Button(selectionButtonsFrame, text="Clear All", height=1, width=6)
1031 clearAllButton.bind("<Button-1>",__choiceboxClearAll)
1032 clearAllButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1033
1034
1035
1036 boxRoot.bind("<Escape>", __choiceboxCancel)
1037
1038
1039
1040 choiceboxWidget.select_set(0)
1041 choiceboxWidget.focus_force()
1042
1043
1044 boxRoot.mainloop()
1045
1046 boxRoot.destroy()
1047 return __choiceboxResults
1048
1049
1063
1064
1068
1072
1073
1074
1080
1081
1130
1131
1132
1133
1143
1144
1145
1146
1148 """
1149 Display a box that gives information about
1150 an exception that has just been raised.
1151
1152 The caller may optionally pass in a title for the window, or a
1153 msg to accompany the error information.
1154
1155 Note that you do not need to (and cannot) pass an exception object
1156 as an argument. The latest exception will automatically be used.
1157 """
1158 if title == None: title = "Error Report"
1159 if msg == None:
1160 msg = "An error (exception) has occurred in the program."
1161
1162 codebox(msg, title, exception_format())
1163
1164
1165
1166
1167
1168 -def codebox(msg=""
1169 , title=" "
1170 , text=""
1171 ):
1172 """
1173 Display some text in a monospaced font, with no line wrapping.
1174 This function is suitable for displaying code and text that is
1175 formatted using spaces.
1176
1177 The text parameter should be a string, or a list or tuple of lines to be
1178 displayed in the textbox.
1179 """
1180 textbox(msg, title, text, codebox=1 )
1181
1182
1183
1184
1185 -def textbox(msg=""
1186 , title=" "
1187 , text=""
1188 , codebox=0
1189 ):
1190 """
1191 Display some text in a proportional font with line wrapping at word breaks.
1192 This function is suitable for displaying general written text.
1193
1194 The text parameter should be a string, or a list or tuple of lines to be
1195 displayed in the textbox.
1196 """
1197
1198 if msg == None: msg = ""
1199 if title == None: title = ""
1200
1201 global boxRoot, __replyButtonText, __widgetTexts, buttonsFrame
1202 global rootWindowPosition
1203 choices = ["OK"]
1204 __replyButtonText = choices[0]
1205
1206
1207 boxRoot = Tk()
1208
1209 boxRoot.protocol('WM_DELETE_WINDOW', denyWindowManagerClose )
1210
1211 screen_width = boxRoot.winfo_screenwidth()
1212 screen_height = boxRoot.winfo_screenheight()
1213 root_width = int((screen_width * 0.8))
1214 root_height = int((screen_height * 0.5))
1215 root_xpos = int((screen_width * 0.1))
1216 root_ypos = int((screen_height * 0.05))
1217
1218 boxRoot.title(title)
1219 boxRoot.iconname('Dialog')
1220 rootWindowPosition = "+0+0"
1221 boxRoot.geometry(rootWindowPosition)
1222 boxRoot.expand=NO
1223 boxRoot.minsize(root_width, root_height)
1224 rootWindowPosition = "+" + str(root_xpos) + "+" + str(root_ypos)
1225 boxRoot.geometry(rootWindowPosition)
1226
1227 mainframe = Frame(master=boxRoot)
1228 mainframe.pack(side=TOP, fill=BOTH, expand=YES)
1229
1230
1231
1232 textboxFrame = Frame(mainframe, borderwidth=3)
1233 textboxFrame.pack(side=BOTTOM , fill=BOTH, expand=YES)
1234
1235 message_and_buttonsFrame = Frame(mainframe)
1236 message_and_buttonsFrame.pack(side=TOP, fill=X, expand=NO)
1237
1238 messageFrame = Frame(message_and_buttonsFrame)
1239 messageFrame.pack(side=LEFT, fill=X, expand=YES)
1240
1241 buttonsFrame = Frame(message_and_buttonsFrame)
1242 buttonsFrame.pack(side=RIGHT, expand=NO)
1243
1244
1245
1246
1247 if codebox:
1248 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1249 textbox = Text(textboxFrame,height=25,width=character_width, padx="2m", pady="1m")
1250 textbox.configure(wrap=NONE)
1251 textbox.configure(font=(MONOSPACE_FONT_FAMILY, MONOSPACE_FONT_SIZE))
1252
1253 else:
1254 character_width = int((root_width * 0.6) / MONOSPACE_FONT_SIZE)
1255 textbox = Text(
1256 textboxFrame
1257 , height=25
1258 , width=character_width
1259 , padx="2m"
1260 , pady="1m"
1261 )
1262 textbox.configure(wrap=WORD)
1263 textbox.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1264
1265
1266
1267 mainframe.bind("<Next>" , textbox.yview_scroll( 1,PAGES))
1268 mainframe.bind("<Prior>", textbox.yview_scroll(-1,PAGES))
1269
1270 mainframe.bind("<Right>", textbox.xview_scroll( 1,PAGES))
1271 mainframe.bind("<Left>" , textbox.xview_scroll(-1,PAGES))
1272
1273 mainframe.bind("<Down>", textbox.yview_scroll( 1,UNITS))
1274 mainframe.bind("<Up>" , textbox.yview_scroll(-1,UNITS))
1275
1276
1277
1278 rightScrollbar = Scrollbar(textboxFrame, orient=VERTICAL, command=textbox.yview)
1279 textbox.configure(yscrollcommand = rightScrollbar.set)
1280
1281
1282 bottomScrollbar = Scrollbar(textboxFrame, orient=HORIZONTAL, command=textbox.xview)
1283 textbox.configure(xscrollcommand = bottomScrollbar.set)
1284
1285
1286
1287
1288
1289
1290
1291
1292 if codebox:
1293 bottomScrollbar.pack(side=BOTTOM, fill=X)
1294 rightScrollbar.pack(side=RIGHT, fill=Y)
1295
1296 textbox.pack(side=LEFT, fill=BOTH, expand=YES)
1297
1298
1299
1300 messageWidget = Message(messageFrame, anchor=NW, text=msg, width=int(root_width * 0.9))
1301 messageWidget.configure(font=(PROPORTIONAL_FONT_FAMILY,PROPORTIONAL_FONT_SIZE))
1302 messageWidget.pack(side=LEFT, expand=YES, fill=BOTH, padx='1m', pady='1m')
1303
1304
1305 okButton = Button(buttonsFrame, takefocus=YES, text="OK", height=1, width=6)
1306 okButton.pack(expand=NO, side=TOP, padx='2m', pady='1m', ipady="1m", ipadx="2m")
1307 okButton.bind("<Return>" , __textboxOK)
1308 okButton.bind("<Button-1>", __textboxOK)
1309 okButton.bind("<Escape>" , __textboxOK)
1310
1311
1312
1313 try:
1314
1315 if type(text) == type("abc"): pass
1316 else:
1317 try:
1318 text = "".join(text)
1319 except:
1320 msgbox("Exception when trying to convert "+ str(type(text)) + " to text in textbox")
1321 sys.exit(16)
1322 textbox.insert(END,text, "normal")
1323
1324
1325
1326 except:
1327 msgbox("Exception when trying to load the textbox.")
1328 sys.exit(16)
1329
1330 try:
1331 okButton.focus_force()
1332 except:
1333 msgbox("Exception when trying to put focus on okButton.")
1334 sys.exit(16)
1335
1336 boxRoot.mainloop()
1337 boxRoot.destroy()
1338 return __replyButtonText
1339
1340
1341
1342
1343 -def __textboxOK(event):
1344 global boxRoot
1345 boxRoot.quit()
1346
1347
1348
1349
1350
1351
1352 -def diropenbox(msg=None
1353 , title=None
1354 , default=None
1355 ):
1356 """
1357 A dialog to get a directory name.
1358 Note that the msg argument, if specified, is ignored.
1359
1360 Returns the name of a directory, or None if user chose to cancel.
1361
1362 If the "default" argument specifies a directory name, and that
1363 directory exists, then the dialog box will start with that directory.
1364 """
1365 title=getFileDialogTitle(msg,title)
1366 boxRoot = Tk()
1367 boxRoot.withdraw()
1368 if not default: default = None
1369 f = tk_FileDialog.askdirectory(
1370 parent=boxRoot
1371 , title=title
1372 , initialdir=default
1373 , initialfile=None
1374 )
1375 boxRoot.destroy()
1376 if not f: return None
1377 return os.path.normpath(f)
1378
1379
1380
1381
1382
1383
1387 if msg and title: return "%s - %s" % (title,msg)
1388 if msg and not title: return str(msg)
1389 if title and not msg: return str(title)
1390 return None
1391
1392
1393
1394
1397 if len(filemask) == 0:
1398 raise AssertionError('Filetype argument is empty.')
1399
1400 self.masks = []
1401
1402 if type(filemask) == type("abc"):
1403 self.initializeFromString(filemask)
1404
1405 elif type(filemask) == type([]):
1406 if len(filemask) < 2:
1407 raise AssertionError('Invalid filemask.\n'
1408 +'List contains less than 2 members: "%s"' % filemask)
1409 else:
1410 self.name = filemask[-1]
1411 self.masks = list(filemask[:-1] )
1412 else:
1413 raise AssertionError('Invalid filemask: "%s"' % filemask)
1414
1416 if self.name == other.name: return True
1417 return False
1418
1419 - def add(self,other):
1420 for mask in other.masks:
1421 if mask in self.masks: pass
1422 else: self.masks.append(mask)
1423
1425 return (self.name,tuple(self.masks))
1426
1428 if self.name == "All files": return True
1429 return False
1430
1432
1433 self.ext = os.path.splitext(filemask)[1]
1434 if self.ext == "" : self.ext = ".*"
1435 if self.ext == ".": self.ext = ".*"
1436 self.name = self.getName()
1437 self.masks = ["*" + self.ext]
1438
1440 e = self.ext
1441 if e == ".*" : return "All files"
1442 if e == ".txt": return "Text files"
1443 if e == ".py" : return "Python files"
1444 if e == ".pyc" : return "Python files"
1445 if e == ".xls": return "Excel files"
1446 if e.startswith("."):
1447 return e[1:].upper() + " files"
1448 return e.upper() + " files"
1449
1450
1451
1452
1453
1454 -def fileopenbox(msg=None
1455 , title=None
1456 , default="*"
1457 , filetypes=None
1458 ):
1459 """
1460 A dialog to get a file name.
1461
1462 About the "default" argument
1463 ============================
1464 The "default" argument specifies a filepath that (normally)
1465 contains one or more wildcards.
1466 fileopenbox will display only files that match the default filepath.
1467 If omitted, defaults to "*" (all files in the current directory).
1468
1469 WINDOWS EXAMPLE::
1470 ...default="c:/myjunk/*.py"
1471 will open in directory c:\myjunk\ and show all Python files.
1472
1473 WINDOWS EXAMPLE::
1474 ...default="c:/myjunk/test*.py"
1475 will open in directory c:\myjunk\ and show all Python files
1476 whose names begin with "test".
1477
1478
1479 Note that on Windows, fileopenbox automatically changes the path
1480 separator to the Windows path separator (backslash).
1481
1482 About the "filetypes" argument
1483 ==============================
1484 If specified, it should contain a list of items,
1485 where each item is either::
1486 - a string containing a filemask # e.g. "*.txt"
1487 - a list of strings, where all of the strings except the last one
1488 are filemasks (each beginning with "*.",
1489 such as "*.txt" for text files, "*.py" for Python files, etc.).
1490 and the last string contains a filetype description
1491
1492 EXAMPLE::
1493 filetypes = ["*.css", ["*.htm", "*.html", "HTML files"] ]
1494
1495 NOTE THAT
1496 =========
1497
1498 If the filetypes list does not contain ("All files","*"),
1499 it will be added.
1500
1501 If the filetypes list does not contain a filemask that includes
1502 the extension of the "default" argument, it will be added.
1503 For example, if default="*abc.py"
1504 and no filetypes argument was specified, then
1505 "*.py" will automatically be added to the filetypes argument.
1506
1507 @rtype: string or None
1508 @return: the name of a file, or None if user chose to cancel
1509
1510 @arg msg: the msg to be displayed.
1511 @arg title: the window title
1512 @arg default: filepath with wildcards
1513 @arg filetypes: filemasks that a user can choose, e.g. "*.txt"
1514 """
1515 boxRoot = Tk()
1516 boxRoot.withdraw()
1517
1518 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1519
1520
1521
1522
1523
1524
1525
1526 if (initialfile.find("*") < 0) and (initialfile.find("?") < 0):
1527 initialfile = None
1528 elif initialbase == "*":
1529 initialfile = None
1530
1531 f = tk_FileDialog.askopenfilename(parent=boxRoot
1532 , title=getFileDialogTitle(msg,title)
1533 , initialdir=initialdir
1534 , initialfile=initialfile
1535 , filetypes=filetypes
1536 )
1537
1538 boxRoot.destroy()
1539
1540 if not f: return None
1541 return os.path.normpath(f)
1542
1543
1544
1545
1546
1547 -def filesavebox(msg=None
1548 , title=None
1549 , default=""
1550 , filetypes=None
1551 ):
1552 """
1553 A file to get the name of a file to save.
1554 Returns the name of a file, or None if user chose to cancel.
1555
1556 The "default" argument should contain a filename (i.e. the
1557 current name of the file to be saved). It may also be empty,
1558 or contain a filemask that includes wildcards.
1559
1560 The "filetypes" argument works like the "filetypes" argument to
1561 fileopenbox.
1562 """
1563
1564 boxRoot = Tk()
1565 boxRoot.withdraw()
1566
1567 initialbase, initialfile, initialdir, filetypes = fileboxSetup(default,filetypes)
1568
1569 f = tk_FileDialog.asksaveasfilename(parent=boxRoot
1570 , title=getFileDialogTitle(msg,title)
1571 , initialfile=initialfile
1572 , initialdir=initialdir
1573 , filetypes=filetypes
1574 )
1575 boxRoot.destroy()
1576 if not f: return None
1577 return os.path.normpath(f)
1578
1579
1580
1581
1582
1583
1584
1586 if not default: default = os.path.join(".","*")
1587 initialdir, initialfile = os.path.split(default)
1588 if not initialdir : initialdir = "."
1589 if not initialfile: initialfile = "*"
1590 initialbase, initialext = os.path.splitext(initialfile)
1591 initialFileTypeObject = FileTypeObject(initialfile)
1592
1593 allFileTypeObject = FileTypeObject("*")
1594 ALL_filetypes_was_specified = False
1595
1596 if not filetypes: filetypes= []
1597 filetypeObjects = []
1598
1599 for filemask in filetypes:
1600 fto = FileTypeObject(filemask)
1601
1602 if fto.isAll():
1603 ALL_filetypes_was_specified = True
1604
1605 if fto == initialFileTypeObject:
1606 initialFileTypeObject.add(fto)
1607 else:
1608 filetypeObjects.append(fto)
1609
1610
1611
1612
1613 if ALL_filetypes_was_specified:
1614 pass
1615 elif allFileTypeObject == initialFileTypeObject:
1616 pass
1617 else:
1618 filetypeObjects.insert(0,allFileTypeObject)
1619
1620
1621
1622
1623
1624 if len(filetypeObjects) == 0:
1625 filetypeObjects.append(initialFileTypeObject)
1626
1627 if initialFileTypeObject in (filetypeObjects[0], filetypeObjects[-1]):
1628 pass
1629 else:
1630 if runningPython26:
1631 filetypeObjects.append(initialFileTypeObject)
1632 else:
1633 filetypeObjects.insert(0,initialFileTypeObject)
1634
1635 filetypes = [fto.toTuple() for fto in filetypeObjects]
1636
1637 return initialbase, initialfile, initialdir, filetypes
1638
1639
1640
1641
1642
1643
1651
1652
1678
1679
1680
1681
1682
1683
1684
1686 r"""
1687 A class to support persistent storage.
1688
1689 You can use EgStore to support the storage and retrieval
1690 of user settings for an EasyGui application.
1691
1692
1693 # Example A
1694 #-----------------------------------------------------------------------
1695 # define a class named Settings as a subclass of EgStore
1696 #-----------------------------------------------------------------------
1697 class Settings(EgStore):
1698
1699 def __init__(self, filename): # filename is required
1700 #-------------------------------------------------
1701 # Specify default/initial values for variables that
1702 # this particular application wants to remember.
1703 #-------------------------------------------------
1704 self.userId = ""
1705 self.targetServer = ""
1706
1707 #-------------------------------------------------
1708 # For subclasses of EgStore, these must be
1709 # the last two statements in __init__
1710 #-------------------------------------------------
1711 self.filename = filename # this is required
1712 self.restore() # restore values from the storage file if possible
1713
1714
1715
1716 # Example B
1717 #-----------------------------------------------------------------------
1718 # create settings, a persistent Settings object
1719 #-----------------------------------------------------------------------
1720 settingsFile = "myApp_settings.txt"
1721 settings = Settings(settingsFile)
1722
1723 user = "obama_barak"
1724 server = "whitehouse1"
1725 settings.userId = user
1726 settings.targetServer = server
1727 settings.store() # persist the settings
1728
1729 # run code that gets a new value for userId, and persist the settings
1730 user = "biden_joe"
1731 settings.userId = user
1732 settings.store()
1733
1734
1735 # Example C
1736 #-----------------------------------------------------------------------
1737 # recover the Settings instance, change an attribute, and store it again.
1738 #-----------------------------------------------------------------------
1739 settings = Settings(settingsFile)
1740 settings.userId = "vanrossum_g"
1741 settings.store()
1742
1743 """
1745 raise NotImplementedError()
1746
1748 """
1749 Set the values of whatever attributes are recoverable
1750 from the pickle file.
1751
1752 Populate the attributes (the __dict__) of the EgStore object
1753 from the attributes (the __dict__) of the pickled object.
1754
1755 If the pickled object has attributes that have been initialized
1756 in the EgStore object, then those attributes of the EgStore object
1757 will be replaced by the values of the corresponding attributes
1758 in the pickled object.
1759
1760 If the pickled object is missing some attributes that have
1761 been initialized in the EgStore object, then those attributes
1762 of the EgStore object will retain the values that they were
1763 initialized with.
1764
1765 If the pickled object has some attributes that were not
1766 initialized in the EgStore object, then those attributes
1767 will be ignored.
1768
1769 IN SUMMARY:
1770
1771 After the recover() operation, the EgStore object will have all,
1772 and only, the attributes that it had when it was initialized.
1773
1774 Where possible, those attributes will have values recovered
1775 from the pickled object.
1776 """
1777 if not os.path.exists(self.filename): return self
1778 if not os.path.isfile(self.filename): return self
1779
1780 try:
1781 f = open(self.filename)
1782 unpickledObject = pickle.load(f)
1783 f.close()
1784
1785 for key in list(self.__dict__.keys()):
1786 default = self.__dict__[key]
1787 self.__dict__[key] = unpickledObject.__dict__.get(key,default)
1788 except:
1789 pass
1790
1791 return self
1792
1794 """
1795 Save the attributes of the EgStore object to a pickle file.
1796 Note that if the directory for the pickle file does not already exist,
1797 the store operation will fail.
1798 """
1799 f = open(self.filename, "w")
1800 pickle.dump(self, f)
1801 f.close()
1802
1803
1805 """
1806 Delete my persistent file (i.e. pickle file), if it exists.
1807 """
1808 if os.path.isfile(self.filename):
1809 os.remove(self.filename)
1810 return
1811
1813 """
1814 return my contents as a string in an easy-to-read format.
1815 """
1816
1817 longest_key_length = 0
1818 keys = []
1819 for key in self.__dict__.keys():
1820 keys.append(key)
1821 longest_key_length = max(longest_key_length, len(key))
1822
1823 keys.sort()
1824 lines = []
1825 for key in keys:
1826 value = self.__dict__[key]
1827 key = key.ljust(longest_key_length)
1828 lines.append("%s : %s\n" % (key,repr(value)) )
1829 return "".join(lines)
1830
1831
1832
1833
1834
1835
1836
1837
1838
1840 """
1841 Run the EasyGui demo.
1842 """
1843
1844 writeln("\n" * 100)
1845
1846
1847 code_snippet = ("dafsdfa dasflkj pp[oadsij asdfp;ij asdfpjkop asdfpok asdfpok asdfpok"*3) +"\n"+\
1848 """# here is some dummy Python code
1849 for someItem in myListOfStuff:
1850 do something(someItem)
1851 do something()
1852 do something()
1853 if somethingElse(someItem):
1854 doSomethingEvenMoreInteresting()
1855
1856 """*16
1857
1858
1859
1860 text_snippet = ((\
1861 """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! """ \
1862 *5)+"\n\n")*10
1863
1864
1865
1866 intro_message = ("Pick the kind of box that you wish to demo.\n"
1867 + "\n * Python version " + sys.version
1868 + "\n * EasyGui version " + egversion
1869 + "\n * Tk version " + str(TkVersion)
1870 )
1871
1872
1873
1874
1875 while 1:
1876 choices = [
1877 "msgbox",
1878 "buttonbox",
1879 "buttonbox(image) -- a buttonbox that displays an image",
1880 "choicebox",
1881 "multchoicebox",
1882 "textbox",
1883 "ynbox",
1884 "ccbox",
1885 "enterbox",
1886 "enterbox(image) -- an enterbox that displays an image",
1887 "exceptionbox",
1888 "codebox",
1889 "integerbox",
1890 "boolbox",
1891 "indexbox",
1892 "filesavebox",
1893 "fileopenbox",
1894 "passwordbox",
1895 "multenterbox",
1896 "multpasswordbox",
1897 "diropenbox",
1898 "About EasyGui",
1899 " Help"
1900 ]
1901 choice = choicebox(msg=intro_message
1902 , title="EasyGui " + egversion
1903 , choices=choices)
1904
1905 if not choice: return
1906
1907 reply = choice.split()
1908
1909 if reply[0] == "msgbox":
1910 reply = msgbox("short msg", "This is a long title")
1911 writeln("Reply was: %s" % repr(reply))
1912
1913 elif reply[0] == "About":
1914 reply = abouteasygui()
1915
1916 elif reply[0] == "Help":
1917 _demo_help()
1918
1919 elif reply[0] == "buttonbox":
1920 reply = buttonbox()
1921 writeln("Reply was: %s" % repr(reply))
1922
1923 title = "Demo of Buttonbox with many, many buttons!"
1924 msg = "This buttonbox shows what happens when you specify too many buttons."
1925 reply = buttonbox(msg=msg, title=title, choices=choices)
1926 writeln("Reply was: %s" % repr(reply))
1927
1928 elif reply[0] == "buttonbox(image)":
1929 _demo_buttonbox_with_image()
1930
1931 elif reply[0] == "boolbox":
1932 reply = boolbox()
1933 writeln("Reply was: %s" % repr(reply))
1934
1935 elif reply[0] == "enterbox":
1936 image = "python_and_check_logo.gif"
1937 message = "Enter the name of your best friend."\
1938 "\n(Result will be stripped.)"
1939 reply = enterbox(message, "Love!", " Suzy Smith ")
1940 writeln("Reply was: %s" % repr(reply))
1941
1942 message = "Enter the name of your best friend."\
1943 "\n(Result will NOT be stripped.)"
1944 reply = enterbox(message, "Love!", " Suzy Smith ",strip=False)
1945 writeln("Reply was: %s" % repr(reply))
1946
1947 reply = enterbox("Enter the name of your worst enemy:", "Hate!")
1948 writeln("Reply was: %s" % repr(reply))
1949
1950 elif reply[0] == "enterbox(image)":
1951 image = "python_and_check_logo.gif"
1952 message = "What kind of snake is this?"
1953 reply = enterbox(message, "Quiz",image=image)
1954 writeln("Reply was: %s" % repr(reply))
1955
1956 elif reply[0] == "exceptionbox":
1957 try:
1958 thisWillCauseADivideByZeroException = 1/0
1959 except:
1960 exceptionbox()
1961
1962 elif reply[0] == "integerbox":
1963 reply = integerbox(
1964 "Enter a number between 3 and 333",
1965 "Demo: integerbox WITH a default value",
1966 222, 3, 333)
1967 writeln("Reply was: %s" % repr(reply))
1968
1969 reply = integerbox(
1970 "Enter a number between 0 and 99",
1971 "Demo: integerbox WITHOUT a default value"
1972 )
1973 writeln("Reply was: %s" % repr(reply))
1974
1975 elif reply[0] == "diropenbox" : _demo_diropenbox()
1976 elif reply[0] == "fileopenbox": _demo_fileopenbox()
1977 elif reply[0] == "filesavebox": _demo_filesavebox()
1978
1979 elif reply[0] == "indexbox":
1980 title = reply[0]
1981 msg = "Demo of " + reply[0]
1982 choices = ["Choice1", "Choice2", "Choice3", "Choice4"]
1983 reply = indexbox(msg, title, choices)
1984 writeln("Reply was: %s" % repr(reply))
1985
1986 elif reply[0] == "passwordbox":
1987 reply = passwordbox("Demo of password box WITHOUT default"
1988 + "\n\nEnter your secret password", "Member Logon")
1989 writeln("Reply was: %s" % str(reply))
1990
1991 reply = passwordbox("Demo of password box WITH default"
1992 + "\n\nEnter your secret password", "Member Logon", "alfie")
1993 writeln("Reply was: %s" % str(reply))
1994
1995 elif reply[0] == "multenterbox":
1996 msg = "Enter your personal information"
1997 title = "Credit Card Application"
1998 fieldNames = ["Name","Street Address","City","State","ZipCode"]
1999 fieldValues = []
2000 fieldValues = multenterbox(msg,title, fieldNames)
2001
2002
2003 while 1:
2004 if fieldValues == None: break
2005 errmsg = ""
2006 for i in range(len(fieldNames)):
2007 if fieldValues[i].strip() == "":
2008 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2009 if errmsg == "": break
2010 fieldValues = multenterbox(errmsg, title, fieldNames, fieldValues)
2011
2012 writeln("Reply was: %s" % str(fieldValues))
2013
2014 elif reply[0] == "multpasswordbox":
2015 msg = "Enter logon information"
2016 title = "Demo of multpasswordbox"
2017 fieldNames = ["Server ID", "User ID", "Password"]
2018 fieldValues = []
2019 fieldValues = multpasswordbox(msg,title, fieldNames)
2020
2021
2022 while 1:
2023 if fieldValues == None: break
2024 errmsg = ""
2025 for i in range(len(fieldNames)):
2026 if fieldValues[i].strip() == "":
2027 errmsg = errmsg + ('"%s" is a required field.\n\n' % fieldNames[i])
2028 if errmsg == "": break
2029 fieldValues = multpasswordbox(errmsg, title, fieldNames, fieldValues)
2030
2031 writeln("Reply was: %s" % str(fieldValues))
2032
2033 elif reply[0] == "ynbox":
2034 title = "Demo of ynbox"
2035 msg = "Were you expecting the Spanish Inquisition?"
2036 reply = ynbox(msg, title)
2037 writeln("Reply was: %s" % repr(reply))
2038 if reply:
2039 msgbox("NOBODY expects the Spanish Inquisition!", "Wrong!")
2040
2041 elif reply[0] == "ccbox":
2042 title = "Demo of ccbox"
2043 reply = ccbox(msg,title)
2044 writeln("Reply was: %s" % repr(reply))
2045
2046 elif reply[0] == "choicebox":
2047 title = "Demo of choicebox"
2048 longchoice = "This is an example of a very long option which you may or may not wish to choose."*2
2049 listChoices = ["nnn", "ddd", "eee", "fff", "aaa", longchoice
2050 , "aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk", "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq", "rrr", "sss", "ttt", "uuu", "vvv"]
2051
2052 msg = "Pick something. " + ("A wrapable sentence of text ?! "*30) + "\nA separate line of text."*6
2053 reply = choicebox(msg=msg, choices=listChoices)
2054 writeln("Reply was: %s" % repr(reply))
2055
2056 msg = "Pick something. "
2057 reply = choicebox(msg=msg, title=title, choices=listChoices)
2058 writeln("Reply was: %s" % repr(reply))
2059
2060 msg = "Pick something. "
2061 reply = choicebox(msg="The list of choices is empty!", choices=[])
2062 writeln("Reply was: %s" % repr(reply))
2063
2064 elif reply[0] == "multchoicebox":
2065 listChoices = ["aaa", "bbb", "ccc", "ggg", "hhh", "iii", "jjj", "kkk"
2066 , "LLL", "mmm" , "nnn", "ooo", "ppp", "qqq"
2067 , "rrr", "sss", "ttt", "uuu", "vvv"]
2068
2069 msg = "Pick as many choices as you wish."
2070 reply = multchoicebox(msg,"Demo of multchoicebox", listChoices)
2071 writeln("Reply was: %s" % repr(reply))
2072
2073 elif reply[0] == "textbox":
2074 title = "Demo of textbox"
2075 msg = "Here is some sample text. " * 16
2076 reply = textbox(msg, "Text Sample", text_snippet)
2077 writeln("Reply was: %s" % repr(reply))
2078
2079 elif reply[0] == "codebox":
2080 msg = "Here is some sample code. " * 16
2081 reply = codebox(msg, "Code Sample", code_snippet)
2082 writeln("Reply was: %s" % repr(reply))
2083
2084 else:
2085 msgbox("Choice\n\n" + choice + "\n\nis not recognized", "Program Logic Error")
2086 return
2087
2088
2089
2110
2111
2113 savedStdout = sys.stdout
2114 sys.stdout = capturedOutput = StringIO()
2115 help("easygui")
2116 sys.stdout = savedStdout
2117 codebox("EasyGui Help",text=capturedOutput.getvalue())
2118
2120 filename = "myNewFile.txt"
2121 title = "File SaveAs"
2122 msg ="Save file as:"
2123
2124 f = filesavebox(msg,title,default=filename)
2125 writeln("You chose to save file: %s" % f)
2126
2128 title = "Demo of diropenbox"
2129 msg = "Pick the directory that you wish to open."
2130 d = diropenbox(msg, title)
2131 writeln("You chose directory...: %s" % d)
2132
2133 d = diropenbox(msg, title,default="./")
2134 writeln("You chose directory...: %s" % d)
2135
2136 d = diropenbox(msg, title,default="c:/")
2137 writeln("You chose directory...: %s" % d)
2138
2139
2141 msg = "Python files"
2142 title = "Open files"
2143 default="*.py"
2144 f = fileopenbox(msg,title,default=default)
2145 writeln("You chose to open file: %s" % f)
2146
2147 default="./*.gif"
2148 filetypes = ["*.jpg",["*.zip","*.tgs","*.gz", "Archive files"],["*.htm", "*.html","HTML files"]]
2149 f = fileopenbox(msg,title,default=default,filetypes=filetypes)
2150 writeln("You chose to open file: %s" % f)
2151
2152 """#deadcode -- testing ----------------------------------------
2153 f = fileopenbox(None,None,default=default)
2154 writeln("You chose to open file: %s" % f)
2155
2156 f = fileopenbox(None,title,default=default)
2157 writeln("You chose to open file: %s" % f)
2158
2159 f = fileopenbox(msg,None,default=default)
2160 writeln("You chose to open file: %s" % f)
2161
2162 f = fileopenbox(default=default)
2163 writeln("You chose to open file: %s" % f)
2164
2165 f = fileopenbox(default=None)
2166 writeln("You chose to open file: %s" % f)
2167 #----------------------------------------------------deadcode """
2168
2169
2172
2173 EASYGUI_ABOUT_INFORMATION = '''
2174 ========================================================================
2175 0.93(2009-07-07)
2176 ========================================================================
2177
2178 ENHANCEMENTS
2179 ------------------------------------------------------
2180
2181 * Added exceptionbox to display stack trace of exceptions
2182 * modified names of some font-related constants to make it
2183 easier to customize them
2184
2185
2186 ========================================================================
2187 0.92(2009-06-22)
2188 ========================================================================
2189
2190 ENHANCEMENTS
2191 ------------------------------------------------------
2192
2193 * Added EgStore class to to provide basic easy-to-use persistence.
2194
2195 BUG FIXES
2196 ------------------------------------------------------
2197
2198 * Fixed a bug that was preventing Linux users from copying text out of
2199 a textbox and a codebox. This was not a problem for Windows users.
2200
2201
2202 ========================================================================
2203 version: 0.91(2009-04-25)
2204 ========================================================================
2205
2206 ENHANCEMENTS
2207 ------------------------------------------------------
2208
2209 * exposed egdemo(). Now you can write a program like this:
2210
2211 from easygui import *
2212 egdemo() # run the easygui demo
2213
2214
2215 BUG FIXES
2216 ------------------------------------------------------
2217
2218 * Removed some fileopenbox test code that was accidentally left in,
2219 causing egdemo to crash.
2220
2221 * Made EasyGui "version 2.5 aware" so that it will use the correct
2222 default filemask in fileopenbox and filesavebox under Python 2.5
2223 and earlier.
2224
2225 * fixed bugs in filesavebox by converting it to use the same code as
2226 fileopenbox for handling the "default" and "filetypes" args.
2227
2228
2229 ========================================================================
2230 version: 0.90(2009-04-20)
2231 ========================================================================
2232
2233 ENHANCEMENTS
2234 ------------------------------------------------------
2235
2236 * Enhanced fileopenbox to allow it to accept multiple
2237 extensions for a single file type, e.g.:
2238 ... filetype = [[".htm",".html", "HTML filetypes"]]
2239 Previously it accepted only one.
2240
2241
2242 BUG FIXES
2243 ------------------------------------------------------
2244
2245 * Fixed a bug in fileopenbox (it was not displaying initialfile).
2246
2247 Thanks to Matthias Meier in the lovely city of Schwerin, Germany
2248 (http://en.wikipedia.org/wiki/Schwerin) for reporting the problem
2249 and supplying the basic elements of the fix.
2250
2251 * In fileopenbox, removed acceptance of a 1-tuple as a filetype.
2252
2253 This was an unnecessary and confusing feature. In theory,
2254 this change will break backward compatibility ... but
2255 there are probably no programs that used this feature.
2256
2257
2258 NOTES
2259 ----------------------------------------------------------
2260
2261 In some cases (e.g. ... default="junk.txt")
2262 there are minor differences in the display of fileopenbox
2263 between Python version 2.5 on the one hand,
2264 and Python version 2.6 and higher on the other.
2265 Python versions 2.6+ behave correctly.
2266
2267
2268 ========================================================================
2269 version 0.89(2009-04-09)
2270 ========================================================================
2271
2272 BUG FIX
2273 ------------------------------------------------------
2274 * An enhancement in version 0.88 (which added an import of StringIO)
2275 broke compatibility with Python 3.
2276
2277 "In Python 3, the StringIO and cStringIO modules are gone.
2278 Instead, import the io module and use io.StringIO or io.BytesIO
2279 for text and data respectively."
2280
2281 Many thanks to Alan Gauld, author of the "Learn to Program" web site
2282 (http://www.alan-g.me.uk/l2p) for reporting this problem and
2283 supplying the fix.
2284
2285 ========================================================================
2286 version 0.88(2009-04-06)
2287 ========================================================================
2288
2289 ENHANCEMENTS
2290 ------------------------------------------------------
2291 * Support for display of images has been extended to
2292 the various flavors of enterbox. An example of
2293 an enterbox with an image was added to the EasyGui demo.
2294
2295 MODIFICATIONS
2296 ------------------------------------------------------
2297 * In EasyGui demo, HELP output is now captured and displayed
2298 in a codebox rather than being written to stdout.
2299
2300 BUG FIX
2301 ------------------------------------------------------
2302 * Fixed a bug in fileopenbox.
2303 Thanks to Dennis A. Wolf for both the bug report and the fix.
2304
2305
2306 ========================================================================
2307 version 0.87(2009-03-18)
2308 ========================================================================
2309
2310 ENHANCEMENTS
2311 ------------------------------------------------------
2312 * EasyGui will now run under Python version 3.*
2313 as well as version 2.*
2314
2315 BUG FIXES
2316 ------------------------------------------------------
2317 * added support for "root" argument to msgbox, buttonbox, and
2318 enterbox. This feature is not yet documented or very well
2319 tested -- it should be considered experimental/beta.
2320 '''
2321
2328
2329
2330
2331 if __name__ == '__main__':
2332 if True:
2333 egdemo()
2334 else:
2335
2336 root = Tk()
2337 msg = """This is a test of a main Tk() window in which we will place an easygui msgbox.
2338 It will be an interesting experiment.\n\n"""
2339 messageWidget = Message(root, text=msg, width=1000)
2340 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2341 messageWidget = Message(root, text=msg, width=1000)
2342 messageWidget.pack(side=TOP, expand=YES, fill=X, padx='3m', pady='3m')
2343
2344
2345 msgbox("this is a test of passing in boxRoot", root=root)
2346 msgbox("this is a second test of passing in boxRoot", root=root)
2347
2348 reply = enterbox("Enter something", root=root)
2349 writeln("You wrote:", reply)
2350
2351 reply = enterbox("Enter something else", root=root)
2352 writeln("You wrote:", reply)
2353 root.destroy()
2354