score provides ways to create hierarchical data models, based on an entity-component paradigm similar to game engines.
Organization
The objects in score are organized in a hierarchical tree :
This tree is based on Qt's QObject. See http://doc.qt.io/qt-5/object.html for more information.
This applies to all the "meaningful" objects in the software, i.e. the objects of the score domain (Process::ProcessModel, Scenario::IntervalModel, Automation::ProcessModel, etc).
We call these the "model" objects.
Model objects are all identified uniquely across the children of their parents.
E.g. the following case is possible :
The following case is not possible :
Identification of objects
Identifiers
Objects are identified by the couple Name.Identifier
(see ObjectIdentifier). The name comes from the object's objectName()
property. The identifier comes from the object's id()
property.
The numeric identifier of an object is a template class parametrized by the object's type (see id_base_t and Id).
This approach then allows us to have paths to objects, by chaining ObjectIdentifiers together. This is necessary for the Commands system, for instance.
Paths
Paths are a list of identifiers that lead from the root of the score::Document, to the actual object we are looking for.
There are two variants :
- ObjectPath is weakly typed.
- Path is a strongly typed wrapper over ObjectPath, like Id. It is the one to be used 90% of the time.
Paths allow to get a serializable reference to a specific entity in the object hierarchy.
This is necessary for undo-redo commands. Take the following case :
- An object is created by the user by drag'n'drop.
- A Command is instantiated for this action and applied with score::Command::redo.
- During
redo()
, the object is allocated, inserted in the hierarchy, etc. - Then, the object is moved : a new Command is created, with a Path to the moved object.
- Then, the user undoes everything : the move is undone, as well as the creation of the object.
- At this point, the object has been
delete
d : its memory has been freed and it is not available anymore. If any Command or other object had a pointer on this object, doing anything with this pointer would crash. - Then, the user decides to redo everything. A new object is created during the redoing of the first command.
- The second command is redone : since it had a Path to the object and not a pointer, it is able to find it instead of crashing, even though memory-wise, it is not the same object that was created initially.
Creating models
Base classes for custom model objects are provided :
- IdentifiedObject : provides identification.
- Entity<> : provides identification, Components and score::ModelMetadata.
Relationship to Qt's item models
The previous information is separate from Qt's model-view paradigm which is more useful when one wants to see a tree of objects in a tree widget.
This means that it does not apply to the "small" objects in the various QAbstractItemModel child classes, such as the nodes in the tree (Device::Node).
This is done mostly for performance & memory usage reasons, and because it would not be really useful for these cases.
For these objects, paths can still be saved with the TreePath class which is a simpler list of integers.
A base "Tree" QAbstractItemModel implementation is provided with TreeNodeItemModel, with the nodes of the tree are based on TreeNode.
Tree models for simple objects
This section refers to the handling of non-QObject objects. Such lightweight "data-like" objects (for the device explorer, the library, the states) don't own an identifier. They are identified by their position in their own tree. Such a data-like object can be put easily in a tree structure with the TreeNode mixin. It can then be exposed very easily to the Qt's model/view implementation, with NodeItemModel.
- See also
- Serialization