2013-07-05 17 views
6

我有一個Python腳本,我想通過點擊某個東西或通過刪除一個我想在該東西上打開的文件來啓動。我也想避免討厭的mac Python問題,即解釋器的名字(Python)而不是腳本的名字。我不想使用py2app,因爲我不想捆綁Python,並希望python文件保持爲文本文件。我想要一個純Python(或最好的shell腳本解決方案。)This顯示瞭如何做所有事情,但拖放部分; this顯示瞭如何通過創建一個允許控制圖標的包來執行相同操作。在Mac上,如何爲MenuBar(等)中腳本名稱而不是Python顯示的python腳本創建拖放應用程序?

回答

2

我對我的問題有一個答案,但我希望有人能想出一些不太複雜的東西。我想出的解決方案是創建AppleScript(Python)的Python腳本,將其編譯爲一個包,然後修改該包以更改允許的文件擴展名,圖標等等。

我沒有找到一種方法來獲得當啓動一個wxpython腳本,沒有AppleScript(或使用py2app)時的拖放工作,我無法讓AppleScript在我創建的包中工作,我不得不使用osacompile爲我創建捆綁軟件。

我喜歡更好的方法herehere,其中一個創建plist文件「手動」而不是修改外部創建一個像我這樣做,但下面的腳本確實做這項工作。

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

有沒有人有更好的方式來做到這一點,而不使用py2app?

2

通過覆蓋wx.App對象的MacOpenFiles方法可以處理應用程序圖標上的文件的DnD。

在lib/python2.7/plat-mac中的Python庫中存在一個腳本,名爲bundlebuilder.py,可用於幫助您構建應用程序包。它是從OSX的早期開始的,並且在某些方面可能已經過時,但是它在我最後一次嘗試它的時候起作用了。

+0

我對此有點困惑。當我最初嘗試一個測試腳本時,我認爲覆蓋MacOpenFiles會做我想做的事情,但是在我的項目中實現它之後,似乎MacOpenFiles只響應在應用程序已經運行時被刪除的文件。 – bht