2009-09-29 20 views
7

C'è un modo per chiamare dinamicamente una funzione Objective C da Python?chiama le funzioni Objective C da Python?

Ad esempio, su Mac Vorrei richiamare questa funzione Objective C

[NSSpeechSynthesizer availableVoices] 

senza dover precompilare qualsiasi modulo speciale Python avvolgitore.

risposta

6

Poiché OS X 10.5, OS X è stato fornito con il bridge PyObjC, un bridge Python-Objective-C. Usa il framework BridgeSupport per mappare i framework Objective-C su Python. A differenza di MacRuby, PyObjC è un bridge classico: esiste un oggetto proxy sul lato python per ogni oggetto ObjC e viceversa. Il bridge è abbastanza semplice, tuttavia, ed è possibile scrivere intere app in PyObjC (Xcode ha un supporto PyObjC di base, e puoi scaricare i modelli di app e file per Xcode da PyObjC SVN al link precedente). Molti lo usano per utilità o per app-scripting/plug-in. Il sito per gli sviluppatori di Apple ha anche uno introduction per lo sviluppo di applicazioni Cocoa con Python tramite PyObjC, che è leggermente obsoleto, ma potrebbe essere una buona panoramica per voi.

Nel tuo caso, il seguente codice chiamerà [NSSpeechSynthesizer availableVoices]:

from AppKit import NSSpeechSynthesizer 

NSSpeechSynthesizer.availableVoices() 

che restituisce

(
    "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" 
) 

(un NSCFArray ponte) sulla mia macchina SL.

3

Probabilmente si desidera PyObjC. Detto questo, in realtà non l'ho mai usato (ho visto solo demo), quindi non sono sicuro che farà ciò di cui hai bisogno.

4

Mac OS X dalla 10.5 in poi è stato fornito con Python e il modulo objc che consente di fare ciò che si desidera.

Un esempio:

from Foundation import * 

thing = NSKeyedUnarchiver.unarchiveObjectWithFile_(some_plist_file) 

È possibile trovare maggiori documentazione here.

+0

+1 per esempio. –

+1

Non è necessario importare il modulo objc, solo Foundation. –

+0

Hah, hai ragione. Aggiornerò il mio esempio – Benno

19

Come altri hanno già menzionato, PyObjC è la strada da percorrere. Ma, per completezza, ecco come si può fare con ctypes, in caso di necessità di lavorare su versioni di OS X precedenti alla 10.5 che non hanno PyObjC installati:

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')) 

Non è abbastanza , ma ha fatto il lavoro. L'elenco finale dei nomi disponibili è memorizzato nella variabile voiceNames sopra.

2012-4-28 Aggiornamento: corretto per funzionare in build Python a 64 bit assicurandosi che tutti i parametri e i tipi restituiti vengano passati come puntatori anziché interi a 32 bit.

+0

Ciao, questo si blocca su 10.6 - qualche idea del perché? –

+3

@Ecir: Grazie per il suggerimento, sono sorpreso che il codice originale abbia funzionato in primo luogo. Il problema era che tutti i puntatori (puntatori di classe e puntatori di istanza) venivano troncati a 32 bit a causa del modo in cui funzionavano i ctypes, con conseguenti arresti anomali. Per risolvere il problema, ho modificato il codice per impostare tutti i tipi di risultati e i tipi di argomenti in modo esplicito come puntatori. –

+0

Grazie, lavora ora. –

Problemi correlati