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

Source Code for Module easygui

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