Hello,
below is an updated example that shows how to call a CLI module from C++. I used the code from the link, but it seems to be quite old. So, i you want an update, please take my code (with any modification).
void MyWidget::generate3dModel()
{
Q_D(MyWidget);
vtkMRMLScene* scene = qSlicerCoreApplication::application()->mrmlScene();
// get module and its widget
qSlicerCLIModule* cliModule = static_cast<qSlicerCLIModule*>(qSlicerCoreApplication::application()->moduleManager()->module("GrayscaleModelMaker"));
qSlicerCLIModuleWidget *cliWidget = static_cast<qSlicerCLIModuleWidget*>(cliModule->widgetRepresentation());
vtkSlicerCLIModuleLogic* moduleLogic = vtkSlicerCLIModuleLogic::SafeDownCast(cliModule->logic());
// get/create CLI module node
vtkMRMLCommandLineModuleNode* cliNode = moduleLogic->CreateNodeInScene();
// get/create output node
QString outputNodeID(this->volumeNode()->GetID());
outputNodeID += "Model";
vtkMRMLModelNode* outputNode = vtkMRMLModelNode::SafeDownCast(scene->GetFirstNodeByName(outputNodeID.toStdString().c_str()));
if (!outputNode)
{
outputNode = vtkMRMLModelNode::SafeDownCast(scene->CreateNodeByClass("vtkMRMLModelNode"));
outputNode->SetName(outputNodeID.toStdString().c_str());
// Add node to the scene
scene->AddNode(outputNode);
outputNode->Delete();
}
// extract threshold from volume display node
vtkMRMLScalarVolumeDisplayNode* VolumeDisplayNode = vtkMRMLScalarVolumeDisplayNode::SafeDownCast(this->volumeNode()->GetDisplayNode());
double threshold = VolumeDisplayNode->GetLowerThreshold();
// set parameters
cliNode->SetParameterAsNode("InputVolume", this->volumeNode());
cliNode->SetParameterAsNode("OutputGeometry", outputNode);
cliNode->SetParameterAsFloat("Threshold", threshold);
cliNode->SetParameterAsString("Name", outputNodeID.toStdString().c_str());
cliNode->SetParameterAsInt("Smooth", 15); // default
cliNode->SetParameterAsFloat("Decimate", 0.25f); // default
cliNode->SetParameterAsBool("SplitNormals", true); // default
cliNode->SetParameterAsBool("PointNormals", true); // default
// connect cli widget with cli module node
cliWidget->setCurrentCommandLineModuleNode(cliNode);
// Connect node modified event to the update the progress bar and to proceed to the next step
this->qvtkConnect(cliNode, vtkCommand::ModifiedEvent, this, SLOT(on3dModelGenerationProgress(vtkObject*)));
// start via logic
moduleLogic->Apply(cliNode);
}
void MyWidget::on3dModelGenerationProgress(vtkObject* cliNode)
{
Q_D(MyWidget);
vtkMRMLCommandLineModuleNode * node = vtkMRMLCommandLineModuleNode::SafeDownCast(cliNode);
if (node)
{
// Update Progress
ModuleProcessInformation* info = node->GetModuleDescription().GetProcessInformation();
switch (node->GetStatus())
{
case vtkMRMLCommandLineModuleNode::Cancelled:
d->cliProgressBar->setMaximum(0);
break;
case vtkMRMLCommandLineModuleNode::Scheduled:
d->cliProgressBar->setMaximum(0);
break;
case vtkMRMLCommandLineModuleNode::Running:
d->cliProgressBar->setMaximum(info->Progress != 0.0 ? 100 : 0);
d->cliProgressBar->setValue(info->Progress * 100.);
break;
case vtkMRMLCommandLineModuleNode::Completed:
case vtkMRMLCommandLineModuleNode::CompletedWithErrors:
d->cliProgressBar->reset();
this->qvtkDisconnect(cliNode, vtkCommand::ModifiedEvent, this, SLOT(on3dModelGenerationProgress(vtkObject*)));
break;
default:
case vtkMRMLCommandLineModuleNode::Idle:
d->cliProgressBar->reset();
break;
}
}
}
I have one question to this: where would be the right place to remove the vtkMRMLCommandLineModuleNode again after execution? I tried to remove it in the case vtkMRMLCommandLineModuleNode::Completed: case, but the program crashed…
The VTK object that invoked an event should not be deleted in the callback of that event. You can set a single-shot QTimer with 0 timeout to clean up the temporary nodes after the callback is completed.
It is an important design principle that all processing must be implemented in a module’s logic class, and not in the user interface. This decoupling of GUI from logic allows performing batch processing (without instantiating GUI) or using a module from another module. There are a few more similar principles - see development tutorial here.
Therefore, a few suggestions:
Do not retreive or use cliWidget. A module’s widget should never be used from another module.
Node ID gets assigned automatically when a node is added to the scene and must not be get or set manually. To create an output model node, add it to the scene, and retrieve the object point, use vtkMRMLNode* outputModelNode = scene->AddNewNodeByClass("vtkMRMLModelNode"); (this single line replaces 5-10 lines in your code)
All data processing should happen in your module logic, not in the GUI (widget) class, so move Generate3dModel method to your module logic and call that method from the GUI. See for example how it is done in Crop Volume module. If you need progress bar or execution in the background then you can return the created CLI module node to the caller.
Trivial: There is no “Name” parameter of Grayscale Model Maker module, so you can remove setting of that parameter.