macosxSupport.py revision fa002d4cdd3a44a07252857ec7a55ed27fb15648
1""" 2A number of functions that enhance IDLE on Mac OSX. 3""" 4import sys 5import Tkinter 6from os import path 7 8 9import warnings 10 11def runningAsOSXApp(): 12 warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()", 13 DeprecationWarning, stacklevel=2) 14 return isAquaTk() 15 16def isCarbonAquaTk(root): 17 warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()", 18 DeprecationWarning, stacklevel=2) 19 return isCarbonTk() 20 21_tk_type = None 22 23def _initializeTkVariantTests(root): 24 """ 25 Initializes OS X Tk variant values for 26 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz(). 27 """ 28 global _tk_type 29 if sys.platform == 'darwin': 30 ws = root.tk.call('tk', 'windowingsystem') 31 if 'x11' in ws: 32 _tk_type = "xquartz" 33 elif 'aqua' not in ws: 34 _tk_type = "other" 35 elif 'AppKit' in root.tk.call('winfo', 'server', '.'): 36 _tk_type = "cocoa" 37 else: 38 _tk_type = "carbon" 39 else: 40 _tk_type = "other" 41 42def isAquaTk(): 43 """ 44 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon). 45 """ 46 assert _tk_type is not None 47 return _tk_type == "cocoa" or _tk_type == "carbon" 48 49def isCarbonTk(): 50 """ 51 Returns True if IDLE is using a Carbon Aqua Tk (instead of the 52 newer Cocoa Aqua Tk). 53 """ 54 assert _tk_type is not None 55 return _tk_type == "carbon" 56 57def isCocoaTk(): 58 """ 59 Returns True if IDLE is using a Cocoa Aqua Tk. 60 """ 61 assert _tk_type is not None 62 return _tk_type == "cocoa" 63 64def isXQuartz(): 65 """ 66 Returns True if IDLE is using an OS X X11 Tk. 67 """ 68 assert _tk_type is not None 69 return _tk_type == "xquartz" 70 71def tkVersionWarning(root): 72 """ 73 Returns a string warning message if the Tk version in use appears to 74 be one known to cause problems with IDLE. 75 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable. 76 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but 77 can still crash unexpectedly. 78 """ 79 80 if isCocoaTk(): 81 patchlevel = root.tk.call('info', 'patchlevel') 82 if patchlevel not in ('8.5.7', '8.5.9'): 83 return False 84 return (r"WARNING: The version of Tcl/Tk ({0}) in use may" 85 r" be unstable.\n" 86 r"Visit http://www.python.org/download/mac/tcltk/" 87 r" for current information.".format(patchlevel)) 88 else: 89 return False 90 91def addOpenEventSupport(root, flist): 92 """ 93 This ensures that the application will respond to open AppleEvents, which 94 makes is feasible to use IDLE as the default application for python files. 95 """ 96 def doOpenFile(*args): 97 for fn in args: 98 flist.open(fn) 99 100 # The command below is a hook in aquatk that is called whenever the app 101 # receives a file open event. The callback can have multiple arguments, 102 # one for every file that should be opened. 103 root.createcommand("::tk::mac::OpenDocument", doOpenFile) 104 105def hideTkConsole(root): 106 try: 107 root.tk.call('console', 'hide') 108 except Tkinter.TclError: 109 # Some versions of the Tk framework don't have a console object 110 pass 111 112def overrideRootMenu(root, flist): 113 """ 114 Replace the Tk root menu by something that is more appropriate for 115 IDLE with an Aqua Tk. 116 """ 117 # The menu that is attached to the Tk root (".") is also used by AquaTk for 118 # all windows that don't specify a menu of their own. The default menubar 119 # contains a number of menus, none of which are appropriate for IDLE. The 120 # Most annoying of those is an 'About Tck/Tk...' menu in the application 121 # menu. 122 # 123 # This function replaces the default menubar by a mostly empty one, it 124 # should only contain the correct application menu and the window menu. 125 # 126 # Due to a (mis-)feature of TkAqua the user will also see an empty Help 127 # menu. 128 from Tkinter import Menu 129 from idlelib import Bindings 130 from idlelib import WindowList 131 132 closeItem = Bindings.menudefs[0][1][-2] 133 134 # Remove the last 3 items of the file menu: a separator, close window and 135 # quit. Close window will be reinserted just above the save item, where 136 # it should be according to the HIG. Quit is in the application menu. 137 del Bindings.menudefs[0][1][-3:] 138 Bindings.menudefs[0][1].insert(6, closeItem) 139 140 # Remove the 'About' entry from the help menu, it is in the application 141 # menu 142 del Bindings.menudefs[-1][1][0:2] 143 # Remove the 'Configure Idle' entry from the options menu, it is in the 144 # application menu as 'Preferences' 145 del Bindings.menudefs[-2][1][0] 146 menubar = Menu(root) 147 root.configure(menu=menubar) 148 menudict = {} 149 150 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0) 151 menubar.add_cascade(label='Window', menu=menu, underline=0) 152 153 def postwindowsmenu(menu=menu): 154 end = menu.index('end') 155 if end is None: 156 end = -1 157 158 if end > 0: 159 menu.delete(0, end) 160 WindowList.add_windows_to_menu(menu) 161 WindowList.register_callback(postwindowsmenu) 162 163 def about_dialog(event=None): 164 from idlelib import aboutDialog 165 aboutDialog.AboutDialog(root, 'About IDLE') 166 167 def config_dialog(event=None): 168 from idlelib import configDialog 169 root.instance_dict = flist.inversedict 170 configDialog.ConfigDialog(root, 'Settings') 171 172 def help_dialog(event=None): 173 from idlelib import textView 174 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt') 175 textView.view_file(root, 'Help', fn) 176 177 root.bind('<<about-idle>>', about_dialog) 178 root.bind('<<open-config-dialog>>', config_dialog) 179 root.createcommand('::tk::mac::ShowPreferences', config_dialog) 180 if flist: 181 root.bind('<<close-all-windows>>', flist.close_all_callback) 182 183 # The binding above doesn't reliably work on all versions of Tk 184 # on MacOSX. Adding command definition below does seem to do the 185 # right thing for now. 186 root.createcommand('exit', flist.close_all_callback) 187 188 if isCarbonTk(): 189 # for Carbon AquaTk, replace the default Tk apple menu 190 menudict['application'] = menu = Menu(menubar, name='apple', 191 tearoff=0) 192 menubar.add_cascade(label='IDLE', menu=menu) 193 Bindings.menudefs.insert(0, 194 ('application', [ 195 ('About IDLE', '<<about-idle>>'), 196 None, 197 ])) 198 tkversion = root.tk.eval('info patchlevel') 199 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): 200 # for earlier AquaTk versions, supply a Preferences menu item 201 Bindings.menudefs[0][1].append( 202 ('_Preferences....', '<<open-config-dialog>>'), 203 ) 204 if isCocoaTk(): 205 # replace default About dialog with About IDLE one 206 root.createcommand('tkAboutDialog', about_dialog) 207 # replace default "Help" item in Help menu 208 root.createcommand('::tk::mac::ShowHelp', help_dialog) 209 # remove redundant "IDLE Help" from menu 210 del Bindings.menudefs[-1][1][0] 211 212def setupApp(root, flist): 213 """ 214 Perform initial OS X customizations if needed. 215 Called from PyShell.main() after initial calls to Tk() 216 217 There are currently three major versions of Tk in use on OS X: 218 1. Aqua Cocoa Tk (native default since OS X 10.6) 219 2. Aqua Carbon Tk (original native, 32-bit only, deprecated) 220 3. X11 (supported by some third-party distributors, deprecated) 221 There are various differences among the three that affect IDLE 222 behavior, primarily with menus, mouse key events, and accelerators. 223 Some one-time customizations are performed here. 224 Others are dynamically tested throughout idlelib by calls to the 225 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which 226 are initialized here as well. 227 """ 228 _initializeTkVariantTests(root) 229 if isAquaTk(): 230 hideTkConsole(root) 231 overrideRootMenu(root, flist) 232 addOpenEventSupport(root, flist) 233