Ho uno script Python che vorrei avviare facendo clic su qualcosa o rilasciando un file che voglio aprire su quella cosa. Voglio anche evitare il fastidioso problema con Mac Python che il nome dell'interprete (Python) piuttosto che il nome dello script. Non voglio usare py2app, dato che non voglio raggruppare Python e voglio che i file python rimangano come file di testo. Mi piacerebbe un puro Python (o la migliore soluzione di script di shell.) This mostra come fare tutto, ma la parte drag and drop; this mostra come fare lo stesso creando un pacchetto, che consente di controllare l'icona.In Mac, come creare un'app drag & drop per uno script python in cui il nome dello script piuttosto che Python viene visualizzato nel MenuBar (ecc.)?
risposta
Ho una risposta alla mia domanda, ma spero che qualcuno possa inventare qualcosa di meno complesso. La soluzione che ho trovato era uno script Python per creare un AppleScript (ugh!), Compilarlo in un bundle e quindi modificare il pacchetto per modificare le estensioni di file consentite, l'icona e così via.
Non ho trovato un modo per ottenere il trascinamento durante l'avvio di uno script wxpython, senza un AppleScript (o usando py2app) e non ho potuto far funzionare l'AppleScript in un pacchetto che ho creato, ho dovuto usa osacompile per creare il pacchetto per me.
Mi piace l'approccio here o here, dove si crea il file plist "a mano" piuttosto che modificare uno creato esternamente come faccio qui, ma lo script di seguito fa il lavoro.
#!/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])
Qualcuno ha un modo migliore per farlo senza usare py2app?
DnD dei file sull'icona dell'applicazione può essere gestito sovrascrivendo il metodo MacOpenFiles dell'oggetto wx.App.
C'è uno script distribuito nella libreria Python in lib/python2.7/plat-mac chiamato bundlebuilder.py che può essere utilizzato per aiutare a costruire un pacchetto di applicazione. È fin dai primi giorni di OSX ed è probabilmente obsoleto in qualche modo, ma ha funzionato l'ultima volta che l'ho provato.
Sono un po 'confuso su questo. Quando inizialmente ho provato uno script di test, ho pensato che sovrascrivere MacOpenFiles avrebbe fatto ciò che volevo, ma dopo averlo implementato nel mio progetto, sembra che MacOpenFiles abbia risposto solo ai file che erano stati rilasciati quando l'applicazione era già in esecuzione. – bht