1 | #!/usr/bin/env python |
---|
2 | ''' |
---|
3 | *makeMacApp: Create Mac Applet* |
---|
4 | =============================== |
---|
5 | |
---|
6 | This script creates an AppleScript app to launch GSAS-II. The app is |
---|
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 | |
---|
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 | |
---|
38 | project="GSAS-II" |
---|
39 | AppleScript = '' |
---|
40 | '''Contains an AppleScript to start GSAS-II launching python and the |
---|
41 | GSAS-II python script |
---|
42 | ''' |
---|
43 | |
---|
44 | if __name__ == '__main__': AppleScript += '''(* GSAS-II AppleScript by B. Toby (brian.toby@anl.gov) |
---|
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}" |
---|
69 | set env to "{:s}" |
---|
70 | TestFilePresent(appwithpath) |
---|
71 | TestFilePresent(python) |
---|
72 | tell application "Terminal" |
---|
73 | activate |
---|
74 | do script env & python & " " & appwithpath & "; exit" |
---|
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}" |
---|
90 | set env to "{:s}" |
---|
91 | |
---|
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 |
---|
102 | do script env & python & " " & appwithpath & " " & filename & "; exit" |
---|
103 | end tell |
---|
104 | end if |
---|
105 | end repeat |
---|
106 | end open |
---|
107 | ''' |
---|
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 |
---|
135 | pythonpath = os.path.realpath(sys.executable) |
---|
136 | top = True |
---|
137 | while top: |
---|
138 | pythonapp = os.path.join(pythonpath,'Resources','Python.app','Contents','MacOS','Python') |
---|
139 | if os.path.exists(pythonapp): break |
---|
140 | pythonpath,top = os.path.split(pythonpath) |
---|
141 | else: |
---|
142 | #print("\nSorry, failed to find a Resources directory associated with "+str(sys.executable)) |
---|
143 | pythonapp = sys.executable |
---|
144 | |
---|
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": |
---|
153 | print 'Run of python app failed, resorting to non-app version of Python, Alas!' |
---|
154 | pythonapp = sys.executable |
---|
155 | # is this brain-dead Canopy 1.4.0, if so, switch to pythonw |
---|
156 | try: |
---|
157 | import canopy.version |
---|
158 | if canopy.version.version == '1.4.0': |
---|
159 | print 'using pythonw for Canopy 1.4.0' |
---|
160 | pythonapp = os.path.join(os.path.split(pythonapp)[0],'pythonw') |
---|
161 | if not os.path.exists(pythonapp): |
---|
162 | raise Exception('no pythonw here: '+pythonapp) |
---|
163 | except ImportError: |
---|
164 | pass |
---|
165 | newpython = pythonapp |
---|
166 | else: |
---|
167 | # new name to call python |
---|
168 | newpython = os.path.join(apppath,"Contents","MacOS",project) |
---|
169 | |
---|
170 | if os.path.exists(apppath): # cleanup |
---|
171 | print("\nRemoving old "+project+" app ("+str(apppath)+")") |
---|
172 | shutil.rmtree(apppath) |
---|
173 | |
---|
174 | shell = os.path.join("/tmp/","appscrpt.script") |
---|
175 | f = open(shell, "w") |
---|
176 | f.write(AppleScript.format(newpython,script,'',newpython,script,'')) |
---|
177 | f.close() |
---|
178 | |
---|
179 | try: |
---|
180 | subprocess.check_output(["osacompile","-o",apppath,shell],stderr=subprocess.STDOUT) |
---|
181 | except subprocess.CalledProcessError, msg: |
---|
182 | print '''Error compiling AppleScript. |
---|
183 | Report the next message along with details about your Mac to toby@anl.gov''' |
---|
184 | print msg.output |
---|
185 | sys.exit() |
---|
186 | |
---|
187 | # create a link to the python app, but named to match the project |
---|
188 | if pythonapp != newpython: os.symlink(pythonapp,newpython) |
---|
189 | |
---|
190 | # change the icon |
---|
191 | oldicon = os.path.join(apppath,"Contents","Resources","droplet.icns") |
---|
192 | if os.path.exists(iconfile) and os.path.exists(oldicon): |
---|
193 | shutil.copyfile(iconfile,oldicon) |
---|
194 | |
---|
195 | # Edit the app plist file to restrict the type of files that can be dropped |
---|
196 | d = plistlib.readPlist(os.path.join(apppath,"Contents",'Info.plist')) |
---|
197 | d['CFBundleDocumentTypes'] = [{ |
---|
198 | 'CFBundleTypeExtensions': ['gpx'], |
---|
199 | 'CFBundleTypeName': 'GSAS-II project', |
---|
200 | 'CFBundleTypeRole': 'Editor'}] |
---|
201 | |
---|
202 | plistlib.writePlist(d,os.path.join(apppath,"Contents",'Info.plist')) |
---|
203 | |
---|
204 | print("\nCreated "+project+" app ("+str(apppath)+ |
---|
205 | ").\nViewing app in Finder so you can drag it to the dock if, you wish.") |
---|
206 | subprocess.call(["open","-R",apppath]) |
---|