Clarisse 4.0 SP16 SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Extending classes in script

Table of Contents

This topic overviews the extension of Clarisse classes via script. Before proceeding further, please read Introduction to Clarisse Object Model and Using the Python API.

The Scripted Class

Sometimes it can be really useful to declare custom classes in Clarisse. While this can be done in C++ using Clarisse API, Classes can be extended in a limited way via script too. You can, for example, declare a completely new Clarisse class using Python.

Make sure that custom classes are defined prior to loading projects. If a project declares items relying on custom script classes that aren't defined in Clarisse then such item definitions are lost.

Clarisse API provides a special module dedicated to declare a custom class via script. This module is called ModuleScriptedClass. It defines a special static method ModuleScriptedClass::register_scripted_class that registers a custom class from a scripted implementation which must inherits from ModuleScriptedClassEngine. This class provides severals methods that must be overloaded and acts as an interface.

In the following example we will create a simple class in Python with a couple of attributes on which we will redefine ModuleScriptedClassEngine::on_attribute_change method. This method is called called each time an attribute value of a specific instance is modified.

1 class MyScriptedClass(ix.api.ModuleScriptedClassEngine):
2  def __init__(self):
3  ix.api.ModuleScriptedClassEngine.__init__(self)
5  def on_attribute_change(self, object, attr, dirtiness, dirtiness_flags):
6  # Do something when an attribute value is changing. To check
7  # which attribute is changing use the attribute name.
8  print "The attribute", attr.get_name(), "is changing"
10 # string using the CID syntax to declare attributes
11 my_cid = 'color "my_color" color "another_color"'
13 # Register our class to Clarisse with the supplied implementation
14 ix.api.ModuleScriptedClass.register_scripted_class(ix.application, "MyScriptedClass", MyScriptedClass(), my_cid)

Registering Scripted Class

As you might have noticed the previous example ends up with a call to ModuleScriptedClass::register_scripted_class.

1 ix.api.ModuleScriptedClass.register_scripted_class(ix.application, "MyScriptedClass", MyScriptedClass(), my_cid)

This call is extremely important as it allows you to declare and register a new class in Clarisse. In this example, we are providing an instance of OfClass|OfClass|MyScriptedClass as engine or implementation. The engine or implementation of a class is unique for each class. It is shared across each OfObject instance. In other words, you can't store instance specific data in it.

While there is no dedicated place to register scripted class, it is wise to make the registration very early in when Clarisse is loading. The recomanded place would be in a scripted called directly or indirectly by the startup script Clarisse is executing when it starts.

You can find which script is used as startup script in the Preferences panel. Go to Edit > Preferences.../Application/General/Startup Script

Creating per-class instance storage data.

Sometimes it is useful to store data per instance. For example, a script might want to load some data depending on attribute values. It may then want to keep the data resident in memory to access it later on. This is exactly why Clarisse API provide a dedicated per-class instance storage data.

To get an instance storage data you just have to inherit from ModuleScriptedClassEngineData and overload ModuleScriptedClassEngine::create_instance_data to return a new instance of your specialized class. Accessing to your per instance storage data is then performed using ModuleScriptedClass::get_instance_data()

1 if ix.selection[0].is_kindof("ScriptedClass"):
2  data = ix.selection[0].get_module().get_instance_data()

Here is a more complete example of a specialized data used in a scripted class engine.

1 # A specialized class that will be used to store per class instance data for OfClass|MyScriptedClass
2 class MyScriptedClassData(ix.api.ModuleScriptedClassEngineData):
3  def __init__(self):
4  ix.api.ModuleScriptedClassEngineData.__init__(self)
5  self.my_value = 0
7  def set_my_value(self, value):
8  self.my_value = value
10  def get_my_value(self):
11  return self.my_value
13 # class defining our engine
14 class MyScriptedClass(ix.api.ModuleScriptedClassEngine):
15  def __init__(self):
16  ix.api.ModuleScriptedClassEngine.__init__(self)
18  def create_instance_data(self, object):
19  # Return per instance data storage. This is called once per instance
20  print "creating instance data for", object.get_full_name()
21  data = MyScriptedClassData()
22  # you must call initialize_data before returning the data!
23  data.initialize_data()
24  return data
26  def on_attribute_change(self, object, attr, dirtiness, dirtiness_flags):
27  # accessing to instance storage data
28  my_data = object.get_module().get_instance_data()
29  my_data.get_my_value(len(object.get_full_name()))
31 # string using the CID syntax to declare attributes
32 my_cid = 'color "my_color"
34 # Register our class to Clarisse with the supplied implementation
35 ix.api.ModuleScriptedClass.register_scripted_class(ix.application, "MyScriptedClass", MyScriptedClass(), my_cid)

Working with actions

Actions (OfAction) can be seen as commands without arguments that are declared at the class level. Actions are displayed as buttons in the Attribute Editor. An example of action is the Fit to Bbox button that sets, when called, the texture projection according to the size of a selected Scene Object. You can declare as many actions as you want in a class, however their names musn't collide.

Each time an action is called, the call is redirected to ModuleScriptedClassEngine::on_action on which you get by argument the action that was called. This way it is very simple to know what to do by action name for example:

1 class MyScriptedClassAction(ix.api.ModuleScriptedClassEngine):
2  def __init__(self):
3  ix.api.ModuleScriptedClassEngine.__init__(self)
5  def on_action(self, action, object, data):
6  # Called each time an action is pressed by the user
7  if action.get_name() == "my_custom_action":
8  print "should do something"
9  elif action.get_name() == "my_2nd_custom_action":
10  print "should do something else"
11  else:
12  print "Unknown action"
14  def declare_attributes(self, cls):
15  # Declare attributes and actions to the newly class
16  # This method just after the class has been created with the optional CID
17  print cls.get_name()
18  self.add_action(cls, "my_custom_action", "action_group")
19  self.add_action(cls, "my_2nd_custom_action", "action_group")
21 # Register our class to Clarisse with the supplied implementation
22 ix.api.ModuleScriptedClass.register_scripted_class(ix.application, "MyScriptedClassAction", MyScriptedClassAction())

Creating a process via script

One thing that can be even more useful than creating Scripted Class is creating specialized scripted process. OfClass|ScriptedProcess allows you to create a Clarisse process for which the engine is created in script. A very good example of what can be acheived with it is the UV Baker process which is provided by default.

While ModuleProcessScript is very similar to ModuleScriptedClass, its registrating a OfClass|ProcessScript is performed using ModuleProcessScript::register_scripted_process. In the very same way, it needs as engine a specialized ModuleScriptClassEngine called ModuleProcessScriptEngine which defines a new method to overload: ModuleProcessScriptEngine::run. ModuleProcessScriptEngine::run is called each time the process is runned. A convenient and provided progress bar is as argument of the method. This progress bar allows to check if the process must be aborted using AppProgressBar::must_abort.

1 class MyScriptProcess(ix.api.ModuleProcessScriptEngine):
2  def __init__(self):
3  ix.api.ModuleProcessScriptEngine.__init__(self)
5  def run(self, object, options, progress):
6  while progress.must_abort() == False:
7  ix.application.wait_for_events()
8  #must_abort returns true when the user pressed abort
9  return True
11 print ix.api.ModuleProcessScript.register_scripted_process(ix.application, "MyScriptProcess", MyScriptProcess())