Commit 9a8bb15a authored by jcfr's avatar jcfr
Browse files

ENH: Added QTScripted module factory and associated classes

The QTScripted module factory is composed of:
 - the factory itself
 - concrete implementations of qSlicerLoadableModule, qSlicerAbstractModuleWidget
and vtkSlicerModuleLogic

These concrete implementations are:
- qSlicerScriptedLoadableModuleWidget
- qSlicerScriptedLoadableModule
- vtkSlicerScriptedLoadableModuleLogic

All have a method named "setPythonSource".


After setting the python script, the corresponding python implementation
will be retrieved.

git-svn-id: http://svn.slicer.org/Slicer4/trunk@15420 3bd1e089-480b-0410-8dfb-8563597acbee
parent 14e0933e
......@@ -51,6 +51,7 @@
#ifdef Slicer_USE_PYTHONQT
# include "qSlicerPythonManager.h"
# include "qSlicerScriptedLoadableModuleFactory.h"
# include <dPython.h>
// PythonQt wrapper initialization methods
......@@ -109,6 +110,9 @@ int main(int argc, char* argv[])
{
moduleFactoryManager->registerFactory("qSlicerLoadableModuleFactory",
new qSlicerLoadableModuleFactory());
moduleFactoryManager->registerFactory("qSlicerScriptedLoadableModuleFactory",
new qSlicerScriptedLoadableModuleFactory());
}
if (!app.commandOptions()->disableCLIModule())
......
......@@ -51,6 +51,11 @@ set(include_dirs
${FreeSurfer_SOURCE_DIR}
${FreeSurfer_BINARY_DIR}
)
IF(Slicer_USE_PYTHONQT)
LIST(APPEND include_dirs
${PYTHON_INCLUDE_DIR}
)
ENDIF()
include_directories(${include_dirs})
......@@ -98,6 +103,12 @@ set(SlicerBaseLogic_SRCS
vtkSystemInformation.cxx
)
IF(Slicer_USE_PYTHONQT)
LIST(APPEND SlicerBaseLogic_SRCS
vtkSlicerScriptedLoadableModuleLogic.cxx
)
ENDIF()
SET_SOURCE_FILES_PROPERTIES(
vtkPichonFastMarchingPDF.cxx
WRAP_EXCLUDE
......@@ -150,6 +161,14 @@ SET(libs
FreeSurfer
${VTK_LIBRARIES}
)
IF(Slicer_USE_PYTHONQT)
LIST(APPEND libs
vtkCommonPythonD # For vtkPythonUtils
${PYTHON_LIBRARY} # At some point, VTK should be updated to expose VTK_PYTHON_LIBRARY
)
ENDIF()
TARGET_LINK_LIBRARIES(${lib_name} ${libs})
IF(Slicer_USE_KWWIDGETS)
......
/*==============================================================================
Program: 3D Slicer
Copyright (c) 2010 Kitware Inc.
See Doc/copyright/copyright.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
and was partially funded by NIH grant 3P41RR013218-12S1
==============================================================================*/
// Slicer includes
#include "vtkSlicerScriptedLoadableModuleLogic.h"
// VTKSYS includes
#include <vtksys/SystemTools.hxx>
// VTK includes
#include <vtkPythonUtil.h>
#include <vtkSmartPointer.h>
// Python includes
#include <Python.h>
//----------------------------------------------------------------------------
vtkCxxRevisionMacro(vtkSlicerScriptedLoadableModuleLogic, "$Revision$");
vtkStandardNewMacro(vtkSlicerScriptedLoadableModuleLogic);
//---------------------------------------------------------------------------
class vtkSlicerScriptedLoadableModuleLogic::vtkInternal
{
public:
vtkInternal();
~vtkInternal();
// enum {
// GetMRMLSceneEventsToObserveMethod = 0,
// ProcessMRMLEventsMethod,
// };
// static int APIMethodCount;
// static const char * APIMethodNames[2];
std::string PythonSource;
PyObject * PythonSelf;
// PyObject * PythonAPIMethods[2];
};
//----------------------------------------------------------------------------
// vtkInternal methods
////---------------------------------------------------------------------------
//int vtkSlicerScriptedLoadableModuleLogic::vtkInternal::APIMethodCount = 2;
////---------------------------------------------------------------------------
//const char* vtkSlicerScriptedLoadableModuleLogic::vtkInternal::APIMethodNames[2] =
//{
// "GetMRMLSceneEventsToObserve",
// "ProcessMRMLEvents",
//};
//---------------------------------------------------------------------------
vtkSlicerScriptedLoadableModuleLogic::vtkInternal::vtkInternal()
{
this->PythonSelf = 0;
// for (int i = 0; i < vtkInternal::APIMethodCount; ++i)
// {
// this->PythonAPIMethods[i] = 0;
// }
}
//---------------------------------------------------------------------------
vtkSlicerScriptedLoadableModuleLogic::vtkInternal::~vtkInternal()
{
if (this->PythonSelf)
{
Py_DECREF(this->PythonSelf);
}
}
//----------------------------------------------------------------------------
// vtkSlicerScriptedLoadableModuleLogic methods
//----------------------------------------------------------------------------
vtkSlicerScriptedLoadableModuleLogic::vtkSlicerScriptedLoadableModuleLogic()
{
this->Internal = new vtkInternal;
}
//----------------------------------------------------------------------------
vtkSlicerScriptedLoadableModuleLogic::~vtkSlicerScriptedLoadableModuleLogic()
{
delete this->Internal;
}
//----------------------------------------------------------------------------
void vtkSlicerScriptedLoadableModuleLogic::PrintSelf(ostream& os, vtkIndent indent)
{
this->vtkObject::PrintSelf(os, indent);
}
////---------------------------------------------------------------------------
//void vtkSlicerScriptedLoadableModuleLogic::SetMRMLSceneInternal(vtkMRMLScene* newScene)
//{
// vtkIntArray * sceneEventsAsPointer = 0;
// // Obtain list of event to listen
// PyObject* method =
// this->Internal->PythonAPIMethods[vtkInternal::GetMRMLSceneEventsToObserveMethod];
// if (method)
// {
// sceneEventsAsPointer = vtkIntArray::SafeDownCast(
// vtkPythonUtil::GetPointerFromObject(PyObject_CallObject(method, 0), "vtkIntArray"));
// }
// vtkSmartPointer<vtkIntArray> sceneEvents;
// sceneEvents.TakeReference(sceneEventsAsPointer);
// //for(int i = 0; i < sceneEvents->GetNumberOfTuples(); i++)
// // {
// // std::cout << "eventid:" << sceneEvents->GetValue(i) << std::endl;
// // }
// this->SetAndObserveMRMLSceneEventsInternal(newScene, sceneEvents);
//}
////---------------------------------------------------------------------------
//void vtkSlicerScriptedLoadableModuleLogic::ProcessMRMLEvents(vtkObject *caller,
// unsigned long event,
// void *callData)
//{
// PyObject* method = this->Internal->PythonAPIMethods[vtkInternal::ProcessMRMLEventsMethod];
// if (!method)
// {
// return;
// }
// PyObject * arguments = PyTuple_New(3);
// PyTuple_SET_ITEM(arguments, 0, vtkPythonUtil::GetObjectFromPointer(caller));
// PyTuple_SET_ITEM(arguments, 1, PyInt_FromLong(event));
// PyTuple_SET_ITEM(arguments, 2,
// vtkPythonUtil::GetObjectFromPointer(reinterpret_cast<vtkMRMLNode*>(callData)));
// PyObject_CallObject(method, arguments);
// Py_DECREF(arguments);
//}
////---------------------------------------------------------------------------
//void vtkSlicerScriptedLoadableModuleLogic::ProcessLogicEvents(vtkObject *caller,
// unsigned long event,
// void *callData)
//{
// PyObject* method = this->Internal->PythonAPIMethods[vtkInternal::ProcessLogicEventsMethod];
// if (!method)
// {
// return;
// }
// PyObject * arguments = PyTuple_New(3);
// PyTuple_SET_ITEM(arguments, 0, vtkPythonUtil::GetObjectFromPointer(caller));
// PyTuple_SET_ITEM(arguments, 1, PyInt_FromLong(event));
// PyTuple_SET_ITEM(arguments, 2,
// vtkPythonUtil::GetObjectFromPointer(reinterpret_cast<vtkMRMLNode*>(callData)));
// PyObject_CallObject(method, arguments);
// Py_DECREF(arguments);
//}
//---------------------------------------------------------------------------
bool vtkSlicerScriptedLoadableModuleLogic::SetPythonSource(const std::string& pythonSource)
{
assert(pythonSource.find(".py") != std::string::npos);
// Open the file
FILE* pyfile = fopen(pythonSource.c_str(), "r");
if (!pyfile)
{
vtkErrorMacro(<< "SetPythonSource - File " << pythonSource << " doesn't exist !");
return false;
}
// Extract filename - It should match the associated python class
std::string className = vtksys::SystemTools::GetFilenameWithoutExtension(pythonSource);
className+= "Logic";
//std::cout << "SetPythonSource - className:" << className << std::endl;
// Get a reference to the main module and global dictionary
PyObject * main_module = PyImport_AddModule("__main__");
PyObject * global_dict = PyModule_GetDict(main_module);
// Load class definition if needed
PyObject * classToInstantiate = PyDict_GetItemString(global_dict, className.c_str());
if (!classToInstantiate)
{
PyRun_File(pyfile, pythonSource.c_str(), Py_file_input, global_dict, global_dict);
classToInstantiate = PyDict_GetItemString(global_dict, className.c_str());
}
if (!classToInstantiate)
{
vtkErrorMacro(<< "SetPythonSource - Failed to load displayable manager class definition from "
<< pythonSource);
return false;
}
//std::cout << "classToInstantiate:" << classToInstantiate << std::endl;
PyObject * arguments = PyTuple_New(1);
PyTuple_SET_ITEM(arguments, 0, vtkPythonUtil::GetObjectFromPointer(this));
// Attempt to instantiate the associated python class
PyObject * self = PyInstance_New(classToInstantiate, arguments, 0);
Py_DECREF(arguments);
if (!self)
{
vtkErrorMacro(<< "SetPythonSource - Failed to instanciate displayable manager:"
<< classToInstantiate);
return false;
}
// // Retrieve API methods
// for (int i = 0; i < vtkInternal::APIMethodCount; ++i)
// {
// assert(vtkInternal::APIMethodNames[i]);
// PyObject * method = PyObject_GetAttrString(self, vtkInternal::APIMethodNames[i]);
// //std::cout << "method:" << method << std::endl;
// this->Internal->PythonAPIMethods[i] = method;
// }
//std::cout << "self (" << className << ", instance:" << self << ")" << std::endl;
this->Internal->PythonSource = pythonSource;
this->Internal->PythonSelf = self;
return true;
}
/*==============================================================================
Program: 3D Slicer
Copyright (c) 2010 Kitware Inc.
See Doc/copyright/copyright.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
and was partially funded by NIH grant 3P41RR013218-12S1
==============================================================================*/
#ifndef __vtkSlicerScriptedLoadableModuleLogic_h
#define __vtkSlicerScriptedLoadableModuleLogic_h
// Slicer includes
#include "vtkSlicerModuleLogic.h"
// VTK includes
#include "vtkObject.h"
#include "vtkObjectFactory.h"
class VTK_SLICER_BASE_LOGIC_EXPORT vtkSlicerScriptedLoadableModuleLogic :
public vtkSlicerModuleLogic
{
public:
static vtkSlicerScriptedLoadableModuleLogic *New();
vtkTypeRevisionMacro(vtkSlicerScriptedLoadableModuleLogic, vtkSlicerModuleLogic);
void PrintSelf(ostream& os, vtkIndent indent);
bool SetPythonSource(const std::string& pythonSource);
protected:
vtkSlicerScriptedLoadableModuleLogic();
virtual ~vtkSlicerScriptedLoadableModuleLogic();
// virtual void SetMRMLSceneInternal(vtkMRMLScene* newScene);
private:
vtkSlicerScriptedLoadableModuleLogic(const vtkSlicerScriptedLoadableModuleLogic&); // Not implemented
void operator=(const vtkSlicerScriptedLoadableModuleLogic&); // Not implemented
//BTX
class vtkInternal;
vtkInternal * Internal;
//ETX
};
#endif
......@@ -63,6 +63,17 @@ SET(KIT_SRCS
qSlicerWidget.h
)
IF(Slicer_USE_PYTHONQT)
LIST(APPEND KIT_SRCS
qSlicerScriptedLoadableModuleFactory.cxx
qSlicerScriptedLoadableModuleFactory.h
qSlicerScriptedLoadableModuleWidget.cxx
qSlicerScriptedLoadableModuleWidget.h
qSlicerScriptedLoadableModule.cxx
qSlicerScriptedLoadableModule.h
)
ENDIF()
# Since the wrapper doesn't consider hierarchy of classes, let's exclude
# classes deriving from pure abstract class and remaining abstract pure
SET_SOURCE_FILES_PROPERTIES(
......@@ -103,7 +114,11 @@ SET(KIT_MOC_SRCS
# PythonQt decorator
IF(Slicer_USE_PYTHONQT)
LIST(APPEND KIT_MOC_SRCS qSlicerBaseQTGUIPythonQtDecorators.h)
LIST(APPEND KIT_MOC_SRCS
qSlicerBaseQTGUIPythonQtDecorators.h
qSlicerScriptedLoadableModule.h
qSlicerScriptedLoadableModuleWidget.h
)
ENDIF(Slicer_USE_PYTHONQT)
# UI files
......
/*==============================================================================
Program: 3D Slicer
Copyright (c) 2010 Kitware Inc.
See Doc/copyright/copyright.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
and was partially funded by NIH grant 3P41RR013218-12S1
==============================================================================*/
// Qt includes
#include <QDebug>
#include <QFileInfo>
// CTK includes
#include <ctkPimpl.h>
// PythonQt includes
#include <PythonQt.h>
// Slicer includes
#include "qSlicerScriptedLoadableModule.h"
#include "qSlicerScriptedLoadableModuleWidget.h"
#include "vtkSlicerScriptedLoadableModuleLogic.h"
// VTK includes
#include <vtkSmartPointer.h>
// Python includes
#include <Python.h>
//-----------------------------------------------------------------------------
class qSlicerScriptedLoadableModulePrivate
{
public:
qSlicerScriptedLoadableModulePrivate();
virtual ~qSlicerScriptedLoadableModulePrivate();
QString Title;
QString Category;
QString Contributor;
QString HelpText;
QString AcknowledgementText;
PyObject * PythonSelf;
QString PythonSource;
};
//-----------------------------------------------------------------------------
// qSlicerScriptedLoadableModulePrivate methods
//-----------------------------------------------------------------------------
qSlicerScriptedLoadableModulePrivate::qSlicerScriptedLoadableModulePrivate()
{
this->PythonSelf = 0;
}
//-----------------------------------------------------------------------------
qSlicerScriptedLoadableModulePrivate::~qSlicerScriptedLoadableModulePrivate()
{
if (this->PythonSelf)
{
Py_DECREF(this->PythonSelf);
}
}
//-----------------------------------------------------------------------------
// qSlicerScriptedLoadableModule methods
//-----------------------------------------------------------------------------
qSlicerScriptedLoadableModule::qSlicerScriptedLoadableModule(QObject* _parentObject)
: Superclass(_parentObject)
, d_ptr(new qSlicerScriptedLoadableModulePrivate)
{
}
//-----------------------------------------------------------------------------
qSlicerScriptedLoadableModule::~qSlicerScriptedLoadableModule()
{
}
//-----------------------------------------------------------------------------
bool qSlicerScriptedLoadableModule::setPythonSource(const QString& newPythonSource)
{
Q_D(qSlicerScriptedLoadableModule);
Q_ASSERT(newPythonSource.endsWith(".py"));
// Open the file
FILE* pyfile = fopen(newPythonSource.toLatin1(), "r");
if (!pyfile)
{
qCritical() << "SetPythonSource - File" << newPythonSource << "doesn't exist !";
return false;
}
// Extract moduleName from the provided filename
QString moduleName = QFileInfo(newPythonSource).baseName();
QString className = moduleName;
//qDebug() << "className" << className;
// Get a reference to the main module and global dictionary
PyObject * main_module = PyImport_AddModule("__main__");
PyObject * global_dict = PyModule_GetDict(main_module);
// Load class definition if needed
PyObject * classToInstantiate = PyDict_GetItemString(global_dict, className.toLatin1());
if (!classToInstantiate)
{
PyRun_File(pyfile, newPythonSource.toLatin1(), Py_file_input, global_dict, global_dict);
classToInstantiate = PyDict_GetItemString(global_dict, className.toLatin1());
}
if (!classToInstantiate)
{
qCritical()
<< "Failed to load scripted pythonqt module class definition from" << newPythonSource;
return false;
}
//qDebug() << "classToInstantiate:" << classToInstantiate;
PyObject * wrappedThis = PythonQt::self()->priv()->wrapQObject(this);
if (!wrappedThis)
{
qCritical() << "setPythonSource" << newPythonSource
<< "- Failed to wrap" << this->metaObject()->className();
return false;
}
PyObject * arguments = PyTuple_New(1);
PyTuple_SET_ITEM(arguments, 0, wrappedThis);
// Attempt to instantiate the associated python class
PyObject * self = PyInstance_New(classToInstantiate, arguments, 0);
Py_DECREF(arguments);
if (!self)
{
qCritical() << "setPythonSource" << newPythonSource
<< "- Failed to instanciate scripted pythonqt class" << className << classToInstantiate;
return false;
}
d->PythonSource = newPythonSource;
d->PythonSelf = self;
return true;
}
//-----------------------------------------------------------------------------
//PyObject* qSlicerScriptedLoadableModule::pythonInstance() const
//{
// Q_D(const qSlicerScriptedLoadableModule);
// return d->PythonSelf;
//}
//-----------------------------------------------------------------------------
void qSlicerScriptedLoadableModule::setup()
{
#ifndef QT_NO_DEBUG
Q_D(qSlicerScriptedLoadableModule);
#endif
Q_ASSERT(d != 0);
}
//-----------------------------------------------------------------------------
qSlicerAbstractModuleRepresentation* qSlicerScriptedLoadableModule::createWidgetRepresentation()
{
Q_D(qSlicerScriptedLoadableModule);
QScopedPointer<qSlicerScriptedLoadableModuleWidget> widget(new qSlicerScriptedLoadableModuleWidget);
bool ret = widget->setPythonSource(d->PythonSource);
if (!ret)
{
return 0;
}
return widget.take();
}
//-----------------------------------------------------------------------------
vtkSlicerLogic* qSlicerScriptedLoadableModule::createLogic()
{
Q_D(qSlicerScriptedLoadableModule);
vtkSlicerScriptedLoadableModuleLogic* logic = vtkSlicerScriptedLoadableModuleLogic::New();
// bool ret = logic->SetPythonSource(d->PythonSource.toStdString());
// if (!ret)
// {
// logic->Delete();
// return 0;
// }
return logic;
}
//-----------------------------------------------------------------------------
CTK_SET_CPP(qSlicerScriptedLoadableModule, const QString&, setTitle, Title)
CTK_GET_CPP(qSlicerScriptedLoadableModule, QString, title, Title)
//-----------------------------------------------------------------------------
CTK_SET_CPP(qSlicerScriptedLoadableModule, const QString&, setCategory, Category)
CTK_GET_CPP(qSlicerScriptedLoadableModule, QString, category, Category)
//-----------------------------------------------------------------------------
CTK_SET_CPP(qSlicerScriptedLoadableModule, const QString&, setContributor, Contributor)
CTK_GET_CPP(qSlicerScriptedLoadableModule, QString, contributor,