Project Item Operations

Overview

One of the main components of the Choreonoid framework is the “project item”. If you have experience using Choreonoid as a user, you should have a general understanding of what these are, but Projects and Items also provides a comprehensive explanation for reference.

When trying to implement meaningful functionality in Choreonoid, you will often be working with project items. In plugin development as well, mastering the use of project items is important for achieving your target functionality.

Therefore, this section introduces the “Item class”, which is the Choreonoid SDK class corresponding to project items, and explains an overview of how to manipulate project items through this class from plugins.

Note that we will use the abbreviated term “item” instead of “project item” in the following sections.

Item Class

The Choreonoid SDK defines the Item class as the base class for each item. This class defines and implements various information and processing that are fundamental for referencing and manipulating items.

This class is defined in the Base module of the Choreonoid SDK. For details on the class definition, see the API reference for the Item class. In the Choreonoid source repository, it corresponds to the source files Item.h and Item.cpp under src/Base.

The main features implemented in this class are shown below:

  • Setting, getting, and change notification for item names

  • Setting, getting, and change notification for parent-child/sibling relationships

  • Item searching

  • Setting, getting, and change notification for item selection/check states

It also provides APIs for uniformly handling the following features implemented in individual item types:

  • Item file saving and loading

  • Item project saving and loading

  • Item property display and editing

In this section, we will introduce operations related to “item names”, “parent-child/sibling relationships”, “searching”, and “selection/check states” from among these. This should give you an overview of how to manipulate items from programs. Other topics will be explained later in this development guide.

Item-Derived Classes

The Item class is the base class for items, but it does not itself have specific data or functionality to fulfill application purposes. Specific data and functionality are defined and implemented in individual item types that inherit from this Item class.

Various item types are defined in Choreonoid itself as such individual item types. To give you an idea of this, some existing item types are shown below:

+ Item
  + WorldItem: Virtual world
  + BodyItem: Physical/rendering models of robots and environments
  + ControllerItem: Base for robot control items
    + SimpleControllerItem: Simple controller implementation
  + SimulatorItem: Base for simulator items
    + AISTSimulatorItem: AIST simulator implementation
    + KinematicSimulatorItem: Kinematic simulator implementation
  + SceneItem: 3D rendering model
  + ScriptItem: Base for script items
    + PythonScriptItem: Implements Python script functionality

Here the inheritance relationships are shown in tree format. As you can see, some inherit directly from the Item class, while other item types have two levels of inheritance. In two-level cases, the first level often defines common APIs for specific functionality, and the second level implements specific implementations based on those APIs. Of course, there is no limit to the depth of inheritance, and examples with three or more levels exist.

In real feature implementation, utilizing the functionality of these individual items is also essential. You can also define your own item types and use them. With this in mind, there are three main levels of item manipulation and utilization:

  1. Perform basic item operations using Item class functionality

  2. Perform operations related to specific data and processing using individual item type functionality

  3. Define and implement your own item types to enable custom data and processing

The content introduced in this section mainly corresponds to level 1. The sample in the next section also uses Body items, which are individual item types, as an example of level 2. Level 3 will be explained in future sections.

Note

The explanations and samples from this section onward use Body items that correspond to robot and environment models. We also use related Body models and other objects. We assume that readers of this guide have experience running robot simulations using Choreonoid, in which case you should generally understand Body items and Body models. If not, please refer to Basics of Robot/Environment Models as needed. Your understanding will also deepen as you progress through this guide.

Item Parent-Child/Sibling Relationships

In Choreonoid, you generally work by combining multiple items. These multiple items are organized into a tree structure, which is called the Item Tree.

Having a tree structure means that parent-child and sibling relationships are built between items. This information is held by the Item class, and you can set and reference relationships using the Item class API.

For example, if you have two item instances called itemA and itemB:

itemA->addChildItem(itemB);

This sets itemB as a child item of itemA. This relationship is represented as follows:

+ itemA
  + itemB

Note

All items are Referenced type objects, dynamically created on heap memory and held by smart pointers ref_ptr. Therefore, coding related to items takes the form of pointers as shown above. Note that items are basically held by ref_ptr references from parent items, and since ref_ptr can be mutually converted with raw pointers, there is no particular problem using raw pointers for temporary references to items.

In this case, the following conditions hold:

itemA->childItem() == itemB
itemB->parentItem() == itemA

This way you can get the child item or parent item of each item.

At this point, itemA has no parent and itemB has no children, so:

itemA->parentItem() == nullptr
itemB->childItem() == nullptr

Next, suppose there is also an instance called itemC, which we also add as a child item of itemA:

itemA->addChildItem(itemC);

Then the item tree becomes:

+ itemA
  + itemB
  + itemC

Here itemA has two child items, and a sibling relationship is created between the two child items. Then the following conditions hold:

itemB->nextItem() == itemC
itemC->prevItem() == itemB

You can see sibling relationships by referencing nextItem and prevItem. Also in this case:

itemB->prevItem() == nullptr
itemC->nextItem() == nullptr

From the parent item itemA, you can also get the following information:

itemA->lastChildItem() == itemC
itemA->numChildren() == 2

You can also specify the position to add a child item. In that case, use the insertChild function. For example:

itemA->insertChild(itemC, itemD);

Then the item tree becomes:

+ itemA
  + itemB
  + itemD
  + itemC

Thus itemD was added at the position before itemC. The insertChild function has arguments like:

Item::insertChild(item to be the insertion position, item to insert)

Note

If following the same naming as addChildItem, it should be insertChildItem, but here we use a function called insertChild. Actually, there is also a function called insertChildItem, but the argument order is reversed, which is also opposite to the order seen in standard libraries. Since it’s common to take the insertion position as the first argument, insertChild was defined as a corrected version, and insertChildItem is now deprecated.

It’s common to want to apply certain processing to all child items of an item. You can code this using the childItem and nextItem functions as follows:

for(auto child = itemA->childItem(); child; child = child->nextItem()){
    doSomething(child);
}

To break a parent-child relationship, use the removeFromParentItem function on the child item. For example:

itemB->removeFromParentItem();

After execution, the item tree becomes:

+ itemA
  + itemD
  + itemC

In this case, sibling relationships involving itemB are also broken. Also, for the parent item itemA:

itemA->clearChildren();

This breaks all child items of itemA.

Root Item

There is always exactly one “root item” in Choreonoid. Items become visible and operable in the GUI by having a connection to the root item. Conversely, items without a connection to the root item are basically not subject to manipulation.

The root item is a singleton instance of the dedicated item type RootItem class. You can get it like:

#include<cnoid/RootItem>

...

auto rootItem = RootItem::instance();

The item tree shown as an example above cannot be handled in the GUI unless it is added to the root item. For example, for the previous tree:

+ itemA
  + itemB
  + itemC

You can connect it to the root item by:

RootItem::instance()->addChildItem(itemA);

In this case the tree becomes:

+ RootItem::instance()
  + itemA
    + itemB
    + itemC

And the tree from itemA onward is displayed in the Item Tree View. Conversely, items already loaded in the GUI are connected to the root item in this way.

In this state, the member function isConnectedToRoot() returns true for all items included in itemA’s subtree. When not connected to the root item, this returns false.

Whether an item is connected to the root item is an important element in Choreonoid, so please keep this in mind when programming.

Basic Item Attributes

The Item class holds information about basic item attributes. The attribute items are defined in the Item class enumeration type Attribute as follows:

Attribute

Content

SubItem

Item that is part of a composite item

Attached

Prohibits detachment from parent item

Temporal

Temporarily created item

LoadOnly

Item that can only be loaded

These attributes can be set and referenced using the following member functions of the Item class:

Function

Processing

void setAttribute(Attribute attribute)

Set the specified attribute

void unsetAttribute(Attribute attribute)

Clear the specified attribute

bool hasAttribute(Attribute attribute)

Check if the specified attribute is set

The SubItem attribute indicates whether an item is a component of a Composite Items. Items with this attribute are called “sub-items”. Sub-items cannot change parent-child/sibling relationships, save, load, or delete by themselves. They are always processed as integrated with the main item of the composite item.

This SubItem attribute can also be determined with the dedicated member function isSubItem. Also, when adding an item to a parent item, if you use the addSubItem function instead of the addChildItem function, the item is added as a sub-item.

The Attached attribute is for prohibiting detachment of an item from its parent item. This is similar to the SubItem attribute, but even when set, the item’s data and processing remain independent from the parent item, allowing independent saving and loading, for example. Only the operation of detaching from the parent in the GUI is prohibited. This is set when you want to always use an item together with its parent item, even though they are not originally integrated like composite items.

The Temporal attribute indicates that an item was created temporarily. When this attribute is assigned, the item is treated as if it doesn’t exist when saving the entire project. That is, it is not saved to the project file, so this item is not restored when the saved project is reloaded, nor is it saved to a file.

This is applied, for example, to items that store simulation result log data. Log data is used to replay simulation results on the spot, but does not necessarily need to be saved as part of the project. You can get the same log by re-running the simulation under the same conditions. Also, log data often becomes huge in size, so trying to save it would make operation cumbersome. For this reason, log data is treated as temporary data with the Temporal attribute.

The LoadOnly attribute is set when an item only supports loading from files and cannot be saved. This attribute should be considered when implementing items and doesn’t need special consideration in item operations.

ItemList Class

The ItemList template class is defined as a container for storing multiple Items. This is a type of array that stores item pointers (specifically smart pointers), but it becomes an array that selectively stores only items of the type given as the template argument.

This is used to extract only items of a specified type from functions that return multiple items.

First, include the header for this class:

#include <cnoid/ItemList>

Here, let’s assume there is a hypothetical function:

ItemList<Item> getItemList();

The return value of this function is an ItemList for Item type. That is, this list can store all item types. By the way, since the template argument of ItemList defaults to the Item class, a list for the Item class like this example can be written as ItemList<>. We’ll use that notation below.

Here, if you write:

ItemList<> items = getItemList();

items will contain the same results as the item set returned by getItemList. However, if you write:

ItemList<BodyItem> bodyItems = getItemlist();

Only items that match the BodyItem type among those returned by getItemList will be stored in bodyItems.

By using ItemList with a specific type specified in this way, you can selectively get only items that match that type. Between ItemLists, even if they target different types, you can use copy constructors and assignment operators mutually. In that case, what is actually copied is only the item types targeted by the newly created ItemList or the assignment destination ItemList.

The Choreonoid SDK has many functions that return ItemList, which can be conveniently used in actual usage. Specific examples are introduced below.

Note

Elements of ItemList are ref_ptr that hold items of the specified type. Therefore, items are guaranteed to remain alive while contained in the ItemList.

Item Selection/Check States

Items have “selection state” and “check state”. If you have experience using Choreonoid, you should already understand what these states are. If not, please refer to Item Tree Management - Selection and Check.

These states can of course be set and retrieved from programs. First, the following are available as member functions of the Item class:

Function

Processing

bool isSelected() const

Returns the current selection state

void setSelected(bool on, bool isCurrent = false)

Switches to the selection state specified by on

void setSubTreeItemsSelected(bool on)

Batch switches the selection state of items in the subtree including itself

bool isChecked(int checkId = PrimaryCheck) const

Returns the current check state

void setChecked(bool on)

Switches to the check state specified by on

These functions allow you to set or get the selection/check state of items. When you call the above setting functions from a program, the GUI state also switches immediately.

Note

The arguments isCurrent and checkId in the above functions are used for somewhat advanced usage methods. If there’s no particular reason, it’s fine to use them with default arguments.

To get the selection state within the entire item tree, use RootItem functions.

First, to get all selected items on the item tree:

ItemList<> selectedItems = RootItem::instance()->selectedItems();

This function also has a template version that specifies the item type. Using it, for example:

ItemList<BodyItem> selectedBodyItems = RootItem::instance()->selectedItems<BodyItem>();

You can get the currently selected Body items.

The same applies to check states:

ItemList<> checkedItems = RootItem::instance()->checkedItems();

Or specifying the item type:

ItemList<BodyItem> checkedBodyItems = RootItem::instance()->checkedItems<BodyItem>();

Referencing item selection and check states is often used to determine which items are targets for various functions. Please make appropriate use of these states in developing your own plugins.