On a Mac, how to create a drag & drop application for a python script where the name of the script is displayed in the MenuBar, not Python (etc.)?

I have a Python script that I would like to run by clicking on something or dropping the file that I want to open on this. I also want to avoid the annoying problem with Mac Python that the name of the interpreter (Python) and not the name of the script. I don't want to use py2app since I don't want to bind Python and I want the python files to remain as text files. I would like to get pure Python (or at best a shell script solution.) This shows how to do everything, but part of the drag and drop; this shows how to do the same by creating a package that lets you control the icon.

+6
source share
2 answers

I have an answer to my question, but I hope someone can come up with something less complicated. The solution I came across is a Python script to create an AppleScript (ugh!), Compile it into a package and then modify the package to change the allowed file extensions, icon, etc.

I did not find a way to make drag-and-drop work when running the wxpython script without AppleScript (or using py2app), and I could not get AppleScript to work in the bundle that I created, use osacompile to create the package for me.

I like the more convenient approach here or here , where you create the plist file β€œmanually”, and not modify the externally created one, as I am here, but below the script does the job.

#!/usr/bin/env python '''This script creates an AppleScript app to launch a python script. The app is created in the current working directory. A softlink is made to an app version of the current python (with name .../Resources/Python.app/Contents/MacOS/Python and found in the directory tree of calling python interpreter), but with the name of the app to be created, this means that the app name shows up in the Mac menus. Run this script with one, two or three arguments: <python script> <project name> <icon file> The python script path may be specified relative to the current path or given an absolute path, but will be accessed via an absolute path in the AppleScript. If the project name is not specified, it will be taken from the root name of the script. If the icon file is not specified, it will be assumed to be the same as the python script, but with the extension changed from .py to .icns. (Note that image files can be saved in ICNS format in Preview.). No error occurs if the icon file does not exist. ''' import sys, os, os.path, stat, shutil, subprocess, plistlib allowedfiletypes = ['txt',] allowedtypedesc = 'Text Files' scripttype = 'Editor' # use 'Viewer' if the script does not change the file def Usage(): print("\n\tUsage: python "+sys.argv[0]+" <python script> [<project name>] [<icon file>]\n") sys.exit() if not 2 <= len(sys.argv) <= 4: Usage() script = os.path.abspath(sys.argv[1]) if not os.path.exists(script): print("\nFile "+script+" not found") Usage() if os.path.splitext(script)[1].lower() != '.py': print("\nScript "+script+" does not have extension .py") Usage() # make sure we found it if not os.path.exists(script): print("\nFile "+script+" not found") Usage() if len(sys.argv) >= 3: project = sys.argv[2] else: project = os.path.splitext(os.path.split(script)[1])[0] if len(sys.argv) == 4: iconfile = sys.argv[3] else: iconfile = os.path.splitext(script)[0]+'.icns' # app will be created in current working directory apppath = os.path.abspath(os.path.join('.',project+".app")) # full path to app bundle # find the python application; which must be an OS X app pythonpath,top = os.path.split(os.path.realpath(sys.executable)) while top: if 'Resources' in pythonpath: pass elif os.path.exists(os.path.join(pythonpath,'Resources')): break pythonpath,top = os.path.split(pythonpath) else: print("\nSorry, failed to find a Resources directory associated with "+str(sys.executable)) sys.exit() pythonapp = os.path.join(pythonpath,'Resources','Python.app','Contents','MacOS','Python') if not os.path.exists(pythonapp): print("\nSorry, failed to find a Python app in "+str(pythonapp)) sys.exit() # new name to call python newpython = os.path.join(apppath,"Contents","MacOS",project) if os.path.exists(apppath): # cleanup print("\nRemoving old "+project+" app ("+str(apppath)+")") shutil.rmtree(apppath) # create an AppleScript that launches python with the requested app shell = os.path.join("/tmp/","appscrpt.script") f = open(shell, "w") f.write('''(* drag and drop AppleScript It can launch a python script by double clicking or by dropping a data file over the app. It runs the script in a terminal window. *) (* test if a file is present and exit with an error message if it is not *) on TestFilePresent(appwithpath) tell application "System Events" if (file appwithpath exists) then else display dialog "Error: file " & appwithpath & " not found. If you have moved this file, recreate the AppleScript." with icon caution buttons {{"Quit"}} return end if end tell end TestFilePresent (* ------------------------------------------------------------------------ this section responds to a double-click. No file is supplied ------------------------------------------------------------------------ *) on run set python to "{:s}" set appwithpath to "{:s}" TestFilePresent(appwithpath) TestFilePresent(python) tell application "Terminal" activate do script python & " " & appwithpath & "; exit" end tell end run (* ----------------------------------------------------------------------------------------------- this section handles starting with files dragged into the AppleScript o it goes through the list of file(s) dragged in o then it converts the colon-delimited macintosh file location to a POSIX filename o for every non-directory file dragged into the icon, it starts GSAS-II, passing the file name ------------------------------------------------------------------------------------------------ *) on open names set python to "{:s}" set appwithpath to "{:s}" TestFilePresent(appwithpath) repeat with filename in names set filestr to (filename as string) if filestr ends with ":" then (* should not happen, skip directories *) else (* if this is an input file, open it *) set filename to the quoted form of the POSIX path of filename tell application "Terminal" activate do script python & " " & appwithpath & " " & filename & "; exit" end tell end if end repeat end open '''.format(newpython,script,newpython,script)) f.close() try: subprocess.check_output(["osacompile","-o",apppath,shell],stderr=subprocess.STDOUT) except subprocess.CalledProcessError, msg: print('Error compiling AppleScript:') print msg.output sys.exit() # create a link to the python app, but named to match the project os.symlink(pythonapp,newpython) # change the icon oldicon = os.path.join(apppath,"Contents","Resources","droplet.icns") if os.path.exists(iconfile) and os.path.exists(oldicon): shutil.copyfile(iconfile,oldicon) # Edit the app plist file to restrict the type of files that can be dropped d = plistlib.readPlist(os.path.join(apppath,"Contents",'Info.plist')) d['CFBundleDocumentTypes'] = [{ 'CFBundleTypeExtensions': allowedfiletypes, 'CFBundleTypeName': allowedtypedesc, 'CFBundleTypeRole': scripttype}] plistlib.writePlist(d,os.path.join(apppath,"Contents",'Info.plist')) print("\nCreated "+project+" app ("+str(apppath)+ ").\nViewing app in Finder so you can drag it to the dock if, you wish.") subprocess.call(["open","-R",apppath]) 

Is there anyone better way to do this without using py2app?

+2
source

DnD files per application icon can be processed by overriding the MacOpenFiles method of your wx.App object.

There is a script distributed in the Python library in lib / python2.7 / plat-mac called bundlebuilder.py that can be used to create a set of applications. This is from the early days of OSX and probably outdated somehow, but it worked the last time I tried it.

+2
source

Source: https://habr.com/ru/post/948752/


All Articles