从Python调用Objective C函数?

12

有没有一种方法可以从Python动态调用Objective-C函数?

例如,在Mac上,我想调用这个Objective C函数

[NSSpeechSynthesizer availableVoices]

无需预编译任何特殊的Python包装器模块。

4个回答

25

正如其他人提到的那样,PyObjC是正确的选择。但是,为了完整起见,以下是使用ctypes的方法,以防您需要在没有安装PyObjC的OS X版本上运行:

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 更新:通过确保所有参数和返回类型都作为指针而不是32位整数传递,修复64位Python构建中的问题。


1
你好,这在10.6上崩溃了 - 有什么想法吗? - Ecir Hana
4
@Ecir:谢谢您的提示,我很惊讶原始代码一开始竟然能够工作。问题在于由于ctypes的工作方式,所有指针(类指针和实例指针)都被截断为32位,导致崩溃。为了解决这个问题,我改变了代码,将所有结果类型和参数类型明确设置为指针。 - Adam Rosenfield
1
似乎 Mavericks 不再附带 PyObjC,因此这个答案变得重要了! - boxed

10
自从OS X 10.5以来,OS X就内置了Python-Objective-C桥梁PyObjC。它使用BridgeSupport框架将Objective-C框架映射到Python。与MacRuby不同,PyObjC是一个经典的桥梁,即Python端有一个代理对象对应每个ObjC对象,反之亦然。但是,这座桥梁非常无缝,可以在PyObjC中编写整个应用程序(Xcode具有基本的PyObjC支持,并且您可以从上面的链接下载Xcode的应用程序和文件模板)。许多人将其用于实用工具或应用程序脚本/插件。苹果的开发者网站还提供有关如何通过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。

1
你分享的介绍链接已经失效了。您可能是指这个链接吗?https://opensource.apple.com/source/pyobjc/pyobjc-47/pyobjc/pyobjc-core-2.5.1/Doc/sphinx_build/html/tutorials/firstapp.html - kcstricks

4

从10.5版本开始,Mac OS X已经预装了Python和objc模块,这将使您能够实现您想要的功能。

例如:

from Foundation import *

thing = NSKeyedUnarchiver.unarchiveObjectWithFile_(some_plist_file)

您可以在这里找到更多的文档。

1
你不需要导入objc模块,只需导入Foundation。 - Barry Wark

3
你可能需要使用PyObjC来实现你的目标。但是,我自己从未使用过它(只看过演示),所以我不能确定它是否能满足你的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接