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

Source Code for Module easygui

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