Skip to content

Commit 12c9140

Browse files
JDuffeyBQimikejacksonjmarquisbq
committed
PY: Add infrastructure to allow python plugins to be reloaded in a GUI. (#910)
Signed-off-by: Jared Duffey <[email protected]> Signed-off-by: Michael Jackson <[email protected]> Co-authored-by: Michael Jackson <[email protected]> Co-authored-by: Jessica Marquis <[email protected]>
1 parent fe979fe commit 12c9140

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1403
-343
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,11 @@ set(SIMPLNX_HDRS
461461
${SIMPLNX_SOURCE_DIR}/Parameters/util/DynamicTableInfo.hpp
462462
${SIMPLNX_SOURCE_DIR}/Parameters/util/ReadCSVData.hpp
463463

464+
${SIMPLNX_SOURCE_DIR}/Pipeline/AbstractPipelineFilter.hpp
464465
${SIMPLNX_SOURCE_DIR}/Pipeline/AbstractPipelineNode.hpp
465466
${SIMPLNX_SOURCE_DIR}/Pipeline/Pipeline.hpp
466467
${SIMPLNX_SOURCE_DIR}/Pipeline/PipelineFilter.hpp
468+
${SIMPLNX_SOURCE_DIR}/Pipeline/PlaceholderFilter.hpp
467469

468470
${SIMPLNX_SOURCE_DIR}/Pipeline/Messaging/AbstractPipelineMessage.hpp
469471
${SIMPLNX_SOURCE_DIR}/Pipeline/Messaging/FilterPreflightMessage.hpp
@@ -664,9 +666,11 @@ set(SIMPLNX_SRCS
664666
${SIMPLNX_SOURCE_DIR}/Parameters/util/ReadCSVData.cpp
665667
${SIMPLNX_SOURCE_DIR}/Parameters/util/DynamicTableInfo.cpp
666668

669+
${SIMPLNX_SOURCE_DIR}/Pipeline/AbstractPipelineFilter.cpp
667670
${SIMPLNX_SOURCE_DIR}/Pipeline/AbstractPipelineNode.cpp
668671
${SIMPLNX_SOURCE_DIR}/Pipeline/Pipeline.cpp
669672
${SIMPLNX_SOURCE_DIR}/Pipeline/PipelineFilter.cpp
673+
${SIMPLNX_SOURCE_DIR}/Pipeline/PlaceholderFilter.cpp
670674

671675
${SIMPLNX_SOURCE_DIR}/Pipeline/Messaging/AbstractPipelineMessage.cpp
672676
${SIMPLNX_SOURCE_DIR}/Pipeline/Messaging/FilterPreflightMessage.cpp

src/Plugins/SimplnxCore/CMakeLists.txt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -368,18 +368,6 @@ string(HEX ${PYTHON_PLUGIN_TEMPLATE} PYTHON_PLUGIN_TEMPLATE)
368368
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE ${PYTHON_PLUGIN_TEMPLATE})
369369
string(APPEND PYTHON_PLUGIN_TEMPLATE "0x00")
370370

371-
if(WINDOWS)
372-
file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.bat" PYTHON_PLUGIN_TEMPLATE_BAT)
373-
string(HEX ${PYTHON_PLUGIN_TEMPLATE_BAT} PYTHON_PLUGIN_TEMPLATE_BAT)
374-
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE_BAT ${PYTHON_PLUGIN_TEMPLATE_BAT})
375-
string(APPEND PYTHON_PLUGIN_TEMPLATE_BAT "0x00")
376-
else()
377-
file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.sh" PYTHON_PLUGIN_TEMPLATE_BAT)
378-
string(HEX ${PYTHON_PLUGIN_TEMPLATE_BAT} PYTHON_PLUGIN_TEMPLATE_BAT)
379-
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE_BAT ${PYTHON_PLUGIN_TEMPLATE_BAT})
380-
string(APPEND PYTHON_PLUGIN_TEMPLATE_BAT "0x00")
381-
endif()
382-
383371
cmpConfigureFileWithMD5Check(CONFIGURED_TEMPLATE_PATH "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.in.hpp"
384372
GENERATED_FILE_PATH "${${PLUGIN_NAME}_BINARY_DIR}/generated/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.hpp")
385373

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ Result<> GeneratePythonSkeleton::operator()()
3535
}
3636
else
3737
{
38-
return nx::core::WritePythonPluginFiles(m_InputValues->pluginOutputDir, m_InputValues->pluginName, m_InputValues->pluginName, "Description", m_InputValues->filterNames,
39-
m_InputValues->createBatchShellScript, m_InputValues->anacondaEnvName);
38+
return nx::core::WritePythonPluginFiles(m_InputValues->pluginOutputDir, m_InputValues->pluginName, m_InputValues->pluginName, "Description", m_InputValues->filterNames);
4039
}
4140
}

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ struct SIMPLNXCORE_EXPORT GeneratePythonSkeletonInputValues
2020
std::string pluginName;
2121
std::string pluginHumanName;
2222
std::string filterNames;
23-
bool createBatchShellScript;
24-
std::string anacondaEnvName;
2523
};
2624

2725
/**

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,10 @@ Parameters GeneratePythonSkeletonFilter::parameters() const
6666
params.insert(
6767
std::make_unique<StringParameter>(k_PluginFilterNames, "Filter Names (comma-separated)", "The names of filters that will be created, separated by commas (,).", "FirstFilter,SecondFilter"));
6868

69-
params.insertLinkableParameter(
70-
std::make_unique<BoolParameter>(k_CreateBatchFile_Key, "Create Anaconda Init Batch/Shell Script", "Generates a script file that can be used to export needed environment variables", false));
71-
params.insert(std::make_unique<StringParameter>(k_AnacondaEnvName_Key, "Anaconda Environment Name", "The name of the Anaconda environment.", "nxpython"));
72-
7369
params.linkParameters(k_UseExistingPlugin_Key, k_PluginName_Key, false);
7470
params.linkParameters(k_UseExistingPlugin_Key, k_PluginHumanName_Key, false);
7571
params.linkParameters(k_UseExistingPlugin_Key, k_PluginOutputDirectory_Key, false);
7672
params.linkParameters(k_UseExistingPlugin_Key, k_PluginInputDirectory_Key, true);
77-
params.linkParameters(k_CreateBatchFile_Key, k_AnacondaEnvName_Key, true);
7873
return params;
7974
}
8075

@@ -99,20 +94,41 @@ IFilter::PreflightResult GeneratePythonSkeletonFilter::preflightImpl(const DataS
9994

10095
auto filterList = StringUtilities::split(filterNames, ',');
10196

97+
std::stringstream preflightUpdatedValue;
98+
10299
std::string pluginPath = fmt::format("{}{}{}", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName);
103100
if(useExistingPlugin)
104101
{
105102
pluginPath = pluginInputDir.string();
106103
}
104+
std::string fullPath = fmt::format("{}{}{}{}Plugin.py", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName, std::string{fs::path::preferred_separator});
105+
if(std::filesystem::exists({fullPath}))
106+
{
107+
fullPath = "[REPLACE]: " + fullPath;
108+
}
109+
else
110+
{
111+
fullPath = "[New]: " + fullPath;
112+
}
113+
preflightUpdatedValue << fullPath << '\n';
107114

108-
std::stringstream preflightUpdatedValue;
115+
fullPath = fmt::format("{}{}{}{}__init__.py", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName, std::string{fs::path::preferred_separator});
116+
if(std::filesystem::exists({fullPath}))
117+
{
118+
fullPath = "[REPLACE]: " + fullPath;
119+
}
120+
else
121+
{
122+
fullPath = "[New]: " + fullPath;
123+
}
124+
preflightUpdatedValue << fullPath << '\n';
109125

110126
for(const auto& filterName : filterList)
111127
{
112-
std::string fullPath = fmt::format("{}{}{}.py", pluginPath, std::string{fs::path::preferred_separator}, filterName);
128+
fullPath = fmt::format("{}{}{}.py", pluginPath, std::string{fs::path::preferred_separator}, filterName);
113129
if(std::filesystem::exists({fullPath}))
114130
{
115-
fullPath = "[EXISTS]: " + fullPath;
131+
fullPath = "[REPLACE]: " + fullPath;
116132
}
117133
else
118134
{
@@ -139,8 +155,6 @@ Result<> GeneratePythonSkeletonFilter::executeImpl(DataStructure& dataStructure,
139155
inputValues.pluginName = filterArgs.value<StringParameter::ValueType>(k_PluginName_Key);
140156
inputValues.pluginHumanName = filterArgs.value<StringParameter::ValueType>(k_PluginHumanName_Key);
141157
inputValues.filterNames = filterArgs.value<StringParameter::ValueType>(k_PluginFilterNames);
142-
inputValues.createBatchShellScript = filterArgs.value<BoolParameter::ValueType>(k_CreateBatchFile_Key);
143-
inputValues.anacondaEnvName = filterArgs.value<StringParameter::ValueType>(k_AnacondaEnvName_Key);
144158

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

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ class SIMPLNXCORE_EXPORT GeneratePythonSkeletonFilter : public IFilter
2929
static inline constexpr StringLiteral k_PluginHumanName_Key = "plugin_human_name";
3030
static inline constexpr StringLiteral k_PluginInputDirectory_Key = "plugin_input_directory";
3131
static inline constexpr StringLiteral k_PluginOutputDirectory_Key = "plugin_output_directory";
32-
static inline constexpr StringLiteral k_CreateBatchFile_Key = "create_batch_shell_script";
33-
static inline constexpr StringLiteral k_AnacondaEnvName_Key = "anaconda_env_name";
3432
static inline constexpr StringLiteral k_PluginFilterNames = "filter_names";
3533

3634
/**

src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonFilterTemplate.py

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22
import simplnx as nx
33

44
class #PYTHON_FILTER_NAME#:
5-
"""
6-
This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name
7-
of the value should be ALL_CAPITOL_KEY
8-
"""
9-
TEST_KEY = 'test'
105

116
# -----------------------------------------------------------------------------
127
# These methods should not be edited
@@ -59,35 +54,91 @@ def default_tags(self) -> List[str]:
5954
:rtype: list
6055
"""
6156
return ['python', '#PYTHON_FILTER_HUMAN_NAME#']
62-
57+
58+
59+
"""
60+
This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name
61+
of the value should be ALL_CAPITOL_KEY
62+
"""
63+
ARRAY_PATH_KEY = 'output_array_path'
64+
NUM_TUPLES_KEY = 'num_tuples'
65+
6366
def parameters(self) -> nx.Parameters:
64-
"""This function defines the parameters that are needed by the filter. Parameters collect the values from the user
65-
or through a pipeline file.
67+
"""This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface
68+
and pack them up into a dictionary for use in the preflight and execute methods.
6669
"""
6770
params = nx.Parameters()
6871

69-
params.insert(nx.Float64Parameter(#PYTHON_FILTER_NAME#.TEST_KEY, 'Test', '', 0.0))
72+
params.insert(nx.ArrayCreationParameter(#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY, 'Created Array', 'Array storing the data', nx.DataPath()))
73+
74+
params.insert(nx.UInt64Parameter(#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY, 'Num Tuples', 'The number of tuples the array will have', 0))
7075

7176
return params
7277

7378
def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult:
7479
"""This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array
75-
sizes can be checked if the arrays are actually know at preflight time. Some filters will not be able to report output
76-
array sizes during preflight (segmentation filters for example).
80+
sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output
81+
array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1].
7782
:returns:
7883
:rtype: nx.IFilter.PreflightResult
7984
"""
80-
value: float = args[#PYTHON_FILTER_NAME#.TEST_KEY]
81-
message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Preflight: {value}'))
82-
return nx.IFilter.PreflightResult()
85+
86+
# Extract the values from the user interface from the 'args'
87+
data_array_path: nx.DataPath = args[#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY]
88+
num_tuples: int = args[#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY]
89+
90+
# Create an OutputActions object to hold any DataStructure modifications that we are going to make
91+
output_actions = nx.OutputActions()
92+
93+
# Create the Errors and Warnings Lists to commuicate back to the user if anything has gone wrong
94+
# errors = []
95+
# warnings = []
96+
# preflight_values = []
97+
98+
# Validate that the number of tuples > 0, otherwise return immediately with an error message
99+
if num_tuples == 0:
100+
return nx.IFilter.PreflightResult(nx.OutputActions(), [nx.Error(-65020, "The number of tuples should be at least 1.")])
101+
102+
# Append a "CreateArrayAction"
103+
output_actions.append_action(nx.CreateArrayAction(nx.DataType.float32, [num_tuples], [1], data_array_path))
104+
105+
# Send back any messages that will appear in the "Output" widget in the UI. This is optional.
106+
message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f"Creating array at: '{data_array_path.to_string('/')}' with {num_tuples} tuples"))
107+
108+
# Return the output_actions so the changes are reflected in the User Interface.
109+
return nx.IFilter.PreflightResult(output_actions=output_actions, errors=None, warnings=None, preflight_values=None)
83110

84111
def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult:
85112
""" This method actually executes the filter algorithm and reports results.
86113
:returns:
87114
:rtype: nx.IFilter.ExecuteResult
88115
"""
116+
# Extract the values from the user interface from the 'args'
117+
# This is basically repeated from the preflight because the variables are scoped to the method()
118+
data_array_path: nx.DataPath = args[#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY]
119+
num_tuples: int = args[#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY]
120+
121+
# At this point the array has been allocated with the proper number of tuples and components. And we can access
122+
# the data array through a numpy view.
123+
data_array_view = data_structure[data_array_path].npview()
124+
# Now you can go off and use numpy or anything else that can use a numpy view to modify the data
125+
# or use the data in another calculation. Any operation that works on the numpy view in-place
126+
# has an immediate effect within the DataStructure
127+
128+
# -----------------------------------------------------------------------------
129+
# If you want to send back progress on your filter, you can use the message_handler
130+
# -----------------------------------------------------------------------------
131+
message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Information Message: Num_Tuples = {num_tuples}'))
132+
133+
# -----------------------------------------------------------------------------
134+
# If you have a long running process, check the should_cancel to see if the user cancelled the filter
135+
# -----------------------------------------------------------------------------
136+
if not should_cancel:
137+
return nx.Result()
138+
89139

90-
value: float = args[#PYTHON_FILTER_NAME#.TEST_KEY]
91-
message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Execute: {value}'))
92140
return nx.Result()
93141

142+
143+
144+
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
2+
"""
3+
Insert documentation here for #PLUGIN_NAME#
4+
"""
15
from #PLUGIN_NAME#.Plugin import #PLUGIN_NAME#
26

3-
#PLUGIN_IMPORT_CODE## FILTER_INCLUDE_INSERT
7+
__all__ = ['#PLUGIN_NAME#', 'get_plugin']
48

5-
def get_plugin():
6-
return #PLUGIN_NAME#()
9+
"""
10+
This section conditionally tries to import each filter
11+
"""
712

8-
__all__ = [#PLUGIN_FILTER_LIST#] # FILTER_NAME_INSERT
13+
#PLUGIN_IMPORT_CODE#
914

15+
def get_plugin():
16+
return #PLUGIN_NAME#()

src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,4 @@ inline const std::string PluginInitPythonFile()
3737
return {k_PluginInitPythonFileCharArray};
3838
}
3939

40-
// clang-format off
41-
static const char k_PluginBatchFileCharArray[] = {@PYTHON_PLUGIN_TEMPLATE_BAT@};
42-
// clang-format on
43-
44-
inline const std::string PluginBatchFile()
45-
{
46-
return {k_PluginBatchFileCharArray};
47-
}
48-
4940
}; // namespace nx::core

src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.bat

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)