Skip to content

Commit

Permalink
FILTER: Generate Python Plugin/Filter Skeleton Code (#821)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
Signed-off-by: Joey Kleingers <joey.kleingers@bluequartz.net>
Co-authored-by: Michael Jackson <mike.jackson@bluequartz.net>
  • Loading branch information
joeykleingers and imikejackson authored Jan 17, 2024
1 parent 3b8230c commit f869943
Show file tree
Hide file tree
Showing 14 changed files with 907 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmake/Plugin.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ function(create_simplnx_plugin)
PUBLIC
$<BUILD_INTERFACE:${${ARGS_NAME}_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${ARGS_GENERATED_DIR}>
$<BUILD_INTERFACE:${${ARGS_NAME}_BINARY_DIR}/../>
)

if(SIMPLNX_ENABLE_INSTALL)
Expand Down
24 changes: 19 additions & 5 deletions src/Plugins/SimplnxCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ set(FilterList
FindVolFractionsFilter
GenerateColorTableFilter
GenerateVectorColorsFilter
GeneratePythonSkeletonFilter
IdentifySample
ImageContouringFilter
InitializeData
Expand Down Expand Up @@ -168,6 +169,7 @@ set(AlgorithmList
FindVertexToTriangleDistances
GenerateColorTable
GenerateVectorColors
GeneratePythonSkeleton
ImageContouring
KMeans
KMedoids
Expand Down Expand Up @@ -244,10 +246,10 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE reproc++)
# can use the target_sources(...) cmake call

set(PLUGIN_EXTRA_SOURCES
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/CSVDataParser.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/nanoflann.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/StlUtilities.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/StlUtilities.cpp
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/CSVDataParser.hpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/nanoflann.hpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/StlUtilities.hpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/StlUtilities.cpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/CalculatorItem.hpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/CalculatorItem.cpp"
"${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/ICalculatorArray.hpp"
Expand Down Expand Up @@ -335,11 +337,23 @@ source_group(TREE "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}" PREFIX ${PLU
${PLUGIN_EXTRA_SOURCES}
)

#------------------------------------------------------------------------------
# Configure the Python Plugin Generation Header file
# This will read the template files into the CMake variables, then configure
# the template file to use the contents of those files. This should allow easier
# updating of those template files.
file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonFilterTemplate.py" PYTHON_FILTER_TEMPLATE)
file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginInitTemplate.py" PYTHON_PLUGIN_INIT_TEMPLATE)
file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.py" PYTHON_PLUGIN_TEMPLATE)

configure_file("${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplateFile.in.hpp"
"${${PLUGIN_NAME}_BINARY_DIR}/utils/PythonPluginTemplateFile.hpp")

#------------------------------------------------------------------------------
# If there are additional include directories that are needed for this plugin
# you can use the target_include_directories(.....) cmake call
# target_include_directories(${PLUGIN_NAME}
# PUBLIC
# PRIVATE
# additional include directories here
# )

Expand Down
51 changes: 51 additions & 0 deletions src/Plugins/SimplnxCore/docs/GeneratePythonSkeletonFilter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generate Python Plugin or Python Filter

## Group

Python

## Description

This **Filter** generates the Python skeleton code for a new DREAM3D-NX/SIMPL-NX filters. These new Python filters can either be written to a newly generated Python plugin, or to an existing Python plugin.

### Generating A New Plugin

If the **Use Existing Plugin** parameter is OFF, then this filter will generate both a new Python skeleton plugin and new Python skeleton filters.

The following parameters are available when **Use Existing Plugin** is OFF:

+ Name of Plugin - The name of the plugin, referred to in the generated code.
+ Human Name of Plugin - The human-readable version of the plugin name.
+ Plugin Output Directory - The location on the file system where the plugin will be generated.
+ Filter Names - The names of filters that will be generated in the new plugin, separated by commas.

### Using An Existing Plugin

If the **Use Existing Plugin** parameter is ON, then this filter will generate new Python skeleton filters in an existing plugin. If the existing plugin was created manually and NOT created using this filter, some additional edits will need to be done in a few of the other plugin files.

The following parameters are available when **Use Existing Plugin** is ON:

+ Existing Plugin Location - The file system path where the existing plugin is located.
+ Filter Names - The names of filters that will be generated in the existing plugin, separated by commas.

**NOTE:** This option searches the existing plugin's **\_\_init\_\_.py** and **Plugin.py** files for the following comment tokens:

1. \# FILTER_INCLUDE_INSERT
2. \# FILTER_NAME_INSERT

Both of these tokens are included in both files for any plugin that was originally generated using this filter.

#### What if my plugin was created manually (not generated using this filter)?

+ If the **\# FILTER_INCLUDE_INSERT** token cannot be found, then the Python import statements for the new filters will need to be manually added to both files.
+ If the **\# FILTER_NAME_INSERT** token cannot be found, then each filter name will need to be manually added to the **all** and **get_filters** methods in the **\_\_init\_\_.py** and **Plugin.py** files, respectively.

## Example Pipelines

## License & Copyright

Please see the description file distributed with this **Plugin**

## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues) GItHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "GeneratePythonSkeleton.hpp"

#include "simplnx/Common/Constants.hpp"
#include "simplnx/Common/RgbColor.hpp"
#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/DataStructure/DataGroup.hpp"
#include "simplnx/Utilities/DataArrayUtilities.hpp"

#include <Eigen/Dense>

using namespace nx::core;

// -----------------------------------------------------------------------------
GeneratePythonSkeleton::GeneratePythonSkeleton(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel,
GeneratePythonSkeletonInputValues* inputValues)
: m_DataStructure(dataStructure)
, m_InputValues(inputValues)
, m_ShouldCancel(shouldCancel)
, m_MessageHandler(mesgHandler)
{
}

// -----------------------------------------------------------------------------
GeneratePythonSkeleton::~GeneratePythonSkeleton() noexcept = default;

// -----------------------------------------------------------------------------
const std::atomic_bool& GeneratePythonSkeleton::getCancel()
{
return m_ShouldCancel;
}

// -----------------------------------------------------------------------------
Result<> GeneratePythonSkeleton::operator()()
{
if(m_InputValues->useExistingPlugin)
{
return nx::core::WritePythonFiltersToPlugin(m_InputValues->pluginInputDir, m_InputValues->filterNames);
}
else
{
return nx::core::WritePythonPluginFiles(m_InputValues->pluginOutputDir, m_InputValues->pluginName, m_InputValues->pluginName, "Description", m_InputValues->filterNames);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include "SimplnxCore/SimplnxCore_export.hpp"
#include "SimplnxCore/utils/PythonPluginTemplateFile.hpp"

#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/DataStructure/DataStructure.hpp"
#include "simplnx/Filter/IFilter.hpp"
#include "simplnx/Parameters/ArrayCreationParameter.hpp"
#include "simplnx/Parameters/ArraySelectionParameter.hpp"

namespace nx::core
{

struct SIMPLNXCORE_EXPORT GeneratePythonSkeletonInputValues
{
bool useExistingPlugin;
std::filesystem::path pluginInputDir;
std::filesystem::path pluginOutputDir;
std::string pluginName;
std::string pluginHumanName;
std::string filterNames;
};

/**
* @class ConditionalSetValue
* @brief This filter replaces values in the target array with a user specified value
* where a bool mask array specifies.
*/

class SIMPLNXCORE_EXPORT GeneratePythonSkeleton
{
public:
GeneratePythonSkeleton(DataStructure& dataStructure, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel, GeneratePythonSkeletonInputValues* inputValues);
~GeneratePythonSkeleton() noexcept;

GeneratePythonSkeleton(const GeneratePythonSkeleton&) = delete;
GeneratePythonSkeleton(GeneratePythonSkeleton&&) noexcept = delete;
GeneratePythonSkeleton& operator=(const GeneratePythonSkeleton&) = delete;
GeneratePythonSkeleton& operator=(GeneratePythonSkeleton&&) noexcept = delete;

Result<> operator()();

const std::atomic_bool& getCancel();

private:
DataStructure& m_DataStructure;
const GeneratePythonSkeletonInputValues* m_InputValues = nullptr;
const std::atomic_bool& m_ShouldCancel;
const IFilter::MessageHandler& m_MessageHandler;
};

} // namespace nx::core
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include "GeneratePythonSkeletonFilter.hpp"

#include "SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp"

#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/Filter/Actions/CreateArrayAction.hpp"
#include "simplnx/Utilities/SIMPLConversion.hpp"
#include "simplnx/Utilities/StringUtilities.hpp"

#include "simplnx/Parameters/BoolParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"

#include <filesystem>
namespace fs = std::filesystem;

using namespace nx::core;

namespace nx::core
{
//------------------------------------------------------------------------------
std::string GeneratePythonSkeletonFilter::name() const
{
return FilterTraits<GeneratePythonSkeletonFilter>::name.str();
}

//------------------------------------------------------------------------------
std::string GeneratePythonSkeletonFilter::className() const
{
return FilterTraits<GeneratePythonSkeletonFilter>::className;
}

//------------------------------------------------------------------------------
Uuid GeneratePythonSkeletonFilter::uuid() const
{
return FilterTraits<GeneratePythonSkeletonFilter>::uuid;
}

//------------------------------------------------------------------------------
std::string GeneratePythonSkeletonFilter::humanName() const
{
return "Generate Python Plugin or Python Filter";
}

//------------------------------------------------------------------------------
std::vector<std::string> GeneratePythonSkeletonFilter::defaultTags() const
{
return {className(), "Generic", "Python", "Plugin", "Skeleton", "Generate", "Create", "Template", "Code", "Produce",
"Form", "Develop", "Construct", "Make", "Build", "Engineer", "Invent", "Initiate", "Design"};
}

//------------------------------------------------------------------------------
Parameters GeneratePythonSkeletonFilter::parameters() const
{
Parameters params;

params.insertLinkableParameter(
std::make_unique<BoolParameter>(k_UseExistingPlugin_Key, "Use Existing Plugin", "Generate the list of filters into an existing plugin instead of creating a new plugin.", false));
params.insert(std::make_unique<StringParameter>(k_PluginName_Key, "Name of Plugin", "This is the name of the plugin.", "ExamplePlugin"));
params.insert(std::make_unique<StringParameter>(k_PluginHumanName_Key, "Human Name of Plugin", "This is the user facing name of the plugin.", "ExamplePlugin"));

params.insert(std::make_unique<FileSystemPathParameter>(k_PluginOutputDirectory_Key, "Plugin Output Directory", "The path to the output directory where the new plugin will be generated.",
fs::path(""), FileSystemPathParameter::ExtensionsType{}, FileSystemPathParameter::PathType::OutputDir));
params.insert(std::make_unique<FileSystemPathParameter>(k_PluginInputDirectory_Key, "Existing Plugin Location", "The location of the existing plugin's top level directory on the file system.",
fs::path(""), FileSystemPathParameter::ExtensionsType{}, FileSystemPathParameter::PathType::InputDir));

params.insert(
std::make_unique<StringParameter>(k_PluginFilterNames, "Filter Names (comma-separated)", "The names of filters that will be created, separated by commas (,).", "FirstFilter,SecondFilter"));

params.linkParameters(k_UseExistingPlugin_Key, k_PluginName_Key, false);
params.linkParameters(k_UseExistingPlugin_Key, k_PluginHumanName_Key, false);
params.linkParameters(k_UseExistingPlugin_Key, k_PluginOutputDirectory_Key, false);
params.linkParameters(k_UseExistingPlugin_Key, k_PluginInputDirectory_Key, true);

return params;
}

//------------------------------------------------------------------------------
IFilter::UniquePointer GeneratePythonSkeletonFilter::clone() const
{
return std::make_unique<GeneratePythonSkeletonFilter>();
}

//------------------------------------------------------------------------------
IFilter::PreflightResult GeneratePythonSkeletonFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler,
const std::atomic_bool& shouldCancel) const
{
auto useExistingPlugin = filterArgs.value<BoolParameter::ValueType>(k_UseExistingPlugin_Key);
auto pluginOutputDir = filterArgs.value<FileSystemPathParameter::ValueType>(k_PluginOutputDirectory_Key);
auto pluginName = filterArgs.value<StringParameter::ValueType>(k_PluginName_Key);

nx::core::Result<OutputActions> resultOutputActions;
std::vector<PreflightValue> preflightUpdatedValues;

if(!useExistingPlugin)
{
preflightUpdatedValues.push_back({"Generated Plugin Directory", fmt::format("", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName)});
}

return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
}

//------------------------------------------------------------------------------
Result<> GeneratePythonSkeletonFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler,
const std::atomic_bool& shouldCancel) const
{
GeneratePythonSkeletonInputValues inputValues;

inputValues.useExistingPlugin = filterArgs.value<BoolParameter::ValueType>(k_UseExistingPlugin_Key);
inputValues.pluginInputDir = filterArgs.value<FileSystemPathParameter::ValueType>(k_PluginInputDirectory_Key);
inputValues.pluginOutputDir = filterArgs.value<FileSystemPathParameter::ValueType>(k_PluginOutputDirectory_Key);
inputValues.pluginName = filterArgs.value<StringParameter::ValueType>(k_PluginName_Key);
inputValues.pluginHumanName = filterArgs.value<StringParameter::ValueType>(k_PluginHumanName_Key);
inputValues.filterNames = filterArgs.value<StringParameter::ValueType>(k_PluginFilterNames);

return GeneratePythonSkeleton(dataStructure, messageHandler, shouldCancel, &inputValues)();
}

} // namespace nx::core
Loading

0 comments on commit f869943

Please sign in to comment.