7.2. Writing a C++ Process¶
From C++, a process is a single class inheriting one of three classes:
StackProcess
, MeshProcess
or GlobalProcess
. Which class the process
inherits from will decide where in the GUI it will appear and what other
processes it can call. In short, if inheriting from StackProcess
or
MeshProcess
only processes of the same type can be called, while inheriting
from GlobalProcess
allows a process to call any other process.
A process’s class should follow the following pattern, in which Some
must be
replaced by Stack
, Mesh
or Global
class ProcessName : public SomeProcess {
public:
ProcessName(const SomeProcess& process)
: Process(process)
, SomeProcess(process)
{
}
// The two following methods are mandatory
bool operator()(const ParmList& parms) override {
// Implementation of the process
}
QString name() const override {
return "[Process Name]";
}
// All other methods are optional
bool initialize(ParmList& parms, QWidget* parent) override {
// Provide user interface to adjust the list of parameters
// Note: the number of parameters cannot change!
}
QString description() const override {
return "[Process Description]";
}
QString folder() const override {
return "[Folder in which the process is stored]";
}
QStringList parmNames() const override {
return QStringList() << "Parm1"
<< "Parm2"
<< ...;
}
QStringList parmDescs() const override {
return QStringList() << "Description Parm1"
<< "Description Parm2"
<< ...;
}
ParmList parmDefaults() const override {
return ParmList() << value1
<< value2
<< ...;
}
ParmChoiceMap parmChoice() const override {
ParmChoiceMap map;
map[ParmNum1] = QStringList() << "Value 1"
<< "Value 2"
<< ...;
map[ParmNum2] = QStringList() << "Value 1"
<< "Value 2"
<< ...;
// ...
return map;
}
QIcon icon() const override {
return QIcon(":/path/to/resource");
}
}
When writing the process, the first task is to parse the parameters. Then, it is
customary to provide another operator()
method for direct call from C++ with
the parameters already parsed.
7.2.1. Process Execution¶
Processes are executed in a separate thread, from which you cannot run any
GUI-related events. This means you must never try to provide a user interface
from within a process main method. If you want a user interface, it must be
implemented in the Process::initialize(...)
method.
During the process execution, there are two ways to show the user the progress of the algorithm:
- Use the
lgx::Progress
class - Use the
lgx::process::Process::updateState()
method. This will pause the process while LithoGraphX updates the view, showing the current state. Be careful for the mesh to be in a proper state when you call this function. Also, allchanged
states will be reset after that.
7.2.2. Parameter Parsing¶
The ParmList
type is an alias for a QList
or QVariant
. When using
types, remember that the user must be able to type values as a string.
QVariant
are quite good at converting from string to various values, except
for boolean values. So if you need boolean values, You should use the
parmToBool
function, which will convert the strings “yes”, “y”, “true”, “t”,
“on” or “1” as true and any other value as false.
7.2.3. Calling other processes¶
There are two ways to call other processes, depending on the availability of the
C++ definition or not. The more general method consist in using the
runProcess
method of the Process
class. The signation of the function
is:
-
bool
Process
::
runProcess
(const QString &processType, const QString &processName, const ParmList &parms)¶ Launch a process by name
Note that any exception launched by the process will be filtered and converted into an error message and the process returning false.
If the C++ implementation is available (through a library for exemple), then it
is possible to directly instanciate the process, which allows to call the
internal operator()
method. Say you want to use the SieveFilter
, you can
write (see include/SieveFilter/SieveFilter.hpp
in the system processes
folder for details):
SieveFilter filter(*this);
filter(input, output, "Median", 100.f, false);
7.2.4. Modifying the State¶
When a process modifies the state (e.g. image, mesh, transformation, …), it needs to inform the system. For performance reasons, it would be impractical to either track all changes or to re-analyse all the data structures when coming back from a process. This section will explain first how images and meshes are represented and then how to inform the system about what has been changed.
7.2.4.3. Image Modifications¶
Images are modified through the lgx::Store
objects. If the image is
modified, you can call one of two functions:
-
void
lgx::Store
::
changed
()¶ Call this function for large-scale changed in the image, requiring the whole image to be updated.
-
void
lgx::Store
::
changed
(const BoundingBox3i &bbox)¶ Call this function for localised changes. The bounding box should include all the voxel changed.
Other aspects of image rendering (size, transfer function, …) are small and simple enough for their modifications to be tracked automatically.
7.2.4.4. Meshes Modifications¶
There are five different functions to mark what changed in a mesh:
-
void
lgx::Mesh
::
updateTriangles
()¶ Call this function if triangles properties have change, which means label or heat map.
-
void
lgx::Mesh
::
updateLines
()¶ Call this function if the “lines” change. This means mostly cell or mesh edges.
-
void
lgx::Mesh
::
updatePositions
()¶ Call this function if the position of the vertices have changed.
-
void
lgx::Mesh
::
updateSelection
()¶ Call this function if the selection has changed.
-
void
lgx::Mesh
::
updateAll
()¶ Call this function if you would have called all the others or if you added/removed vertices.
7.2.5. Useful Utilities¶
Here are a few features you might want to look at in the developer documentation:
- File
src/Dir.hpp
for path manipulations you really need to use functions here to manage paths, in particular to save paths in files. - File
src/Geometry.hpp
and class templateslgx::util::Vector
andlgx::util::Matrix
for geometry - Class
lgx::util::CSVStream
to parse CSV files - Class
lgx::util::PlyFile
to parse PLY files (e.g. mesh files) - To run a process over a whole collection use the functions in
src/Algorithms.hpp
. - Use the class
lgx::Progress
to provide a progress bar, possibly with a mean for the user to cancel the process. - Use the class
lgx::Image5D
and associated functions to load/save images.