[981] | 1 | #!/usr/bin/env python |
---|
[1109] | 2 | ''' |
---|
| 3 | *makeMacApp: Create Mac Applet* |
---|
| 4 | =============================== |
---|
| 5 | |
---|
| 6 | This script creates an AppleScript app to launch GSAS-II. The app is |
---|
[981] | 7 | created in the directory where the GSAS-II script is located. |
---|
| 8 | A softlink to Python is created, but is named GSAS-II, so that |
---|
| 9 | GSAS-II shows up as the name of the app rather than Python in the |
---|
| 10 | menu bar, etc. Note that this requires finding an app version of Python |
---|
| 11 | (expected name .../Resources/Python.app/Contents/MacOS/Python in |
---|
| 12 | directory tree of the calling python interpreter). |
---|
| 13 | |
---|
| 14 | Run this script with one optional argument, the path to the GSASII.py |
---|
| 15 | The script path may be specified relative to the current path or given |
---|
| 16 | an absolute path, but will be accessed via an absolute path. |
---|
| 17 | If no arguments are supplied, the GSASII.py script is assumed to be in the |
---|
| 18 | same directory as this file. |
---|
| 19 | |
---|
| 20 | ''' |
---|
| 21 | import sys, os, os.path, stat, shutil, subprocess, plistlib |
---|
| 22 | def Usage(): |
---|
| 23 | print("\n\tUsage: python "+sys.argv[0]+" [<GSAS-II script>]\n") |
---|
| 24 | sys.exit() |
---|
| 25 | |
---|
[1356] | 26 | def RunPython(image,cmd): |
---|
| 27 | 'Run a command in a python image' |
---|
| 28 | try: |
---|
| 29 | err=None |
---|
| 30 | p = subprocess.Popen([image,'-c',cmd],stdout=subprocess.PIPE,stderr=subprocess.PIPE) |
---|
| 31 | out = p.stdout.read() |
---|
| 32 | err = p.stderr.read() |
---|
| 33 | p.communicate() |
---|
| 34 | return out,err |
---|
| 35 | except Exception(err): |
---|
| 36 | return '','Exception = '+err |
---|
| 37 | |
---|
[981] | 38 | project="GSAS-II" |
---|
[1356] | 39 | AppleScript = '' |
---|
| 40 | '''Contains an AppleScript to start GSAS-II launching python and the |
---|
| 41 | GSAS-II python script |
---|
| 42 | ''' |
---|
[981] | 43 | |
---|
[2803] | 44 | if __name__ == '__main__': AppleScript += '''(* GSAS-II AppleScript by B. Toby (brian.toby@anl.gov) |
---|
[981] | 45 | It can launch GSAS-II by double clicking or by dropping a data file |
---|
| 46 | or folder over the app. |
---|
| 47 | It runs GSAS-II in a terminal window. |
---|
| 48 | *) |
---|
| 49 | |
---|
| 50 | (* test if a file is present and exit with an error message if it is not *) |
---|
| 51 | on TestFilePresent(appwithpath) |
---|
| 52 | tell application "System Events" |
---|
| 53 | if (file appwithpath exists) then |
---|
| 54 | else |
---|
| 55 | display dialog "Error: file " & appwithpath & " not found. If you have moved this file recreate the AppleScript with bootstrap.py." with icon caution buttons {{"Quit"}} |
---|
| 56 | return |
---|
| 57 | end if |
---|
| 58 | end tell |
---|
| 59 | end TestFilePresent |
---|
| 60 | |
---|
| 61 | (* |
---|
| 62 | ------------------------------------------------------------------------ |
---|
| 63 | this section responds to a double-click. No file is supplied to GSAS-II |
---|
| 64 | ------------------------------------------------------------------------ |
---|
| 65 | *) |
---|
| 66 | on run |
---|
| 67 | set python to "{:s}" |
---|
| 68 | set appwithpath to "{:s}" |
---|
[1356] | 69 | set env to "{:s}" |
---|
[981] | 70 | TestFilePresent(appwithpath) |
---|
| 71 | TestFilePresent(python) |
---|
| 72 | tell application "Terminal" |
---|
| 73 | activate |
---|
[1356] | 74 | do script env & python & " " & appwithpath & "; exit" |
---|
[981] | 75 | end tell |
---|
| 76 | end run |
---|
| 77 | |
---|
| 78 | (* |
---|
| 79 | ----------------------------------------------------------------------------------------------- |
---|
| 80 | this section handles starting with files dragged into the AppleScript |
---|
| 81 | o it goes through the list of file(s) dragged in |
---|
| 82 | o then it converts the colon-delimited macintosh file location to a POSIX filename |
---|
| 83 | o for every non-directory file dragged into the icon, it starts GSAS-II, passing the file name |
---|
| 84 | ------------------------------------------------------------------------------------------------ |
---|
| 85 | *) |
---|
| 86 | |
---|
| 87 | on open names |
---|
| 88 | set python to "{:s}" |
---|
| 89 | set appwithpath to "{:s}" |
---|
[1356] | 90 | set env to "{:s}" |
---|
| 91 | |
---|
[981] | 92 | TestFilePresent(appwithpath) |
---|
| 93 | repeat with filename in names |
---|
| 94 | set filestr to (filename as string) |
---|
| 95 | if filestr ends with ":" then |
---|
| 96 | (* should not happen, skip directories *) |
---|
| 97 | else |
---|
| 98 | (* if this is an input file, open it *) |
---|
| 99 | set filename to the quoted form of the POSIX path of filename |
---|
| 100 | tell application "Terminal" |
---|
| 101 | activate |
---|
[1356] | 102 | do script env & python & " " & appwithpath & " " & filename & "; exit" |
---|
[981] | 103 | end tell |
---|
| 104 | end if |
---|
| 105 | end repeat |
---|
| 106 | end open |
---|
[1109] | 107 | ''' |
---|
[1356] | 108 | |
---|
| 109 | if __name__ == '__main__': |
---|
| 110 | # find the main GSAS-II script if not on command line |
---|
| 111 | if len(sys.argv) == 1: |
---|
| 112 | script = os.path.abspath(os.path.join( |
---|
| 113 | os.path.split(__file__)[0], |
---|
| 114 | "GSASII.py" |
---|
| 115 | )) |
---|
| 116 | elif len(sys.argv) == 2: |
---|
| 117 | script = os.path.abspath(sys.argv[1]) |
---|
| 118 | else: |
---|
| 119 | Usage() |
---|
| 120 | |
---|
| 121 | # make sure we found it |
---|
| 122 | if not os.path.exists(script): |
---|
| 123 | print("\nFile "+script+" not found") |
---|
| 124 | Usage() |
---|
| 125 | if os.path.splitext(script)[1].lower() != '.py': |
---|
| 126 | print("\nScript "+script+" does not have extension .py") |
---|
| 127 | Usage() |
---|
| 128 | # where the app will be created |
---|
| 129 | scriptdir = os.path.split(script)[0] |
---|
| 130 | # name of app |
---|
| 131 | apppath = os.path.abspath(os.path.join(scriptdir,project+".app")) |
---|
| 132 | iconfile = os.path.join(scriptdir,'gsas2.icns') # optional icon file |
---|
| 133 | |
---|
| 134 | # find the python application; must be an OS X app |
---|
[1357] | 135 | pythonpath = os.path.realpath(sys.executable) |
---|
| 136 | top = True |
---|
[1356] | 137 | while top: |
---|
[1357] | 138 | pythonapp = os.path.join(pythonpath,'Resources','Python.app','Contents','MacOS','Python') |
---|
| 139 | if os.path.exists(pythonapp): break |
---|
[1356] | 140 | pythonpath,top = os.path.split(pythonpath) |
---|
| 141 | else: |
---|
[2200] | 142 | #print("\nSorry, failed to find a Resources directory associated with "+str(sys.executable)) |
---|
[1356] | 143 | pythonapp = sys.executable |
---|
[1357] | 144 | |
---|
[1356] | 145 | # create a link to the python app, but named to match the project |
---|
| 146 | if os.path.exists('/tmp/testpython'): os.remove('/tmp/testpython') |
---|
| 147 | os.symlink(pythonapp,'/tmp/testpython') |
---|
| 148 | # test if it runs |
---|
| 149 | testout,errout = RunPython('/tmp/testpython','import numpy; import wx; print("OK")') |
---|
| 150 | os.remove('/tmp/testpython') |
---|
| 151 | #print testout,errout |
---|
| 152 | if testout.strip() != "OK": |
---|
[3166] | 153 | print('Run of wx in python failed, looking for pythonw') |
---|
| 154 | pythonapp = os.path.join(os.path.split(sys.executable)[0],'pythonw') |
---|
| 155 | if not os.path.exists(pythonapp): |
---|
| 156 | raise Exception('no pythonw found with '+sys.executable) |
---|
[1356] | 157 | newpython = pythonapp |
---|
| 158 | else: |
---|
| 159 | # new name to call python |
---|
| 160 | newpython = os.path.join(apppath,"Contents","MacOS",project) |
---|
| 161 | |
---|
| 162 | if os.path.exists(apppath): # cleanup |
---|
| 163 | print("\nRemoving old "+project+" app ("+str(apppath)+")") |
---|
| 164 | shutil.rmtree(apppath) |
---|
| 165 | |
---|
[1109] | 166 | shell = os.path.join("/tmp/","appscrpt.script") |
---|
| 167 | f = open(shell, "w") |
---|
[1356] | 168 | f.write(AppleScript.format(newpython,script,'',newpython,script,'')) |
---|
[1109] | 169 | f.close() |
---|
[981] | 170 | |
---|
[1109] | 171 | try: |
---|
| 172 | subprocess.check_output(["osacompile","-o",apppath,shell],stderr=subprocess.STDOUT) |
---|
[3166] | 173 | except subprocess.CalledProcessError as msg: |
---|
| 174 | print('''Error compiling AppleScript. |
---|
| 175 | Report the next message along with details about your Mac to toby@anl.gov''') |
---|
| 176 | print(msg.output) |
---|
[1109] | 177 | sys.exit() |
---|
[981] | 178 | |
---|
[1109] | 179 | # create a link to the python app, but named to match the project |
---|
[1356] | 180 | if pythonapp != newpython: os.symlink(pythonapp,newpython) |
---|
[981] | 181 | |
---|
[1109] | 182 | # change the icon |
---|
| 183 | oldicon = os.path.join(apppath,"Contents","Resources","droplet.icns") |
---|
| 184 | if os.path.exists(iconfile) and os.path.exists(oldicon): |
---|
| 185 | shutil.copyfile(iconfile,oldicon) |
---|
[981] | 186 | |
---|
[1109] | 187 | # Edit the app plist file to restrict the type of files that can be dropped |
---|
| 188 | d = plistlib.readPlist(os.path.join(apppath,"Contents",'Info.plist')) |
---|
| 189 | d['CFBundleDocumentTypes'] = [{ |
---|
| 190 | 'CFBundleTypeExtensions': ['gpx'], |
---|
| 191 | 'CFBundleTypeName': 'GSAS-II project', |
---|
| 192 | 'CFBundleTypeRole': 'Editor'}] |
---|
[1356] | 193 | |
---|
[1109] | 194 | plistlib.writePlist(d,os.path.join(apppath,"Contents",'Info.plist')) |
---|
[981] | 195 | |
---|
[1109] | 196 | print("\nCreated "+project+" app ("+str(apppath)+ |
---|
| 197 | ").\nViewing app in Finder so you can drag it to the dock if, you wish.") |
---|
| 198 | subprocess.call(["open","-R",apppath]) |
---|