1 """ 2 A number of function that enhance IDLE on MacOSX when it used as a normal 3 GUI application (as opposed to an X11 application). 4 """ 5 import sys 6 import Tkinter 7 from os import path 8 9 10 _appbundle = None 11 12 def runningAsOSXApp(): 13 """ 14 Returns True if Python is running from within an app on OSX. 15 If so, assume that Python was built with Aqua Tcl/Tk rather than 16 X11 Tcl/Tk. 17 """ 18 global _appbundle 19 if _appbundle is None: 20 _appbundle = (sys.platform == 'darwin' and '.app' in sys.executable) 21 return _appbundle 22 23 _carbonaquatk = None 24 25 def isCarbonAquaTk(root): 26 """ 27 Returns True if IDLE is using a Carbon Aqua Tk (instead of the 28 newer Cocoa Aqua Tk). 29 """ 30 global _carbonaquatk 31 if _carbonaquatk is None: 32 _carbonaquatk = (runningAsOSXApp() and 33 'aqua' in root.tk.call('tk', 'windowingsystem') and 34 'AppKit' not in root.tk.call('winfo', 'server', '.')) 35 return _carbonaquatk 36 37 def tkVersionWarning(root): 38 """ 39 Returns a string warning message if the Tk version in use appears to 40 be one known to cause problems with IDLE. 41 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable. 42 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but 43 can still crash unexpectedly. 44 """ 45 46 if (runningAsOSXApp() and 47 ('AppKit' in root.tk.call('winfo', 'server', '.')) ): 48 patchlevel = root.tk.call('info', 'patchlevel') 49 if patchlevel not in ('8.5.7', '8.5.9'): 50 return False 51 return (r"WARNING: The version of Tcl/Tk ({0}) in use may" 52 r" be unstable.\n" 53 r"Visit http://www.python.org/download/mac/tcltk/" 54 r" for current information.".format(patchlevel)) 55 else: 56 return False 57 58 def addOpenEventSupport(root, flist): 59 """ 60 This ensures that the application will respond to open AppleEvents, which 61 makes is feasible to use IDLE as the default application for python files. 62 """ 63 def doOpenFile(*args): 64 for fn in args: 65 flist.open(fn) 66 67 # The command below is a hook in aquatk that is called whenever the app 68 # receives a file open event. The callback can have multiple arguments, 69 # one for every file that should be opened. 70 root.createcommand("::tk::mac::OpenDocument", doOpenFile) 71 72 def hideTkConsole(root): 73 try: 74 root.tk.call('console', 'hide') 75 except Tkinter.TclError: 76 # Some versions of the Tk framework don't have a console object 77 pass 78 79 def overrideRootMenu(root, flist): 80 """ 81 Replace the Tk root menu by something that's more appropriate for 82 IDLE. 83 """ 84 # The menu that is attached to the Tk root (".") is also used by AquaTk for 85 # all windows that don't specify a menu of their own. The default menubar 86 # contains a number of menus, none of which are appropriate for IDLE. The 87 # Most annoying of those is an 'About Tck/Tk...' menu in the application 88 # menu. 89 # 90 # This function replaces the default menubar by a mostly empty one, it 91 # should only contain the correct application menu and the window menu. 92 # 93 # Due to a (mis-)feature of TkAqua the user will also see an empty Help 94 # menu. 95 from Tkinter import Menu, Text, Text 96 from idlelib.EditorWindow import prepstr, get_accelerator 97 from idlelib import Bindings 98 from idlelib import WindowList 99 from idlelib.MultiCall import MultiCallCreator 100 101 menubar = Menu(root) 102 root.configure(menu=menubar) 103 menudict = {} 104 105 menudict['windows'] = menu = Menu(menubar, name='windows') 106 menubar.add_cascade(label='Window', menu=menu, underline=0) 107 108 def postwindowsmenu(menu=menu): 109 end = menu.index('end') 110 if end is None: 111 end = -1 112 113 if end > 0: 114 menu.delete(0, end) 115 WindowList.add_windows_to_menu(menu) 116 WindowList.register_callback(postwindowsmenu) 117 118 def about_dialog(event=None): 119 from idlelib import aboutDialog 120 aboutDialog.AboutDialog(root, 'About IDLE') 121 122 def config_dialog(event=None): 123 from idlelib import configDialog 124 root.instance_dict = flist.inversedict 125 configDialog.ConfigDialog(root, 'Settings') 126 127 def help_dialog(event=None): 128 from idlelib import textView 129 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt') 130 textView.view_file(root, 'Help', fn) 131 132 root.bind('<<about-idle>>', about_dialog) 133 root.bind('<<open-config-dialog>>', config_dialog) 134 root.createcommand('::tk::mac::ShowPreferences', config_dialog) 135 if flist: 136 root.bind('<<close-all-windows>>', flist.close_all_callback) 137 138 # The binding above doesn't reliably work on all versions of Tk 139 # on MacOSX. Adding command definition below does seem to do the 140 # right thing for now. 141 root.createcommand('exit', flist.close_all_callback) 142 143 if isCarbonAquaTk(root): 144 # for Carbon AquaTk, replace the default Tk apple menu 145 menudict['application'] = menu = Menu(menubar, name='apple') 146 menubar.add_cascade(label='IDLE', menu=menu) 147 Bindings.menudefs.insert(0, 148 ('application', [ 149 ('About IDLE', '<<about-idle>>'), 150 None, 151 ])) 152 tkversion = root.tk.eval('info patchlevel') 153 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): 154 # for earlier AquaTk versions, supply a Preferences menu item 155 Bindings.menudefs[0][1].append( 156 ('_Preferences....', '<<open-config-dialog>>'), 157 ) 158 else: 159 # assume Cocoa AquaTk 160 # replace default About dialog with About IDLE one 161 root.createcommand('tkAboutDialog', about_dialog) 162 # replace default "Help" item in Help menu 163 root.createcommand('::tk::mac::ShowHelp', help_dialog) 164 # remove redundant "IDLE Help" from menu 165 del Bindings.menudefs[-1][1][0] 166 167 def setupApp(root, flist): 168 """ 169 Perform setup for the OSX application bundle. 170 """ 171 if not runningAsOSXApp(): return 172 173 hideTkConsole(root) 174 overrideRootMenu(root, flist) 175 addOpenEventSupport(root, flist) 176