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

Source Code for Module easygui

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