Clarisse 5.0 SP8 SDK  5.0.5.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Using Clarisse commands system

Table of Contents

This topic covers how to use the Clarisse commands system to use existing commands and create new ones.

Commands are Clarisse functionalities made available in a user friendly way, with additional services like undo and redo.

Commands declared in Clarisse are automatically exposed to Python.

Command architecture

A command is structured into a class which inherits from OfCommand. This new class must implement two pure virtual methods:

For example, let's say you create a command that modifies an object during the exec call. You have first to store its state to be able to restore it during the unexec call. For more information please refer to Command History section.

// Example command class. Applies a transformation on an object.
class MyTransformCommand : public OfCommand {
public:
// We keep the object fullname because it can have been deleted and re-created between the exec and unexec (by an undo-ed delete command for example). The pointer would become invalid, but his name unchanged.
MyTransformCommand(OfApp& app, OfObject& object, CoreString& new_data) : m_object_name(object.get_fullname()), m_new_data(new_data) {
m_application = app;
}
// Must return true if command execution was successful, false otherwise
bool exec() {
OfObject *obj = m_application->get_factory().get_object(m_object_fullname);
if (obj != 0) {
// keep old object information for undo
m_previous_data = get_object_data();
apply_data(obj, new_data);
}
}
CoreString get_object_data(OfObject& obj) {
// retrieve object current data
}
// function doing the transformation on the object
void apply_data(OfObject& obj, CoreString& data) {
// apply transformation on the object
}
// Cancels the modifications done by exec function
void unexec() {
OfObject *obj = m_application->get_factory().get_object(m_object_fullname);
if (obj != 0) {
// re-apply old data on the object
apply_data(obj, m_previous_data);
}
}
private:
// we store application to be able to access object factory
OfApp* m_application;
// object fullname to retrieve the object
CoreString m_object_fullname;
// new object data
CoreString m_new_data;
// previous object data kept for undo
CoreString m_old_data;
}

Registering new commands

Note
Commands can only be registered within C++ modules. In other words, you can't register new commands coded in Python.

Once you created your command class it's time to register it in the command manager. The file app_command_manager.h in App library defines the class CommandRegistrar. This class is a helper which allows to quickly declare new commands.

Few parameters are required: Command name, command class info, parameters types and names.

To register the command 'MyTransformCommand' from the above example, we use the following line:

#include <of_command_manager.h>
...
CommandRegistrar::register_command("MyTransformCommand", COMMAND_CLASS(MyTransformCommand, 2),
OfCommandArgument::TYPE_OBJECT, "object_name",
OfCommandArgument::TYPE_STRING, "class_name");

A few things there:

If you have to perform specific things before calling the command when its called (like modifying, checking or retrieving arguments), you can change the way its registered.

You can specify your own specific function as second parameter:

#include <of_command_manager.h>
...
CommandRegistrar::register_command("MyTransformCommand", COMMAND_CREATE(MyCreationFunction),
OfCommandArgument::TYPE_OBJECT, "object_name",
OfCommandArgument::TYPE_STRING, "class_name");
MyCreationFunction(void *data, const CoreArray<OfCommandArgument>& args)
{
AppObject* app = (AppObject*) data;
checked_arg0 = validate_object(args[0]);
checked_arg1 = validate_value(args[1]);
MyTransformCommand *cmd = new MyTransformCommand(app, checked_arg0, checked_arg1);
return cmd;
}

This is just an example of why you could need such declaration.

Finally, you may want to have multiple signatures for the same Command. It is possible by creating multiple constructors for the same command with different argument numbers. Then you just have to register several times, one for each constructor:

// registration that will use constructor with 2 parameters
CommandRegistrar::register_command("MyTransformCommand", COMMAND_CLASS(MyTransformCommand, 2),
OfCommandArgument::TYPE_OBJECT, "object_name",
OfCommandArgument::TYPE_STRING, "class_name");
// registration that will use constructor with 4 arguments
CommandRegistrar::register_command("MyTransformCommand", COMMAND_CLASS(MyTransformCommand, 4),
OfCommandArgument::TYPE_OBJECT, "object_name",
OfCommandArgument::TYPE_STRING, "class_name",
OfCommandArgument::TYPE_STRING, "param_3",
OfCommandArgument::TYPE_INT, "param_4");

Calling commands

C++

The following code calls a registered command:

args[0] = *get_application().get_factory().get("project://my_object");
args[1] = "new_value";
CommandRegistrar::call("MyTransformCommand", args)

Python

Each time a new command is registered, it is automatically exposed in the Python namespace cmds. The previous registration example also defines the command in Python which can be called using:

1 ix.cmds.MyTransformCommand("project://MyObjectName", "MyNewValue")

You have to give the proper parameters types that the command expects, with some exceptions:

And, of course, you may want that a specific command is NOT available in python. You just have to specify it during registration:

CommandRegistrar::register_command("MyTransformCommand", COMMAND_CREATE(MyCreationFunction), OfCommandArgument::TYPE_OBJECT, "object_name", OfCommandArgument::TYPE_STRING, "class_name", 0, false);

The last argument false specifies we don't want this signature to be exposed in Python. The 0 is a optional custom data (here empty).

Command History

When a user performs an action, a command should be systematically called as this is how Clarisse performs undo. Each time a command is called, it is appended to the command history. Undoing an action then becomes simply a call to the implementation of its unexec method.

In order to avoid creating a command each time an attribute is modified in a script, command history is automatically disabled before a Python script is run. However, sometimes, it can be useful to be able to undo a script.

To avoid filling the application command history, it's recommended to create a batch command. A batch command simply stacks commands in a local history. That local history then appears as a single named entry in the main history.

To create a bach command you must first make sure to activate the command history:

1 ix.enable_command_history()

Then you just have to call:

1 ix.begin_command_batch("MyBatchCommandName")

and when completed.

1 ix.end_command_batch()
2 ix.disable_command_history()
Note
At the end of the script execution, Clarisse makes sure to call to ix.end_command_batch, if necessary. Failling to do so could corrupt command history.