ImFusion SDK 4.3
Using Properties and Configuration

Configuration Interface

Configuring objects, particularly algorithms, in a generic manner is an important capability for an extensible framework. The ImFusion framework achieves this goal by providing the Configurable interface, which offers methods for saving and loading an object's state to and from generic Properties objects.

Properties objects store arbitrary key-value pairs as strings in a hierarchical fashion and allow you to save and load these properties to and from files. This is extensively used within the framework to save the state of the application, the views, and the algorithms in workspace files. When loading a workspace file, the contained objects are automatically re-created and configured to their previous state. Since a string-based property representation is used, developers are completely free to choose the format in which they store state. Common data types such as matrices, integers, and vectors are directly supported by the framework and do not require custom code.

Properties

The Properties class provides a serialization interface for different kinds of values. Each Properties object consists of a list of parameters and a list of further sub-properties. Each parameter is represented by a unique name and stores a single value. Internally, all parameter values are stored as strings but can be implicitly converted to any supported type. The following types are supported:

Use setParam and param to set and retrieve a parameter value. The name can contain any character except '/'. If a parameter with the same name already exists, its value will be overridden.

Example:

Properties p;
int spam = 5;
p.setParam("Spam", spam);
spam = 10;
std::cout << "spam: " << spam << std::endl;
p.param("Spam", spam);
std::cout << "spam: " << spam << std::endl;
p.setParam("Spam", -8);
std::cout << "spam: " << spam << std::endl;
p.param("Spam", spam);
std::cout << "spam: " << spam << std::endl;
T endl(T... args)

Prints:

spam: 10
spam: 5
spam: 5
spam: -8

Parameter Type

Each parameter gets a PropertiesDetail::ParamType assigned automatically according to the template type of the last setParam call. Since this type can be overridden, it is merely descriptive: There is no guarantee that Properties::param can convert to that type. Changing the type of a parameter directly does not change its value in any way.

The types Enum, EnumString, and Path are never assigned automatically, since they are specialized versions of other types: Enum is a specialized version of Integer, EnumString and Path are specialized versions of String (see PropertiesWidget below for an example).

In general, Properties has some limited support for converting between different types. However, you should not rely on this behavior. An exception to this rule is std::string: since all values are stored as std::string internally, all values can be retrieved as strings. If a parameter cannot be converted to the requested type, param returns false. In this case, the value of the provided variable remains unchanged.

Properties p;
p.setParam("Spam", "foo");
p.paramType("Spam"); // returns ParamType::String
int spam = 5;
p.setParamType("Spam", ParamType::Integer);
if (!p.param("Spam", spam)) // "foo" cannot be converted to an int
std::cout << "Conversion failed" << std::endl;
std::cout << "spam: " << spam << std::endl;
p.setParam("Spam", 8.2);
p.paramType("Spam"); // returns ParamType::Double
double spamDouble = 0.0;
if (!p.param("Spam", spamDouble))
std::cout << "Conversion failed" << std::endl;
std::cout << "spamDouble: " << spamDouble << std::endl;
std::string spamString;
if (!p.param("Spam", spamString))
std::cout << "Conversion failed" << std::endl;
std::cout << "spamString: " << spamString << std::endl;
if (!p.param("Spam", spam)) // truncates double to int
std::cout << "Conversion failed" << std::endl;
std::cout << "spam: " << spam << std::endl;

Prints:

Conversion failed
spam: 5
spamDouble: 8.2
spamString: 8.2
spam: 8

Sub-Properties

In addition to parameters, a Properties object can also hold sub-properties (which can themselves have sub-properties, and so on). All sub-properties instances are owned by their parent Properties instance. Contrary to parameters, sub-properties names are not unique: there can be several sub-properties with the same name.

To access parameters in sub-properties directly, the path syntax can be used (see example). However, if multiple sub-properties share the same name, this syntax retrieves parameters only from the first sub-properties.

Example:

Properties p;
// Add a new sub-properties
auto foo = p.addSubProperties("foo");
foo->setParam("spam", 1);
// create another sub-properties with the same name
auto foo2 = p.addSubProperties("foo");
foo2->setParam("spam", 3);
// Alternatively create one only if it does not exist yet
auto bar = p.subProperties("bar", true);
bar->setParam("spam", 2);
auto bar2 = p.subProperties("bar", true);
assert(bar == bar2);
int spam = 0;
foo->param("spam", spam);
std::cout << "spam in foo: " << spam << std::endl;
bar->param("spam", spam);
std::cout << "spam in bar: " << spam << std::endl;
foo2->param("spam", spam);
std::cout << "spam in foo2: " << spam << std::endl;
// path syntax:
p.param("foo/spam", spam);
std::cout << "spam in foo: " << spam << std::endl;
p.param("bar/spam", spam);
std::cout << "spam in bar: " << spam << std::endl;

Prints:

spam in foo: 1
spam in bar: 2
spam in foo2: 3
spam in foo: 1
spam in bar: 2

PropertiesWidget

The PropertiesWidget provides a convenient way to display and edit a Properties instance in the UI. The "Properties Widget" automatically creates appropriate Qt widgets for each parameter. For example, a parameter with ParamType::Integer will be displayed as a QSpinBox, and a ParamType::String as a QLineEdit. Further customization is possible using the ParamType and the parameter's Attributes, as show in the example below for a widget with a QSpinBox, limited to a range of 1 to 5, and an enum with the values 'One' and 'Two':

Properties p;
p.setParam("Number", 3);
p.setParamAttributes("Number", "min: 1, max: 5");
p.setParam("Enum", "One");
p.setParamType("Enum", Properties::ParamType::EnumString); // certain types have to be specified explicitly
p.setParamAttributes("Enum", "values: 'One, Two'");
PropertiesWidget w;
w.configure(&p);

Some attributes restrict the number of possible values for a parameter (e.g., min/max for Integer). These restrictions are applied only in the PropertiesWidget and have no effect on the Properties::setParam method.

The PropertiesWidget never holds a reference to the Properties object it was configured with. This means that changes made by the user in the widget are not automatically reflected in the Properties object. To retrieve the changed properties, use the PropertiesWidget::configuration method. Be aware that this will only contain the parameters and their values, but not the ParamType and ParamAttributes of the original Properties object. The PropertiesWidget is designed with the Configurable interface in mind: the Configurable::configure method validates the given properties and updates the object's state accordingly; afterwards, Configurable::configuration returns the object's actual properties.

For example, to display the properties of an Algorithm:

SomeAlgorithm a;
Properties p;
a.configuration(&p);
PropertiesWidget w(&p);
/* ... */
// apply the changes from the widget to the algorithm
w.configuration(&p);
a.configure(&p);
// apply the validated properties from the algorithm back to the widget
p.clear();
a.configuration(&p);
w.configure(&p);

Common Pitfalls

  • Parameter names are case-sensitive (e.g. "Foo" and "foo" are different parameters)
  • configuration only returns parameter values, not types and attributes
  • floating point values lose precision when converted to strings (use Encoding::base64Encode to preserve the binary representation)
See also
Object Serialization
Search Tab / S to search, Esc to close