如何从Python调用Objective C函数?
有没有办法从Python动态调用一个Objective C的函数呢?
比如说,在Mac上我想调用这个Objective C的函数
[NSSpeechSynthesizer availableVoices]
而不需要提前编译什么特殊的Python包装模块。
4 个回答
从10.5版本开始,Mac OS X自带了Python和objc模块,这样你就可以实现你想要的功能。
下面是一个例子:
from Foundation import *
thing = NSKeyedUnarchiver.unarchiveObjectWithFile_(some_plist_file)
你可以在这里找到更多的文档。
正如其他人提到的,PyObjC 是个不错的选择。不过,为了完整起见,这里也介绍一下如何使用 ctypes 来实现,特别是如果你需要在 10.5 之前的 OS X 版本上工作,而那时候还没有安装 PyObjC:
import ctypes
import ctypes.util
# Need to do this to load the NSSpeechSynthesizer class, which is in AppKit.framework
appkit = ctypes.cdll.LoadLibrary(ctypes.util.find_library('AppKit'))
objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
objc.objc_getClass.restype = ctypes.c_void_p
objc.sel_registerName.restype = ctypes.c_void_p
objc.objc_msgSend.restype = ctypes.c_void_p
objc.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
# Without this, it will still work, but it'll leak memory
NSAutoreleasePool = objc.objc_getClass('NSAutoreleasePool')
pool = objc.objc_msgSend(NSAutoreleasePool, objc.sel_registerName('alloc'))
pool = objc.objc_msgSend(pool, objc.sel_registerName('init'))
NSSpeechSynthesizer = objc.objc_getClass('NSSpeechSynthesizer')
availableVoices = objc.objc_msgSend(NSSpeechSynthesizer, objc.sel_registerName('availableVoices'))
count = objc.objc_msgSend(availableVoices, objc.sel_registerName('count'))
voiceNames = [
ctypes.string_at(
objc.objc_msgSend(
objc.objc_msgSend(availableVoices, objc.sel_registerName('objectAtIndex:'), i),
objc.sel_registerName('UTF8String')))
for i in range(count)]
print voiceNames
objc.objc_msgSend(pool, objc.sel_registerName('release'))
虽然这个方法看起来不太好,但它能完成任务。最终可用的名称列表存储在上面的 voiceNames
变量中。
2012-4-28 更新:修复了在 64 位 Python 版本中工作的方式,确保所有参数和返回类型都是以指针的形式传递,而不是 32 位整数。
从 OS X 10.5 开始,OS X 自带了一个叫做 PyObjC 的桥接工具,它是用来连接 Python 和 Objective-C 的。这个工具利用了 BridgeSupport 框架,把 Objective-C 的框架映射到 Python 上。和 MacRuby 不同,PyObjC 是一种经典的桥接方式——在 Python 这边每个 Objective-C 对象都有一个代理对象,反之亦然。这个桥接非常顺畅,你可以用 PyObjC 写完整的应用程序(Xcode 对 PyObjC 有一些基本的支持,你可以从上面的链接下载到应用和文件模板)。很多人用它来做一些小工具或者应用脚本/插件。苹果的开发者网站上也有一个关于如何通过 PyObjC 用 Python 开发 Cocoa 应用的 介绍,虽然有点过时,但对你了解整体情况可能有帮助。
在你的情况下,下面的代码会调用 [NSSpeechSynthesizer availableVoices]
:
from AppKit import NSSpeechSynthesizer
NSSpeechSynthesizer.availableVoices()
这段代码会返回
(
"com.apple.speech.synthesis.voice.Agnes",
"com.apple.speech.synthesis.voice.Albert",
"com.apple.speech.synthesis.voice.Alex",
"com.apple.speech.synthesis.voice.BadNews",
"com.apple.speech.synthesis.voice.Bahh",
"com.apple.speech.synthesis.voice.Bells",
"com.apple.speech.synthesis.voice.Boing",
"com.apple.speech.synthesis.voice.Bruce",
"com.apple.speech.synthesis.voice.Bubbles",
"com.apple.speech.synthesis.voice.Cellos",
"com.apple.speech.synthesis.voice.Deranged",
"com.apple.speech.synthesis.voice.Fred",
"com.apple.speech.synthesis.voice.GoodNews",
"com.apple.speech.synthesis.voice.Hysterical",
"com.apple.speech.synthesis.voice.Junior",
"com.apple.speech.synthesis.voice.Kathy",
"com.apple.speech.synthesis.voice.Organ",
"com.apple.speech.synthesis.voice.Princess",
"com.apple.speech.synthesis.voice.Ralph",
"com.apple.speech.synthesis.voice.Trinoids",
"com.apple.speech.synthesis.voice.Vicki",
"com.apple.speech.synthesis.voice.Victoria",
"com.apple.speech.synthesis.voice.Whisper",
"com.apple.speech.synthesis.voice.Zarvox"
)
(在我的 SL 机器上是一个桥接的 NSCFArray)。