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

Source Code for Module easygui

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