From 3d50d4828447e886042cc4988bb190f9fd5ce544 Mon Sep 17 00:00:00 2001
From: Julien Malik <julien.malik@c-s.fr>
Date: Mon, 10 Oct 2011 05:51:23 +0200
Subject: [PATCH] ENH: add PyCommand to receive ITK event in Python callable

---
 Code/Wrappers/SWIG/CMakeLists.txt   |   3 +-
 Code/Wrappers/SWIG/PyCommand.i      |  43 +++++++++++
 Code/Wrappers/SWIG/itkPyCommand.cxx | 112 ++++++++++++++++++++++++++++
 Code/Wrappers/SWIG/itkPyCommand.h   |  81 ++++++++++++++++++++
 Code/Wrappers/SWIG/otbApplication.i |   3 +
 5 files changed, 241 insertions(+), 1 deletion(-)
 create mode 100644 Code/Wrappers/SWIG/PyCommand.i
 create mode 100644 Code/Wrappers/SWIG/itkPyCommand.cxx
 create mode 100644 Code/Wrappers/SWIG/itkPyCommand.h

diff --git a/Code/Wrappers/SWIG/CMakeLists.txt b/Code/Wrappers/SWIG/CMakeLists.txt
index ed3a08ee34..3cf717b707 100644
--- a/Code/Wrappers/SWIG/CMakeLists.txt
+++ b/Code/Wrappers/SWIG/CMakeLists.txt
@@ -34,6 +34,7 @@ if ( WRAP_LUA OR WRAP_PYTHON OR WRAP_JAVA OR WRAP_CSHARP OR WRAP_TCL OR WRAP_R O
   set ( SWIG_HEADERS ${SWIG_HEADERS}
     ${CMAKE_CURRENT_SOURCE_DIR}/itkBase.i
     ${CMAKE_CURRENT_SOURCE_DIR}/itkMacro.i
+    ${CMAKE_CURRENT_SOURCE_DIR}/PyCommand.i
 
     ${CMAKE_CURRENT_SOURCE_DIR}/Python.i
     ${CMAKE_CURRENT_SOURCE_DIR}/Java.i
@@ -63,7 +64,7 @@ if ( WRAP_PYTHON )
   set(CMAKE_SWIG_FLAGS ${CMAKE_SWIG_GLOBAL_FLAGS})
   set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR})
   set(SWIG_MODULE_otbApplication_EXTRA_DEPS OTBApplicationEngine ${SWIG_HEADERS})
-  SWIG_add_module ( otbApplication python otbApplication.i otbApplicationPYTHON_wrap.cxx )
+  SWIG_add_module ( otbApplication python otbApplication.i otbApplicationPYTHON_wrap.cxx itkPyCommand.cxx )
   SWIG_link_libraries ( otbApplication ${PYTHON_LIBRARIES} OTBApplicationEngine)
   set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/otbApplicationPYTHON_wrap.cxx COMPILE_FLAGS "-w")
 
diff --git a/Code/Wrappers/SWIG/PyCommand.i b/Code/Wrappers/SWIG/PyCommand.i
new file mode 100644
index 0000000000..1cc2abfa82
--- /dev/null
+++ b/Code/Wrappers/SWIG/PyCommand.i
@@ -0,0 +1,43 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#if SWIGPYTHON
+
+ %{
+#include "itkPyCommand.h"
+typedef itk::PyCommand          itkPyCommand;
+typedef itk::PyCommand::Pointer itkPyCommand_Pointer;
+ %}
+
+
+class itkPyCommand : public itkCommand
+{
+public:
+  static itkPyCommand_Pointer New();
+  virtual char const * GetNameOfClass() const;
+
+  void SetCommandCallable(PyObject *obj);
+  PyObject * GetCommandCallable();
+  void Execute(itkObject *, const itkEventObject&);
+  //void Execute(const itkObject *, const EventObject&);
+protected:
+  itkPyCommand();
+  ~itkPyCommand();
+};
+DECLARE_REF_COUNT_CLASS( itkPyCommand )
+
+#endif
diff --git a/Code/Wrappers/SWIG/itkPyCommand.cxx b/Code/Wrappers/SWIG/itkPyCommand.cxx
new file mode 100644
index 0000000000..da0f40a6f5
--- /dev/null
+++ b/Code/Wrappers/SWIG/itkPyCommand.cxx
@@ -0,0 +1,112 @@
+/*=========================================================================
+
+  Program:   Insight Segmentation & Registration Toolkit
+  Module:    $RCSfile: itkPyCommand.cxx,v $
+  Language:  C++
+  Date:      $Date: 2006/09/06 20:58:42 $
+  Version:   $Revision: 1.1 $
+
+  Copyright (c) Insight Software Consortium. All rights reserved.
+  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "itkPyCommand.h"
+
+namespace itk
+{
+
+PyCommand::PyCommand()
+{
+    this->obj = NULL;
+}
+
+PyCommand::~PyCommand()
+{
+    if (this->obj)
+    {
+        Py_DECREF(this->obj);
+    }
+    this->obj = NULL;
+}
+    
+void PyCommand::SetCommandCallable(PyObject *obj)
+{
+    if (obj != this->obj)
+    {
+        if (this->obj)
+        {
+            // get rid of our reference
+            Py_DECREF(this->obj);
+        }
+
+        // store the new object
+        this->obj = obj;
+
+        if (this->obj)
+        {
+            // take out reference (so that the calling code doesn't
+            // have to keep a binding to the callable around)
+            Py_INCREF(this->obj);
+        }
+    }
+}
+
+PyObject * PyCommand::GetCommandCallable()
+{
+    return this->obj;
+}
+
+void PyCommand::Execute(Object *, const EventObject&)
+{
+    this->PyExecute();
+}
+
+
+void PyCommand::Execute(const Object*, const EventObject&)
+{
+    this->PyExecute();
+
+}
+
+void PyCommand::PyExecute()
+{
+    // make sure that the CommandCallable is in fact callable
+    if (!PyCallable_Check(this->obj))
+    {
+        // we throw a standard ITK exception: this makes it possible for
+        // our standard CableSwig exception handling logic to take this
+        // through to the invoking Python process
+        itkExceptionMacro(<<"CommandCallable is not a callable Python object, "
+                          <<"or it has not been set.");
+    }
+    else
+    {
+        PyObject *result;
+
+        result = PyEval_CallObject(this->obj, (PyObject *)NULL);
+
+        if (result)
+        {
+            Py_DECREF(result);
+        }
+        else
+        {
+            // there was a Python error.  Clear the error by printing to stdout
+            PyErr_Print();
+            // make sure the invoking Python code knows there was a problem
+            // by raising an exception
+            itkExceptionMacro(<<"There was an error executing the "
+                              <<"CommandCallable.");
+        }
+    }
+}
+
+
+
+} // namespace itk
+
+
diff --git a/Code/Wrappers/SWIG/itkPyCommand.h b/Code/Wrappers/SWIG/itkPyCommand.h
new file mode 100644
index 0000000000..71611b4d03
--- /dev/null
+++ b/Code/Wrappers/SWIG/itkPyCommand.h
@@ -0,0 +1,81 @@
+/*=========================================================================
+
+  Program:   Insight Segmentation & Registration Toolkit
+  Module:    $RCSfile: itkPyCommand.h,v $
+  Language:  C++
+  Date:      $Date: 2006/09/06 20:58:42 $
+  Version:   $Revision: 1.1 $
+
+  Copyright (c) Insight Software Consortium. All rights reserved.
+  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef _itkPyCommand_h
+#define _itkPyCommand_h
+
+#include "itkCommand.h"
+
+// The python header defines _POSIX_C_SOURCE without a preceding #undef
+#undef _POSIX_C_SOURCE
+#include <Python.h>
+
+namespace itk
+{
+
+/** \class PyCommand
+ *  \brief Command subclass that calls a Python callable object, e.g.
+ *  a Python function.
+ * 
+ * With this class, arbitrary Python callable objects (e.g. functions)
+ * can be associated with an instance to be used in AddObserver calls.
+ * This is analogous to itk::TclCommand, but then a tad more flexible. ;)
+ *
+ * This class was contributed by Charl P. Botha <cpbotha |AT| ieee.org>
+ */
+class PyCommand : public Command
+{
+public:
+  ///! Standard "Self" typedef.
+  typedef PyCommand         Self;
+
+  ///! Smart pointer typedef support.
+  typedef SmartPointer<Self>  Pointer;
+
+  ///! Run-time type information (and related methods).
+  itkTypeMacro(PyCommand,Command);
+
+  ///! Method for creation through the object factory.
+  itkNewMacro(Self);
+
+  /** 
+   * Assign a Python callable object to be used.  You don't have to keep
+   * a binding to the callable, PyCommand will also take out a reference
+   * to make sure the Callable sticks around.
+   */
+  void SetCommandCallable(PyObject *obj);
+
+  PyObject * GetCommandCallable();
+
+  void Execute(Object *, const EventObject&);
+  void Execute(const Object *, const EventObject&);
+
+protected:
+  PyCommand();
+  ~PyCommand();
+  void PyExecute();
+  PyCommand(const Self&);     // Not implemented.
+  void operator=(const Self&); // Not implemented.
+
+private:
+  PyObject *obj;
+};
+
+
+} // namespace itk
+
+#endif // _itkPyCommand_h
+
diff --git a/Code/Wrappers/SWIG/otbApplication.i b/Code/Wrappers/SWIG/otbApplication.i
index 8d033c5414..b1aa4175c9 100644
--- a/Code/Wrappers/SWIG/otbApplication.i
+++ b/Code/Wrappers/SWIG/otbApplication.i
@@ -161,3 +161,6 @@ private:
   void operator=(const Self&);
 };
 
+%include "PyCommand.i"
+
+
-- 
GitLab