Re: [Pyobjc-dev] Proper way to expose custom Obj-C classes to Python?
Brought to you by:
ronaldoussoren
From: Ken T. <ke...@co...> - 2014-02-19 07:56:39
|
On Feb 18, 2014, at 5:23 PM, Ian McCullough wrote: > I'm trying to determine the best way to expose custom, > application-specific, non-system-framework (i.e. not in one of the > framework wrappers provided with PyObjC) classes written in Objective-C to > python across the PyObjC bridge. My eventual hope is to develop a "hybrid" > application in which some portions of the app are in Objective-C and others > are in python, with the two side integrating via the PyObjC bridge. Most of > the documentation I've been able to find seems to focus on situations where > the goal is to have all the app-specific code be in python, and to have the > python call out to AppKit classes, etc via the bridge and the pre-packaged > system framework wrappers. > > I've not seen any indication that it's possible to expose classes from an > app. It is. I don't know if this is the best way, but one way is to link an otherwise ordinary Cocoa app, written in Objective-C, to the Python framework. You would then use the Python embedding API to set up the Python runtime and load and run some Python code. You might do something like: NSString* pythonPath = /* path within your app bundle to its Python files */; setenv("PYTHONPATH", [pythonPath fileSystemRepresentation], 1); Py_SetProgramName("/usr/bin/python2.6"); // or whatever version of Python you link against; don't know how relevant this is Py_InitializeEx(0); PyEval_InitThreads(); PyGILState_STATE gilState = PyGILState_Ensure(); NSString* script = /* path to a initialization script in your app bundle */; static char* arg0 = strdup([script fileSystemRepresentation]); // don't deallocate until Python runtime is terminated, if ever static char** argv = &arg0; // ditto PySys_SetArgv(1, argv); FILE* scriptFile = fopen([script fileSystemRepresentation], "r"); int result = PyRun_SimpleFileEx(scriptFile, (char *)[[script lastPathComponent] fileSystemRepresentation], 1); if (result) /* handle error */; PyGILState_Release(gilState); if (gilState == PyGILState_LOCKED) { PyThreadState_Swap(NULL); PyEval_ReleaseLock(); } The script file mentioned above would just import modules and do initialization and then exit. It wouldn't be a long-running script. Since the application keeps running and the Python runtime is persistent, the state which is set up by the script persists, too. After that, you have a few ways of interacting between the Python code and the Objective-C code. Any Python class which inherits from a Cocoa class is registered with the Objective-C runtime. It can be looked up with NSClassFromString(). Then, it's methods can be invoked directly. You can make that simpler by declaring Objective-C interfaces for those classes. The Objective-C code can pass its objects into the methods of the Python classes and the Python code can then operate on those objects. Also, the Python code can find some objects itself, such as NSApp or various singletons. It can then do things like set itself as delegates or register for notifications. I believe that the classes of your app, since they were registered with the Objective-C runtime at the time that Python was loaded and your initialization script was run, can just be used directly in your Python scripts. If that doesn't work, I'm sure you can use Foundation.NSClassFromString() to get access. I hope that helps. Cheers, Ken |