Example from and to python converters

From KitwarePublic
Jump to: navigation, search

Example code to illustrate how to use Boost.Python to wrap code, which uses VTK objects in the wrapped functions either as return value or as a function argument.

Useful discussions and descriptions:

* How to write boost converters
* vtkusers: Can I mix Boost-Python and VTK-Python
* Use of VTK in Visit project
* Python C-API
* vtkusers: Mails leading to this page

PythonModule_BoostVTKConversion.cxx:

#include <boost/python.hpp>
 
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
 
 
using namespace boost::python;
 
 
template<class T>
struct vtkObjectPointer_to_python
{
	static PyObject *convert(const T &p)
	{
		if(p == NULL)
		{
			return incref(Py_None);
		}
		std::ostringstream oss;
		oss << (vtkObjectBase*) p; // here don't get address
		std::string address_str = oss.str();
 
		object obj = import("vtk").attr("vtkObjectBase")(address_str);
		return incref(obj.ptr());
	}
};
 
//
// This python to C++ converter uses the fact that VTK Python objects have an 
// attribute called __this__, which is a string containing the memory address 
// of the VTK C++ object and its class name.
// E.g. for a vtkPoints object __this__ might be "_0000000105a64420_p_vtkPoints"
//
void* extract_vtk_wrapped_pointer(PyObject* obj)
{
    char thisStr[] = "__this__";
    //first we need to get the __this__ attribute from the Python Object
    if (!PyObject_HasAttrString(obj, thisStr))
        return NULL;
 
    PyObject* thisAttr = PyObject_GetAttrString(obj, thisStr);
    if (thisAttr == NULL)
        return NULL;
 
    const char* str = PyString_AsString(thisAttr);
    if(str == 0 || strlen(str) < 1)
        return NULL;
 
    char hex_address[32], *pEnd;
    char *_p_ = strstr(str, "_p_vtk");
    if(_p_ == NULL) return NULL;
    char *class_name = strstr(_p_, "vtk");
    if(class_name == NULL) return NULL;
    strcpy(hex_address, str+1);
    hex_address[_p_-str-1] = '\0';
 
    long address = strtol(hex_address, &pEnd, 16);
 
    vtkObjectBase* vtk_object = (vtkObjectBase*)((void*)address);
    if(vtk_object->IsA(class_name))
    {
        return vtk_object;
    }
 
    return NULL;
}
 
 
#define VTK_PYTHON_CONVERSION(type) \
    /* register the to-python converter */ \
    to_python_converter<type*, \
            vtkObjectPointer_to_python<type*> >(); \
    /* register the from-python converter */ \
    converter::registry::insert(&extract_vtk_wrapped_pointer, type_id<type>());
 
 
//
// Example class to illustrate Boost Python wrapped class, which has
// functions which return VTK object pointers
//
struct MyClass
{
	MyClass();
	~MyClass();
	void SetPoints(vtkPoints * pts);
	vtkUnstructuredGrid * GetGrid() { return m_Grid; }
	void DeleteGrid();
	vtkUnstructuredGrid * m_Grid;
};
 
MyClass::MyClass()
{
	m_Grid = vtkUnstructuredGrid::New();
  	m_Grid->Allocate();
 
  	vtkPoints *pts = vtkPoints::New();  	
  	pts->InsertNextPoint(0,0,0);
  	pts->InsertNextPoint(1,0,0);
  	m_Grid->SetPoints(pts);
  	pts->Delete();
}
 
MyClass::~MyClass()
{
	if(m_Grid) this->DeleteGrid();
}
 
void MyClass::DeleteGrid()
{
	m_Grid->Delete();
	m_Grid = NULL;
}
 
void MyClass::SetPoints(vtkPoints *pts)
{
    m_Grid->SetPoints(pts);
}
 
 
//
// Create Python Module, with converters and MyClass wrapped
//
BOOST_PYTHON_MODULE(libBoostVTKConversion)
{
	VTK_PYTHON_CONVERSION(vtkUnstructuredGrid);
	VTK_PYTHON_CONVERSION(vtkPoints);
 
	class_<MyClass>("MyClass")
		.def("GetGrid",&MyClass::GetGrid, return_value_policy<return_by_value>())
		.def("DeleteGrid",&MyClass::DeleteGrid,"Delete the grid")
		.def("SetPoints",&MyClass::SetPoints,"Set new points")
	;
}



CMakeLists.txt:

PROJECT(BoostPythonVTK)
 
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
 
FIND_PACKAGE(VTK REQUIRED)
INCLUDE(${VTK_USE_FILE})
SET(VTK_LIBS  vtkGraphics vtkIO vtkCommon vtkImaging vtkFiltering)
 
FIND_PACKAGE(PythonLibs REQUIRED)
MESSAGE(STATUS "PYTHON_LIBRARIES " ${PYTHON_LIBRARIES})
 
FIND_PACKAGE(Boost 1.45.0)
IF(Boost_FOUND)
  INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
  SET(Boost_USE_STATIC_LIBS OFF)
  SET(Boost_USE_MULTITHREADED ON)
  SET(Boost_USE_STATIC_RUNTIME OFF)
  FIND_PACKAGE(Boost 1.45.0 COMPONENTS python REQUIRED)
 
  ADD_LIBRARY(BoostVTKConversion MODULE PythonModule_BoostVTKConversion.cxx)
  TARGET_LINK_LIBRARIES(BoostVTKConversion ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} ${VTK_LIBS})
 
ELSE()
  MESSAGE(FATAL_ERROR "Unable to find correct Boost version. Did you set BOOST_ROOT?")
ENDIF()