2013-07-05 84 views
6

Mam skrypt Python, który chciałbym uruchomić albo klikając na coś lub przez upuszczenie pliku, który chcę otworzyć na tym rzecz. Chciałbym także uniknąć irytującego problemu z Pythonem, który jest nazwą interpretera (Python), a nie nazwą skryptu. Nie chcę używać py2app, ponieważ nie chcę pakować Pythona i chcę, aby pliki Pythona pozostały jako pliki tekstowe. Chciałbym mieć czysty Python (lub w najlepszym razie rozwiązanie skryptu powłoki). This pokazuje, jak zrobić wszystko, ale część "przeciągnij i upuść"; this pokazuje, jak zrobić to samo, tworząc pakiet, który pozwala kontrolować ikonę.Na komputerze Mac: jak utworzyć aplikację przeciągania i upuszczania dla skryptu Pythona, w którym nazwa skryptu zamiast Python pojawia się w MenuBar (itd.)?

Odpowiedz

2

Mam odpowiedź na moje pytanie, ale mam nadzieję, że ktoś może wymyślić coś mniej skomplikowanego. Rozwiązaniem, które wymyśliłem, był skrypt w języku Python, aby utworzyć AppleScript (ugh!), Skompilować go do pakietu, a następnie zmodyfikować pakiet, aby zmienić dozwolone rozszerzenia plików, ikonę i tak dalej.

Nie znalazłem sposobu na przeciąganie i upuszczanie podczas uruchamiania skryptu wxpython, bez AppleScript (lub za pomocą py2app) i nie mogłem uruchomić skryptu AppleScript w utworzonym pakiecie, musiałem użyj osacompile, aby utworzyć pakiet dla mnie.

Podobało mi się podejście here lub here, gdzie jeden tworzy plik plist "ręcznie", zamiast modyfikować utworzony zewnętrznie, tak jak tutaj, ale poniższy skrypt wykonuje zadanie.

#!/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]) 

Czy ktoś ma lepszy sposób to zrobić bez użycia py2app?

2

DnD plików na ikonę aplikacji można obsłużyć przesłonięcie metody MacOpenFiles obiektu wx.App.

Istnieje skrypt dystrybuowany w bibliotece Pythona w bibliotece lib/python2.7/plat-mac o nazwie bundlebuilder.py, który może być użyty do zbudowania pakietu aplikacji. Pochodzi z początków systemu OSX i prawdopodobnie jest nieaktualny pod pewnymi względami, ale działało, gdy ostatnio go wypróbowałem.

+0

Jestem trochę zdezorientowany. Kiedy początkowo wypróbowałem skrypt testowy, pomyślałem, że nadpisanie MacOpenFiles może zrobić to, co chcę, ale po wdrożeniu go w moim projekcie wydaje się, że MacOpenFiles odpowiadał tylko na pliki, które zostały upuszczone, gdy aplikacja była już uruchomiona. – bht