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

Source Code for Module easygui

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