Commit c1cde3ec authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

ENH: Python algorithm and Python plugins

This adds several enhancements to ParaView's Python-based programmable
filter support.

1. Python Algorithm (or `VTKPythonAlgorithmBase` subclasses) are now
   supported. Users can develop algorithms in Python as Python classes.
2. Decorators can be used to decorate Python algorithm classes and their
   methods to declare the proxy/property definitions needed to use such
   Python algorithms in ParaView as sources, readers, writers, filters.
3. Python modules can be loaded directly as plugins. If the module
   has any Python algorithms that are appropriately decorated to be
   exposed in ParaView, they become available in ParaView, similar to
   other plugins.
parent 6e83f599
......@@ -679,6 +679,28 @@ if(PARAVIEW_ENABLE_PYTHON)
set(ImportCinema_DISABLE_CRS TRUE)
set(ImportCinemaSpecA_DISABLE_CS TRUE)
set(ImportCinemaSpecA_DISABLE_CRS TRUE)
# PythonAlgorithm plugin tests.
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/PythonAlgorithmPlugin.xml.in"
"${CMAKE_CURRENT_BINARY_DIR}/PythonAlgorithmPlugin.xml" @ONLY)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/PythonAlgorithmReadersAndWriters.xml.in"
"${CMAKE_CURRENT_BINARY_DIR}/PythonAlgorithmReadersAndWriters.xml" @ONLY)
set(pyalgo_plugin_tests
PythonAlgorithmPlugin
PythonAlgorithmReadersAndWriters)
foreach(tname IN LISTS pyalgo_plugin_tests)
list(APPEND TESTS_WITH_BASELINES
${CMAKE_CURRENT_BINARY_DIR}/${tname}.xml)
# we need to extend testing infrastructure to better support
# loading plugins in client-server. At that point, we can test these as well.
set(${tname}_DISABLE_CS TRUE)
set(${tname}_DISABLE_CRS TRUE)
endforeach()
endif()
endif()
......
<?xml version="1.0" ?>
<pqevents>
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuTools" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionManage_Plugins" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/loadLocal" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/pqFileDialog" command="filesSelected" arguments="@ParaView_SOURCE_DIR@/Examples/Plugins/PythonAlgorithm/PythonAlgorithmExamples.py" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/buttonBox/1QPushButton0" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuSources" />
<pqevent object="pqClientMainWindow/menubar/menuSources" command="activate" arguments="PythonSuperquadricSource" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/PhiResolution/IntRangeWidget/Slider" command="set_int" arguments="26" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/PhiResolution/IntRangeWidget/LineEdit" command="set_string" arguments="20" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/ThetaResolution/LineEdit0" command="set_string" arguments="20" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/representationToolbar/displayRepresentation/comboBox" command="activated" arguments="Surface With Edges" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/ReloadPythonModule/1QPushButton0" command="activate" arguments="" />
</pqevents>
<?xml version="1.0" ?>
<pqevents>
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuTools" />
<pqevent object="pqClientMainWindow/menubar/menuTools" command="activate" arguments="actionManage_Plugins" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/localGroup/loadLocal" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/pqFileDialog" command="filesSelected" arguments="@ParaView_SOURCE_DIR@/Examples/Plugins/PythonAlgorithm/PythonAlgorithmExamples.py" />
<pqevent object="pqClientMainWindow/PluginManagerDialog/buttonBox/1QPushButton0" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuSources" />
<pqevent object="pqClientMainWindow/menubar/menuSources" command="activate" arguments="RTAnalyticSource" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar/menuFilters/pqProxyGroupMenuManager0/Contour" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/ComputeScalars/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/ComputeGradients/CheckBox" command="set_boolean" arguments="true" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/MainControlsToolbar/actionSaveData" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/FileSaveDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/PythonAlgorithmReadersAndWriters.csv" />
<pqevent object="pqClientMainWindow/FileSaveDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/PythonAlgorithmReadersAndWriters.csv" />
<pqevent object="pqClientMainWindow/WriterSettingsDialog/widget/OKButton" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Delete" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Delete" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/MainControlsToolbar/actionOpenData" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/FileOpenDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/PythonAlgorithmReadersAndWriters.csv" />
<pqevent object="pqClientMainWindow/pqSelectReaderDialog/listWidget" command="currentChangedbyItemName" arguments="Python-based CSV Reader" />
<pqevent object="pqClientMainWindow/pqSelectReaderDialog/okButton" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget/1QHeaderView0" command="mousePress" arguments="1,1,0,0,0,0" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget/1QHeaderView0" command="mouseRelease" arguments="1,0,0,0,0,0" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget" command="setCheckState" arguments="9.0,2" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget" command="setCheckState" arguments="8.0,2" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget" command="setCheckState" arguments="7.0,2" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/Arrays/ArraySelectionWidget" command="setCheckState" arguments="6.0,2" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/menubar" command="activate" arguments="menuFilters" />
<pqevent object="pqClientMainWindow/menubar/menuFilters/Alphabetical" command="activate" arguments="TableToPolyData" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/YColumn/ComboBox" command="activated" arguments="Points1" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/scrollArea/qt_scrollarea_viewport/scrollAreaWidgetContents/PropertiesFrame/ProxyPanel/ZColumn/ComboBox" command="activated" arguments="Points2" />
<pqevent object="pqClientMainWindow/centralwidget/MultiViewWidget/CoreWidget/qt_tabwidget_stackedwidget/MultiViewWidget1/Splitter.0/Frame.2/Close" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/propertiesDock/propertiesPanel/Accept" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/variableToolbar/displayColor/Variables" command="activated" arguments="RTData" />
<pqevent object="pqClientMainWindow/representationToolbar/displayRepresentation/comboBox" command="activated" arguments="Point Gaussian" />
<pqevent object="pqClientMainWindow/variableToolbar/actionScalarBarVisibility" command="set_boolean" arguments="false" />
<pqevent object="pqClientMainWindow/pipelineBrowserDock/pipelineBrowser" command="mousePress" arguments="1,1,0,42,11,/0:0/0:0" />
<pqevent object="pqClientMainWindow/pipelineBrowserDock/pipelineBrowser" command="mouseRelease" arguments="1,0,0,42,11,/0:0/0:0" />
<pqevent object="pqClientMainWindow/MainControlsToolbar/actionSaveData" command="activate" arguments="" />
<pqevent object="pqClientMainWindow/FileSaveDialog" command="remove" arguments="$PARAVIEW_TEST_ROOT/PythonAlgorithmReadersAndWriters.npz" />
<pqevent object="pqClientMainWindow/FileSaveDialog" command="filesSelected" arguments="$PARAVIEW_TEST_ROOT/PythonAlgorithmReadersAndWriters.npz" />
</pqevents>
......@@ -286,7 +286,7 @@ function(_write_static_plugins_init_file header source)
set(plugins_init_function "${plugins_init_function}static bool paraview_static_plugins_load(const char* name);\n\n")
set(plugins_init_function "${plugins_init_function}static bool paraview_static_plugins_search(const char* name);\n\n")
set(plugins_init_function "${plugins_init_function}void paraview_static_plugins_init()\n{\n")
set(plugins_init_function "${plugins_init_function} vtkPVPluginLoader::SetStaticPluginLoadFunction(paraview_static_plugins_load);\n")
set(plugins_init_function "${plugins_init_function} vtkPVPluginLoader::RegisterLoadPluginCallback(paraview_static_plugins_load);\n")
set(plugins_init_function "${plugins_init_function} vtkPVPluginTracker::SetStaticPluginSearchFunction(paraview_static_plugins_search);\n")
set(plugins_init_function "${plugins_init_function}}\n\n")
......
......@@ -380,7 +380,10 @@ list(APPEND _vtk_modules vtkDomainsChemistryOpenGL2)
list (APPEND _vtk_modules vtkIOSegY)
list(APPEND _vtk_mpi_modules vtkRenderingParallelLIC)
if(PARAVIEW_ENABLE_PYTHON)
list (APPEND _vtk_modules vtkPVCinemaReader)
list (APPEND _vtk_modules
vtkPVCinemaReader
vtkPVPythonAlgorithm
)
endif()
if (PARAVIEW_ENABLE_XDMF2)
......
# Improved ability to add Python-based algorithms via Plugin
Support for developing filters, readers, writers in Python and distributing them
as plugins has been improved. `paraview.util.vtkAlgorithm` module provides
decorators to expose `VTKPythonAlgorithmBase`-based VTK algorithms in ParaView.
A python module that provides such decorated algorithms can be directly loaded
in ParaView as plugins.
This diff is collapsed.
......@@ -26,16 +26,44 @@ vtkPVPlugin::EULAConfirmationCallback vtkPVPlugin::EULAConfirmationCallbackPtr =
//-----------------------------------------------------------------------------
bool vtkPVPlugin::ImportPlugin(vtkPVPlugin* plugin)
{
// If plugin has an EULA, confirm it before proceeding.
if (plugin && (plugin->GetEULA() == nullptr || vtkPVPlugin::ConfirmEULA(plugin)))
static bool printDebugInfo = (vtksys::SystemTools::GetEnv("PV_PLUGIN_DEBUG") != nullptr);
std::ostringstream msg;
bool status = false;
if (plugin)
{
// Register the plugin with the plugin manager on the current process. That
// will kick in the code to process the plugin e.g. initialize CSInterpreter,
// load XML etc.
vtkPVPluginTracker::GetInstance()->RegisterPlugin(plugin);
return true;
auto indent = vtkIndent().GetNextIndent();
msg << "----------------------------------------------------------" << endl
<< "Importing plugin: **" << plugin->GetPluginName() << "**" << endl
<< indent << "name: " << plugin->GetPluginName() << endl
<< indent << "version: " << plugin->GetPluginVersionString() << endl
<< indent << "filename: " << (plugin->GetFileName() ? plugin->GetFileName() : "(nullptr)")
<< endl
<< indent << "required-on-server: " << plugin->GetRequiredOnServer() << endl
<< indent << "required-on-client: " << plugin->GetRequiredOnClient() << endl
<< indent << "has-eula: " << (plugin->GetEULA() != nullptr) << endl;
// If plugin has an EULA, confirm it before proceeding.
if ((plugin->GetEULA() == nullptr || vtkPVPlugin::ConfirmEULA(plugin)))
{
// Register the plugin with the plugin manager on the current process. That
// will kick in the code to process the plugin e.g. initialize CSInterpreter,
// load XML etc.
vtkPVPluginTracker::GetInstance()->RegisterPlugin(plugin);
status = true;
}
else
{
msg << " Plugin has EULA and was not accepted. Plugin won't be imported." << endl;
}
}
if (printDebugInfo && msg.str().size())
{
msg << "Import status: " << status << endl;
vtkOutputWindowDisplayText(msg.str().c_str());
}
return false;
return status;
}
//-----------------------------------------------------------------------------
......
......@@ -26,12 +26,12 @@
#include "vtkPVServerManagerPluginInterface.h"
#include "vtkPVXMLParser.h"
#include "vtkProcessModule.h"
#include "vtksys/SystemTools.hxx"
#include <cstdlib>
#include <iterator>
#include <sstream>
#include <string>
#include <vtksys/SystemTools.hxx>
#include <cstdlib>
#define vtkPVPluginLoaderDebugMacro(x) \
{ \
......@@ -207,8 +207,8 @@ vtkPVPluginLoaderCleanerInitializer::~vtkPVPluginLoaderCleanerInitializer()
}
}
//=============================================================================
vtkPluginLoadFunction vtkPVPluginLoader::StaticPluginLoadFunction = 0;
std::vector<vtkPVPluginLoader::PluginLoaderCallback>
vtkPVPluginLoader::OrderedPluginLoaderCallbacks;
vtkStandardNewMacro(vtkPVPluginLoader);
//-----------------------------------------------------------------------------
......@@ -362,6 +362,13 @@ bool vtkPVPluginLoader::LoadPluginInternal(const char* file, bool no_errors)
std::string defaultname = vtksys::SystemTools::GetFilenameWithoutExtension(file);
this->SetPluginName(defaultname.c_str());
// first, try the callbacks.
if (vtkPVPluginLoader::CallPluginLoaderCallbacks(file))
{
this->Loaded = true;
return true;
}
if (vtksys::SystemTools::GetFilenameLastExtension(file) == ".xml")
{
vtkPVPluginLoaderDebugMacro("Loading XML plugin" << endl);
......@@ -369,18 +376,15 @@ bool vtkPVPluginLoader::LoadPluginInternal(const char* file, bool no_errors)
if (plugin)
{
vtkPVPluginLoaderCleaner::GetInstance()->Register(plugin);
return this->LoadPlugin(file, plugin);
plugin->SetFileName(file);
return this->LoadPluginInternal(plugin);
}
vtkPVPluginLoaderErrorMacro(
"Failed to load XML plugin. Not a valid XML or file could not be read.");
return false;
}
#ifndef BUILD_SHARED_LIBS
if (StaticPluginLoadFunction && StaticPluginLoadFunction(file))
{
this->Loaded = true;
return true;
}
vtkPVPluginLoaderErrorMacro("Could not find the plugin statically linked in, and "
"cannot load dynamic plugins in static builds.");
return false;
......@@ -511,84 +515,31 @@ bool vtkPVPluginLoader::LoadPluginInternal(const char* file, bool no_errors)
vtkPVPluginLoaderDebugMacro("Updating Shared Library Paths: " << ldLibPath << endl);
}
vtkPVPlugin* plugin = pv_plugin_query_instance();
// if (plugin->UnloadOnExit())
if (vtkPVPlugin* plugin = pv_plugin_query_instance())
{
// So that the lib is closed when the application quits.
// BUGS #10293, #15608.
vtkPVPluginLoaderCleaner::GetInstance()->Register(plugin->GetPluginName(), lib);
plugin->SetFileName(file);
// if (plugin->UnloadOnExit())
{
// So that the lib is closed when the application quits.
// BUGS #10293, #15608.
vtkPVPluginLoaderCleaner::GetInstance()->Register(plugin->GetPluginName(), lib);
}
return this->LoadPluginInternal(plugin);
}
return this->LoadPlugin(file, plugin);
#endif // ifndef BUILD_SHARED_LIBS else
return false;
}
//-----------------------------------------------------------------------------
bool vtkPVPluginLoader::LoadPlugin(const char* file, vtkPVPlugin* plugin)
bool vtkPVPluginLoader::LoadPluginInternal(vtkPVPlugin* plugin)
{
#ifndef BUILD_SHARED_LIBS
if (StaticPluginLoadFunction && StaticPluginLoadFunction(plugin->GetPluginName()))
{
this->Loaded = true;
return true;
}
else
{
this->SetErrorString("Failed to load static plugin.");
}
#endif
this->SetPluginName(plugin->GetPluginName());
this->SetPluginVersion(plugin->GetPluginVersionString());
// Print some debug information about the plugin, if needed.
vtkPVPluginLoaderDebugMacro(
"Plugin instance located successfully. "
"Now loading components from the plugin instance based on the interfaces it "
"implements."
<< endl);
vtkPVPluginLoaderDebugMacro("----------------------------------------------------------------\n"
"Plugin Information: \n"
" Name : "
<< plugin->GetPluginName() << "\n"
" Version : "
<< plugin->GetPluginVersionString() << "\n"
" ReqOnServer : "
<< plugin->GetRequiredOnServer() << "\n"
" ReqOnClient : "
<< plugin->GetRequiredOnClient() << "\n"
" ReqPlugins : "
<< plugin->GetRequiredPlugins() << endl);
vtkPVServerManagerPluginInterface* smplugin =
dynamic_cast<vtkPVServerManagerPluginInterface*>(plugin);
if (smplugin)
{
vtkPVPluginLoaderDebugMacro(" ServerManager Plugin : Yes" << endl);
}
else
{
vtkPVPluginLoaderDebugMacro(" ServerManager Plugin : No" << endl);
}
vtkPVPythonPluginInterface* pyplugin = dynamic_cast<vtkPVPythonPluginInterface*>(plugin);
if (pyplugin)
{
vtkPVPluginLoaderDebugMacro(" Python Plugin : Yes" << endl);
}
else
{
vtkPVPluginLoaderDebugMacro(" Python Plugin : No" << endl);
}
// Set the filename so the vtkPVPluginTracker knows what file this plugin was
// loaded from.
plugin->SetFileName(file);
// From this point onwards the vtkPVPlugin travels the same path as a
// statically imported plugin.
vtkPVPlugin::ImportPlugin(plugin);
this->Loaded = true;
return true;
}
......@@ -611,16 +562,52 @@ void vtkPVPluginLoader::PrintSelf(ostream& os, vtkIndent indent)
}
//-----------------------------------------------------------------------------
#if !defined(VTK_LEGACY_REMOVE)
void vtkPVPluginLoader::SetStaticPluginLoadFunction(vtkPluginLoadFunction function)
{
if (!StaticPluginLoadFunction)
{
StaticPluginLoadFunction = function;
}
VTK_LEGACY_REPLACED_BODY(vtkPVPluginLoader::SetStaticPluginLoadFunction, "ParaView 5.6",
vtkPVPluginLoader::RegisterLoadPluginCallback);
vtkPVPluginLoader::RegisterLoadPluginCallback(function);
}
#endif
//-----------------------------------------------------------------------------
void vtkPVPluginLoader::PluginLibraryUnloaded(const char* pluginname)
{
vtkPVPluginLoaderCleaner::PluginLibraryUnloaded(pluginname);
}
//-----------------------------------------------------------------------------
int vtkPVPluginLoader::RegisterLoadPluginCallback(PluginLoaderCallback callback)
{
auto& callbackVector = vtkPVPluginLoader::OrderedPluginLoaderCallbacks;
size_t index = callbackVector.size();
callbackVector.push_back(callback);
return static_cast<int>(index);
}
//-----------------------------------------------------------------------------
void vtkPVPluginLoader::UnregisterLoadPluginCallback(int index)
{
auto& callbackVector = vtkPVPluginLoader::OrderedPluginLoaderCallbacks;
if (index >= 0 && index < static_cast<int>(callbackVector.size()))
{
auto iter = callbackVector.begin();
std::advance(iter, index);
callbackVector.erase(iter);
}
}
//-----------------------------------------------------------------------------
bool vtkPVPluginLoader::CallPluginLoaderCallbacks(const char* nameOrFile)
{
auto& callbackVector = vtkPVPluginLoader::OrderedPluginLoaderCallbacks;
for (auto iter = callbackVector.rbegin(); iter != callbackVector.rend(); ++iter)
{
if ((*iter)(nameOrFile))
{
return true;
}
}
return false;
}
......@@ -30,12 +30,16 @@
#include "vtkObject.h"
#include "vtkPVClientServerCoreCoreModule.h" //needed for exports
class vtkIntArray;
class vtkPVPlugin;
class vtkStringArray;
#include <functional> // for std::function
#include <vector> // for std::vector
class vtkPVPlugin;
#if !defined(VTK_LEGACY_REMOVE)
/// deprecated in ParaView 5.6. Use `vtkPVPluginLoader::PluginLoaderCallback`
/// instead.
typedef bool (*vtkPluginLoadFunction)(const char*);
#endif
class VTKPVCLIENTSERVERCORECORE_EXPORT vtkPVPluginLoader : public vtkObject
{
......@@ -132,10 +136,20 @@ public:
vtkGetMacro(Loaded, bool);
//@}
//@{
/**
*/
using PluginLoaderCallback = std::function<bool(const char*)>;
static int RegisterLoadPluginCallback(PluginLoaderCallback callback);
static void UnregisterLoadPluginCallback(int id);
//@}
/**
* Sets the function used to load static plugins.
* @deprecated ParaView 5.6. Please use `RegisterLoadPluginCallback` instead
* which supports adding multiple callbacks.
*/
static void SetStaticPluginLoadFunction(vtkPluginLoadFunction function);
VTK_LEGACY(static void SetStaticPluginLoadFunction(vtkPluginLoadFunction function));
/**
* Internal method used in pqParaViewPlugin.cxx.in to tell the
......@@ -154,7 +168,7 @@ protected:
* Called by LoadPluginInternal() to do the final steps in loading of a
* plugin.
*/
bool LoadPlugin(const char* file, vtkPVPlugin* plugin);
bool LoadPluginInternal(vtkPVPlugin* plugin);
vtkSetStringMacro(ErrorString);
vtkSetStringMacro(PluginName);
......@@ -173,8 +187,9 @@ protected:
private:
vtkPVPluginLoader(const vtkPVPluginLoader&) = delete;
void operator=(const vtkPVPluginLoader&) = delete;
static std::vector<PluginLoaderCallback> OrderedPluginLoaderCallbacks;
static vtkPluginLoadFunction StaticPluginLoadFunction;
static bool CallPluginLoaderCallbacks(const char* nameOrFile);
};
// Implementation of Schwartz counter idiom to ensure that the plugin library
......
set(Module_SRCS
vtkSIPythonSourceProxy.cxx
vtkPVPythonAlgorithmPlugin.cxx
)
vtk_module_library(vtkPVPythonAlgorithm ${Module_SRCS})
if (VTK_WRAP_PYTHON)
# the `if()` can be removed once we remove the if() in `vtkFiltersPython`.
# it's not needed.
vtk_module(vtkPVPythonAlgorithm
OPTIONAL_PYTHON_LINK
DEPENDS
vtkPVServerImplementationCore
PRIVATE_DEPENDS
vtkFiltersPython
vtkPythonInterpreter
vtksys
vtkWrappingPythonCore
TEST_LABELS
PARAVIEW
)
endif()
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPVPythonAlgorithmPlugin.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/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 notice for more information.
=========================================================================*/
#include "vtkPython.h" // must be 1st include.
#include "vtkPVPythonAlgorithmPlugin.h"
#include "vtkObjectFactory.h"
#include "vtkPVPluginLoader.h"
#include "vtkPythonInterpreter.h"
#include "vtkPythonUtil.h"
#include "vtkSMMessage.h"
#include "vtkSmartPyObject.h"
#include "vtksys/SystemTools.hxx"
#include <stdexcept>
//============================================================================
static int nifty_counter = 0;
static int callback_id = -1;
vtkPVPythonAlgorithmPluginLoaderInitializer::vtkPVPythonAlgorithmPluginLoaderInitializer()
{
if (nifty_counter == 0)
{
callback_id =
vtkPVPluginLoader::RegisterLoadPluginCallback(vtkPVPythonAlgorithmPlugin::LoadPlugin);
}
nifty_counter++;
}
vtkPVPythonAlgorithmPluginLoaderInitializer::~vtkPVPythonAlgorithmPluginLoaderInitializer()
{
nifty_counter--;
if (nifty_counter == 0 && callback_id != -1)
{
vtkPVPluginLoader::UnregisterLoadPluginCallback(callback_id);
callback_id = -1;
}
}
//============================================================================
class vtkPVPythonAlgorithmPlugin::vtkInternals
{
public:
std::string PluginName;
std::string PluginVersion;
std::vector<std::string> XMLs;
};
//----------------------------------------------------------------------------
vtkPVPythonAlgorithmPlugin::vtkPVPythonAlgorithmPlugin(const char* modulefile)
: Internals(new vtkPVPythonAlgorithmPlugin::vtkInternals())
{
assert(modulefile != nullptr);
// Initialize Python environment, if not already.
vtkPythonInterpreter::Initialize();
vtkInternals& internals = (*this->Internals);
vtkPythonScopeGilEnsurer gilEnsurer;
vtkSmartPyObject pvdetail(PyImport_ImportModule("paraview.detail.pythonalgorithm"));
if (!pvdetail)
{
throw std::runtime_error("Failed to import `paraview.detail.pythonalgorithm`.");
}
vtkSmartPyObject load_plugin(PyObject_GetAttrString(pvdetail, "load_plugin"));
if (!load_plugin)
{
throw std::runtime_error("Failed to locate `paraview.detail.pythonalgorithm.load_plugin`.");
}
vtkSmartPyObject filename(PyString_FromString(modulefile));
vtkSmartPyObject module(
PyObject_CallFunctionObjArgs(load_plugin, filename.GetPointer(), nullptr));
if (!module)
{
throw std::runtime_error("Failed to call `paraview.detail.pythonalgorithm.load_plugin`.");
}
vtkSmartPyObject get_plugin_xmls(PyObject_GetAttrString(pvdetail, "get_plugin_xmls"));
if (!get_plugin_xmls)
{
throw std::runtime_error("Failed to locate `paraview.detail.pythonalgorithm.get_plugin_xmls`.");
}
vtkSmartPyObject result(
PyObject_CallFunctionObjArgs(get_plugin_xmls, module.GetPointer(), nullptr));
if (!result)
{
throw std::runtime_error("Failed to call `paraview.detail.pythonalgorithm.get_plugin_xmls`.");
}
if (PyList_Check(result))
{
auto numitems = PyList_GET_SIZE(result.GetPointer());
for (decltype(numitems) cc = 0; cc < numitems; ++cc)
{
PyObject* borrowed_item = PyList_GET_ITEM(result.GetPointer(), cc);
if (PyString_Check(borrowed_item))
{
internals.XMLs.push_back(PyString_AsString(borrowed_item));
}
}
}
vtkSmartPyObject get_plugin_name(PyObject_GetAttrString(pvdetail, "get_plugin_name"));
if (!get_plugin_name)
{
throw std::runtime_error("Failed to locate `paraview.detail.pythonalgorithm.get_plugin_name`.");
}
result.TakeReference(PyObject_CallFunctionObjArgs(get_plugin_name, module.GetPointer(), nullptr));
if (!result || !PyString_Check(result))
{
throw std::runtime_error("Failed to call `paraview.detail.pythonalgorithm.get_plugin_name`.");
}
internals.PluginName = PyString_AsString(result);
vtkSmartPyObject get_plugin_version(PyObject_GetAttrString(pvdetail, "get_plugin_version"));
if (!get_plugin_version)
{
throw std::runtime_error(
"Failed to locate `paraview.detail.pythonalgorithm.get_plugin_version`.");
}
result.TakeReference(
PyObject_CallFunctionObjArgs(get_plugin_version, module.GetPointer(), nullptr));
if (!result || !PyString_Check(result))
{
throw std::runtime_error(
"Failed to call `paraview.detail.pythonalgorithm.get_plugin_version`.");
}
internals.PluginVersion = PyString_AsString(result);
this->SetFileName(modulefile);
}
//----------------------------------------------------------------------------
vtkPVPythonAlgorithmPlugin::~vtkPVPythonAlgorithmPlugin()
{
}
//----------------------------------------------------------------------------
const char* vtkPVPythonAlgorithmPlugin::GetPluginName()
{
const vtkInternals& internals = (*this->Internals);
return internals.PluginName.empty() ? nullptr : internals.PluginName.c_str();
}
//----------------------------------------------------------------------------
const char* vtkPVPythonAlgorithmPlugin::GetPluginVersionString()
{
const vtkInternals& internals = (*this->Internals);
return internals.PluginVersion.empty() ? nullptr : internals.PluginVersion.c_str();
}
//----------------------------------------------------------------------------
void vtkPVPythonAlgorithmPlugin::GetXMLs(std::vector<std::string>& xmls)
{
const vtkInternals& internals = (*this->Internals);
xmls = internals.XMLs;
}
//----------------------------------------------------------------------------
bool vtkPVPythonAlgorithmPlugin::LoadPlugin(const char* pname)
{
if (vtksys::SystemTools::GetFilenameLastExtension(pname) == ".py")
{
try
{
auto* plugin = new vtkPVPythonAlgorithmPlugin(pname);
return vtkPVPlugin::ImportPlugin(plugin);
}
catch (const std::runtime_error& err)
{
vtkGenericWarningMacro("Failed to load Python plugin:\n" << err.what());
vtkPythonScopeGilEnsurer gilEnsurer;
if (PyErr_Occurred() != nullptr)
{
PyErr_Print();
PyErr_Clear();
}
return false;
}
}
return false;
}
/*=========================================================================