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

Source Code for Module easygui

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