【问题标题】:calling Objective C functions from Python?从 Python 调用 Objective C 函数?
【发布时间】:2010-12-02 04:20:07
【问题描述】:

有没有办法从 Python 中动态调用 Objective C 函数?

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

[NSSpeechSynthesizer availableVoices]

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

【问题讨论】:

    标签: python objective-c macos


    【解决方案1】:

    你可能想要PyObjC。也就是说,我自己从未真正使用过它(我只看过演示),所以我不确定它是否能满足您的需求。

    【讨论】:

      【解决方案2】:

      Mac OS X 从 10.5 开始就附带了 Python 和 objc 模块,让您可以随心所欲。

      一个例子:

      from Foundation import *
      
      thing = NSKeyedUnarchiver.unarchiveObjectWithFile_(some_plist_file)
      

      您可以找到更多文档here

      【讨论】:

      • 你不需要导入objc模块,只需要Foundation。
      【解决方案3】:

      从 OS X 10.5 开始,OS X 附带了 PyObjC 桥接器,这是一个 Python-Objective-C 桥接器。它使用BridgeSupport 框架将Objective-C 框架映射到Python。与 MacRuby 不同,PyObjC 是一个经典的桥梁——每个 ObjC 对象在 python 端都有一个代理对象,反之亦然。然而,这个桥是非常无缝的,它可以在 PyObjC 中编写整个应用程序(Xcode 有一些基本的 PyObjC 支持,你可以在上面的链接中从 PyObjC SVN 下载 Xcode 的应用程序和文件模板)。许多人将它用于实用程序或应用程序脚本/插件。 Apple 的开发者网站也有一个 introduction 用于通过 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"
      )
      

      (一个桥接的 NSCFArray)在我的 SL 机器上。

      【讨论】:

      【解决方案4】:

      正如其他人所提到的,PyObjC 是要走的路。但是,为了完整起见,如果您需要它在没有安装 PyObjC 的 10.5 之前的 OS X 版本上工作,您可以使用 ctypes 执行此操作:

      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 构建中工作。

      【讨论】:

      • 您好,这在 10.6 上崩溃了 - 知道为什么吗?
      • @Ecir:感谢您的提示,我很惊讶原始代码首先可以工作。问题是由于 ctypes 的工作方式,所有指针(类指针和实例指针)都被截断为 32 位,从而导致崩溃。为了解决这个问题,我更改了代码,将所有结果类型和参数类型设置为显式指针。
      • 似乎小牛不再附带 pyobjc 所以这个答案再次变得相关!
      猜你喜欢
      • 1970-01-01
      • 2011-10-20
      • 2023-04-08
      • 2014-07-27
      • 1970-01-01
      • 2022-01-02
      • 1970-01-01
      • 2010-11-06
      相关资源
      最近更新 更多