Module easygui
[hide private]
[frames] | no frames]

Source Code for Module easygui

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