source: trunk/makeMacApp.py @ 4636

Last change on this file since 4636 was 3997, checked in by toby, 6 years ago

Update the mac install to use python rather than pythonw, should now use GSAS-II as app name

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 8.3 KB
Line 
1#!/usr/bin/env python
2'''
3*makeMacApp: Create Mac Applet*
4===============================
5
6This script creates an AppleScript app bundle to launch GSAS-II. The app is
7created in the directory where the GSAS-II script (GSASII.py) is located.
8A softlink to Python is created, but is named GSAS-II, and placed inside
9the bundle so that GSAS-II shows up as the name of the app rather than
10Python in the menu bar, etc. A soft link named GSAS-II.py, referencing
11GSASII.py, is created so that appropriate menu items also are labeled with
12GSAS-II (but not the right capitalization, alas).
13
14This has been tested with several versions of Python interpreters
15from Anaconda and does not require pythonw (Python.app). It tests to
16make sure that a wx python script will run inside Python and if not,
17it searches for a pythonw image and tries that.
18
19Run this script with no arguments or with one optional argument,
20a reference to the GSASII.py, with a relative or absolute path. Either way
21the path will be converted via an absolute path. If no arguments are
22supplied, the GSASII.py script must be located in the same directory as
23this file.
24
25'''
26from __future__ import division, print_function
27import sys, os, os.path, stat, shutil, subprocess, plistlib
28def Usage():
29    print("\n\tUsage: python "+sys.argv[0]+" [<GSAS-II script>]\n")
30    sys.exit()
31
32def RunPython(image,cmd):
33    'Run a command in a python image'
34    try:
35        err=None
36        p = subprocess.Popen([image,'-c',cmd],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
37        out = p.stdout.read()
38        err = p.stderr.read()
39        p.communicate()
40        return out,err
41    except Exception(err):
42        return '','Exception = '+err
43
44AppleScript = ''
45'''Contains an AppleScript to start GSAS-II, launching Python and the
46GSAS-II python script
47'''
48
49if __name__ == '__main__':
50    project="GSAS-II"
51
52    # set scriptdir: find the main GSAS-II script if not on command line
53    if len(sys.argv) == 1:
54        script = os.path.abspath(os.path.join(
55                os.path.split(__file__)[0],
56                "GSASII.py"
57                ))
58    elif len(sys.argv) == 2:
59            script = os.path.abspath(sys.argv[1])
60    else:
61        Usage()
62        raise Exception
63    # make sure we found it
64    if not os.path.exists(script):
65        print("\nFile "+script+" not found")
66        Usage()
67    if os.path.splitext(script)[1].lower() != '.py':
68        print("\nScript "+script+" does not have extension .py")
69        Usage()
70    # where the app will be created
71    scriptdir = os.path.split(script)[0]
72   
73    appPath = os.path.abspath(os.path.join(scriptdir,project+".app"))
74    iconfile = os.path.join(scriptdir,'gsas2.icns') # optional icon file
75   
76    AppleScript = '''(*   GSAS-II AppleScript by B. Toby (brian.toby@anl.gov)
77     It can launch GSAS-II by double clicking or by dropping a data file
78     or folder over the app.
79     It runs GSAS-II in a terminal window.
80*)
81
82(* test if a file is present and exit with an error message if it is not  *)
83on TestFilePresent(appwithpath)
84        tell application "System Events"
85                if (file appwithpath exists) then
86                else
87                        display dialog "Error: file " & appwithpath & " not found. If you have moved this file recreate the AppleScript with bootstrap.py." with icon caution buttons {{"Quit"}}
88                        return
89                end if
90        end tell
91end TestFilePresent
92
93(*
94------------------------------------------------------------------------
95this section responds to a double-click. No file is supplied to GSAS-II
96------------------------------------------------------------------------
97*)
98on run
99        set python to "{:s}"
100        set appwithpath to "{:s}"
101        set env to "{:s}"
102        TestFilePresent(appwithpath)
103        TestFilePresent(python)
104        tell application "Terminal"
105                activate
106                do script env & python & " " & appwithpath & "; exit"
107        end tell
108end run
109
110(*
111-----------------------------------------------------------------------------------------------
112this section handles starting with files dragged into the AppleScript
113 o it goes through the list of file(s) dragged in
114 o then it converts the colon-delimited macintosh file location to a POSIX filename
115 o for every non-directory file dragged into the icon, it starts GSAS-II, passing the file name
116------------------------------------------------------------------------------------------------
117*)
118
119on open names
120        set python to "{:s}"
121        set appwithpath to "{:s}"
122        set env to "{:s}"
123 
124        TestFilePresent(appwithpath)
125        repeat with filename in names
126                set filestr to (filename as string)
127                if filestr ends with ":" then
128                        (* should not happen, skip directories *)
129                else
130                        (* if this is an input file, open it *)
131                        set filename to the quoted form of the POSIX path of filename
132                        tell application "Terminal"
133                                activate
134                                do script env & python & " " & appwithpath & " " & filename & "; exit"
135                        end tell
136                end if
137        end repeat
138end open
139'''
140    # create a link named GSAS-II.py to the script
141    newScript = os.path.join(scriptdir,project+'.py')
142    if os.path.exists(newScript): # cleanup
143        print("\nRemoving sym link",newScript)
144        os.remove(newScript)
145    os.symlink(os.path.split(script)[1],newScript)
146    script=newScript
147
148    # find Python used to run GSAS-II and set a new to use to call it
149    # inside the app that will be created
150    pythonExe = os.path.realpath(sys.executable)
151    newpython =  os.path.join(appPath,"Contents","MacOS",project)
152
153    # create an app using this python and if that fails to run wx, look for a
154    # pythonw and try that with wx
155    for i in 1,2,3:
156        if os.path.exists(appPath): # cleanup
157            print("\nRemoving old "+project+" app ("+str(appPath)+")")
158            shutil.rmtree(appPath)
159       
160        shell = os.path.join("/tmp/","appscrpt.script")
161        f = open(shell, "w")
162        f.write(AppleScript.format(newpython,script,'',newpython,script,''))
163        f.close()
164
165        try: 
166            subprocess.check_output(["osacompile","-o",appPath,shell],stderr=subprocess.STDOUT)
167        except subprocess.CalledProcessError as msg:
168            print('''Error compiling AppleScript.
169            Report the next message along with details about your Mac to toby@anl.gov''')
170            print(msg.output)
171            sys.exit()
172        # create a link to the python inside the app, if named to match the project
173        if pythonExe != newpython: os.symlink(pythonExe,newpython)
174
175        # test if newpython can run wx
176        testout,errout = RunPython(newpython,'import numpy; import wx; wx.App(); print("-OK-")')
177        if isinstance(testout,bytes): testout = testout.decode()
178        if "-OK-" in testout:
179            break
180        elif i == 1:
181            print('Run of wx in',pythonExe,'failed, looking for pythonw')
182            pythonExe = os.path.join(os.path.split(sys.executable)[0],'pythonw')
183            if not os.path.exists(pythonExe):
184                print('Warning no pythonw found with ',sys.executable,
185                      '\ncontinuing, hoping for the best')
186        elif i == 2:
187            print('Warning could not run wx with',pythonExe,
188                      'will try with that external to app')
189            newpython = pythonExe
190        else:
191            print('Warning still could not run wx with',pythonExe,
192                      '\ncontinuing, hoping for the best')
193
194    # change the icon
195    oldicon = os.path.join(appPath,"Contents","Resources","droplet.icns")
196    #if os.path.exists(iconfile) and os.path.exists(oldicon):
197    if os.path.exists(iconfile):
198        shutil.copyfile(iconfile,oldicon)
199
200    # Edit the app plist file to restrict the type of files that can be dropped
201    if hasattr(plistlib,'load'):
202        fp = open(os.path.join(appPath,"Contents",'Info.plist'),'rb')
203        d = plistlib.load(fp)
204        fp.close()
205    else:
206        d = plistlib.readPlist(os.path.join(appPath,"Contents",'Info.plist'))
207    d['CFBundleDocumentTypes'] = [{
208        'CFBundleTypeExtensions': ['gpx'],
209        'CFBundleTypeName': 'GSAS-II project',
210        'CFBundleTypeRole': 'Editor'}]
211   
212    if hasattr(plistlib,'dump'):
213        fp = open(os.path.join(appPath,"Contents",'Info.plist'),'wb')
214        plistlib.dump(d,fp)
215        fp.close()
216    else:
217        plistlib.writePlist(d,os.path.join(appPath,"Contents",'Info.plist'))
218
219    print("\nCreated "+project+" app ("+str(appPath)+
220          ").\nViewing app in Finder so you can drag it to the dock if, you wish.")
221    subprocess.call(["open","-R",appPath])
Note: See TracBrowser for help on using the repository browser.