With the GUI/addon system use case in mind, I need my C++ code to call into a Python script/module and I also need my Python scripts to interact with C++ objects. Here's a simple example that I've got in mind for this process:
# import the pyd that interfaces python with my c++ library
from az_gui import *
def onCancelButton(self, event):
Event handler for cancel button.
window = self.getWindow()
I've decided to split this topic into several postings. This first post covers the ground work necessary for calling into Python scripts. The second posting will review a PythonCallback wrapper class that is designed to encapsulate the Python C API code for calling function callbacks in python modules. After that I'll have a post or two on my Boost.Python experiences that expose my C++ libraries to Python (e.g., allowing for Python code to call the C++ class, Window as shown in the example above).
C++ calls into Python scripts:
Went with the Python C API for communication in this direction, since it was a manageable process. I'm resistant to adding 3rdParty dependencies to my project, so this seemed like the best I could do. On a side note, my original goal was to write my own Python C API wrappers to also exposing my c++ classes to Python scripts, but that panned out to be an enormous undertaking that I'm not ready to tackle yet. (this discussion is saved for another post).
There were a few catches along the way that made the implementation less straightfoward. For starters, a standard install of Python does not have a debug library. As such, some macro magic needs to be performed to include Python.h header file.
// Since Python may define some pre-processor definitions which affect
// the standard headers on some systems, you must include Python.h before
// any standard headers are included.
It is my preference to keep this Python.h ugliness inside the cpp files, so my next trick involved forward declaring PyObject:
// forward declare PyObject
// as suggested on the python mailing list
typedef _object PyObject;
Pulling it all together:
To manage the Python C API, I created a PythonManager class to encapsulate the Python interpreter session used within my codebase.
// add directories to the python interpreter's sys.path
// to allow for Python scripts to locate script directories.
bool addSysPath(const std::string& relativePath);
bool PythonManager::addSysPath(const std::string& relativePath)
ss << "import os\n";
ss << "import sys\n";
ss << "olderr = sys.stderr\n";
ss << "oldout = sys.stdout\n";
ss << "sys.stdout = open('pythonOut.txt','w')\n";
ss << "print 'cwd is %s' % os.getcwd()\n";
ss << "print 'adding relative path to sys.path: %s' % '" << relativePath.c_str() << "'\n";
ss << "sys.path.append(os.path.join(os.getcwd(), '" << relativePath << "'))\n";
ss << "sys.stderr = olderr\n";
ss << "sys.stdout = oldout\n";
int retval = PyRun_SimpleString(ss.str().c_str());
if (retval != 0)
return retval == 0;
Finally, this PythonManager is called from outside my "main loop":
PythonManager* python = new PythonManager();
Check back for follow on summaries on the remaining implementation details (PythonCallback wrapper class, and Boost Python).