概要ItemクラスItem継承クラスアイテムの親子/兄弟関係ルートアイテムアイテムの基本属性ItemListクラスアイテムの検索アイテムの選択/チェック状態アイテム関連シグナル概要¶Choreonoidのフレームワークの主要な構成要素のひとつが「プロジェクトアイテム」です。ひととおり説明していますので、そちらも確認いただければと思います。Choreonoid上で何か意味のある機能を実現しようとすると、多くの場合このプロジェクトアイテムと関わることになります。独自プラグインの開発においても、このプロジェクトアイテムを使いこなすことが、目標とする機能を実現するにあたって重要となります。SDKのクラスである「Itemクラス」について紹介し、プラグインからこのクラスを介してプロジェクトアイテムを操作する方法の概要を解説します。プロジェクトアイテムについて以下では略称の「アイテム」という用語を用いることにします。SDKでは各アイテムの基底となるクラスとしてItemクラスが定義されています。このクラスにはアイテムを参照したり操作するにあたって基本となる各種情報や処理が定義・実装されています。SDKのBaseモジュールで定義されています。いうソースファイルが対応しています。このクラスに実装されている主要な機能を以下に示します。個別のアイテム型で実装される以下の機能を統一的に扱うためのAPIを提供します。/チェック状態」に関する操作を紹介します。これによりプログラムからアイテムを操作する方法の概要を把握していただければと思います。他の項目については、本開発ガイドの中で追って解説していきます。Item継承クラス¶Itemクラスはアイテムの基底となるクラスですが、それ自体はアプリケーションの目的を果たすための特定のデータや機能を有するものではありません。特定のデータや機能は、このItemクラスを継承した個別のアイテム型で定義・実装されます。そのような個別のアイテム型として、Choreonoid本体では様々なアイテム型が定義されています。このイメージをつかんでいただくため、いくつかの既存アイテム型を以下に示します。Pythonスクリプト機能を実装ここでは継承関係をツリーの形式で表しています。これを見て分かるように、Itemクラスを直接継承したものもあれば、2段階の継承を行っているアイテム型もあります。2段階の場合は、1段階目で特定の機能の共通APIを定義し、そのAPIに基づく個別の実装を2段階目で行うということが多いです。継承の深さに制限は無く、3段階以上の例もあります。現実の機能実装では、これらの個別アイテムの機能を活用することも欠かせません。独自のアイテム型を定義して、それを利用することもできます。これを踏まえると、アイテムの操作・活用については、大きく以下の3段階があると言えます。Itemクラスの機能によりアイテムの基本的な操作を行う個別アイテム型の機能により、特定のデータや処理に関する操作を行う独自のアイテム型を定義・実装し、独自のデータや処理を使用可能にする本節で紹介する内容は主に1に該当するものです。次節のサンプルでは個別アイテム型であるBodyアイテムも利用し、2の例として紹介します。今後の節で3についても解説します。本節以降の説明やサンプルではロボットや環境のモデルに対応するBodyアイテムを使用します。関連するBodyモデルやその他のオブジェクトについてもあわせて使用します。本ガイドの読者はChoreonoidを用いてロボットのシミュレーションを行った経験があることを想定しており、その場合BodyアイテムやBodyモデルについては概ね把握されているかと思います。本ガイドを読み進める中でより理解が深まるかと思います。アイテムの親子/兄弟関係¶Choreonoidでは一般的に複数のアイテムを組み合わせて作業を行います。ツリー構造になるということは、アイテム間で親子関係や兄弟関係が構築されるということでもあります。この情報はItemクラスが有しており、ItemクラスのAPIで関係の設定や参照を行えるようになっています。itemAとitemBという2つのアイテムのインスタンスがあるときに、itemA->addChildItem(itemB);とすると、itemBはitemAの子アイテムとして設定されます。この関係は以下のように表されます。オブジェクトであり、ヒープメモリ上に動的に生成され、スマートポインタref_ptrで保持されます。従ってアイテムに関するコーディングは上記のようにポインタの形態となります。アイテムは基本的に親アイテムからref_ptrによる参照で保持されており、ref_ptrは生ポインタと相互に変換が可能なため、アイテムの一時的な参照においては生ポインタを使用しても特に問題はありません。この場合以下の条件が成り立ちます。itemAこのように各アイテムの子アイテムや親アイテムを取得することができます。nullptrとなります。次にitemCというインスタンスもあるとして、これもitemAの子アイテムとして追加するとします。itemA->addChildItem(itemC);するとアイテムツリーは以下のようになります。itemCここでitemAの子アイテムが2つになり、2つの子アイテム間で兄弟関係ができました。すると以下の条件が成立します。itemBこのように兄弟関係はnextItem、prevItemを参照することで分かります。nullptrとなります。親アイテムであるitemAからは以下の情報も取得できます。2子アイテムを追加する位置を指定することもできます。その場合はinsertChild関数を使います。itemD);とするとアイテムツリーは以下になります。itemCこのようにitemDはitemCの手間の位置に追加されました。このようにinsertChildはItem::insertChild(挿入位置となるアイテム、挿入するアイテム)という引数になります。注釈addChildItemと同様の命名であればinsertChildItemとなるべきですが、ここではinsertChildという関数を使用しています。実はinsertChildItemという関数もあるのですが、引数の順序が逆で、標準ライブラリなどでみられる順序とも逆になっています。一般的には挿入位置を最初の引数にとることが多いので、そのための修正版としてinsertChildが定義され、insertChildItemは現在deprecatedとなっています。あるアイテムの全ての子アイテムに対して所定の処理を適用したいことがよくあります。これはchildItem関数とnextItem関数を使用して以下のようにコーディングします。doSomething(child);}親子関係を解消したいときは子アイテムに対してremoveFromParentItem関数を使用します。itemB->removeFromParentItem();を実行すると、アイテムツリーは以下になります。itemCこの場合itemBに関わる兄弟関係も解消されます。親アイテムitemAに対してitemA->clearChildren();とすると、itemAの子アイテムが全て解消されます。ルートアイテム¶Choreonoid上では常にただひとつの「ルートアイテム」が存在します。各アイテムは、ルートアイテムとの繋がりを持つことによって、GUI上で表示され、操作できるようになります。逆に言えばルートアイテムとの繋がりの無いアイテムは基本的に操作対象とはなりません。シングルトンインスタンスとなります。RootItem::instance();などとして取得することができます。上で例として挙げたアイテムツリーも、ルートアイテムに追加されていなければGUI上で扱うことはできません。itemCというツリーについて、RootItem::instance()->addChildItem(itemA);とすることでルートアイテムと接続できます。逆に言えば、既にGUI上に読み込まれているアイテムは、このようにルートアイテムと接続された状態になっています。ルートアイテムに接続されていないときはこれがfalseを返します。このようにアイテムがルートアイテムに接続されているかどうかはChoreonoidでは重要な要素となりますので、プラグラミングの際に留意してください。アイテムの基本属性¶Itemクラスはアイテムの基本属性に関する情報を持っています。基本属性の項目はItemクラスの列挙型Attributeにて、以下が定義されています。内容SubItem複合アイテムの一部となるアイテムAttached親アイテムからの切り離しを禁止Temporal一時的に生成されるアイテムLoadOnlyロードのみ可能なアイテムこれらの属性は、Itemクラスの以下のメンバ関数で設定・参照ができます。構成要素であるかどうかを表す属性です。この属性が付いているアイテムは「サブアイテム」と呼びます。サブアイテムはそれ単体で親子/兄弟関係を変えたり、保存や読み込みを行ったり、削除するといったことができません。常に複合アイテムの本体アイテムと一体化して処理されるようになります。アイテムを親アイテムに追加する際に、addChildItem関数の代わりにaddSubItem関数を用いると、アイテムはサブアイテムとして追加されます。Attached属性は、アイテムの親アイテムからの切り離しを禁止するための属性です。これはSubItem属性と似た属性ではありますが、これが設定されていてもアイテムのデータや処理は親アイテムからは独立しており、例えば独立して保存や読み込みを行うこともできます。その上で、GUI上で親から切り離すという操作だけが禁止されます。これは元々は複合アイテムのように一体化しているわけではないものの、アイテムを親アイテムと必ずまとめて使用したいときに、設定されることになります。Temporal属性は、アイテムが一時的に生成されたものであることを示すものです。この属性が付与されていると、プロジェクト全体を保存する場合に、該当するアイテムがあたかも存在しないかのように扱われます。プロジェクトファイルには保存されないので、保存したプロジェクトを再度読み込む際にこのアイテムは復元されませんし、ファイルに保存されることもありません。これは例えばシミュレーション結果のログデータを格納するアイテムに適用されます。ログデータはその場でシミュレーション結果を再生し直すのに使用されますが、必ずしもプロジェクトの一部として保存する必要はありません。シミュレーションを同じ条件で実行しなおせば同じログが得られるからです。ログデータはサイズが巨大になることも多いため、保存しようとするとかえって運用が面倒になってしまいます。このためログデータはTemporal属性を付与して一時的なデータとして扱うようになっています。LoadOnly属性についてはアイテムがファイルからの読み込みのみをサポートしていて、保存することがない場合に設定されます。この属性はアイテムの実装時に考慮すべきもので、アイテムの操作においては特に考慮する必要はありません。いうテンプレートクラスが定義されています。これはアイテムのポインタ(正確にはスマートポインタ)を格納する一種の配列ですが、テンプレート引数に与えた型のアイテムのみを選別的に格納する配列となっています。これは複数アイテムを返す関数に対して、指定した型のアイテムのみを取り出す目的で使用します。使用にあたってまずこのクラスのヘッダをインクルードしておきます。getItemList();という関数があるとしましょう。この関数の戻り値はItem型を対象としたItemListになっています。このリストには全てのアイテム型が格納可能となっています。記述することができます。以下ではその書き方を用います。getItemList();と記述すると、itemsにはgetItemListが返すアイテム集合と同じ結果が入ります。getItemlist();とすると、getItemListが返すアイテムのうち、BodyItem型に該当するものだけがbodyItemsに格納されることになります。このようにして、特定の型を指定したItemListを用いることで、その型にマッチするアイテムのみを選別して取得することができます。ItemList同士では、対象とする型が異なっていても、コピーコンストラクタや代入オペレータを相互に使用することができます。その場合実際にコピーされるのは新たに生成されるItemListや代入先のItemListが対象とするアイテム型のみになるというわけです。SDKではItemListを返す関数がいくつもあり、実際の利用において便利に使用することができます。以下ではその具体例を紹介します。注釈ItemListの要素は指定した型のアイテムを保持するref_ptrとなります。従ってアイテムがItemListに含まれる間は必ず生存が維持されます。アイテムの検索¶アイテムツリー上のアイテムについて、与えた条件にマッチするものを取得することができます。利用する例としては、あるアイテム型に適合するアイテムをツリーの中から抽出するという操作です。item->descendantItems<BodyItem>();とすると、itemを起点とするアイテムツリーの中からBodyItem型のアイテムを抽出して、ItemListとして返します。descendantItemsは実行対象のアイテムを起点とするサブツリーの要素を取得する関数です。このテンプレート引数に特定のアイテム型を指定することで、そのアイテム型のアイテムだけが抽出されます。この例は、サブツリー内の全てのBodyItemに対してある一定の操作を行いたい、という場合に利用できます。起点をルートアイテムにすればプロジェクトに存在する全てのBodyItemが抽出されます。アイテムを名前で検索することもできます。item->findChildItem("Robot");とすると、itemの子アイテムの中から"Robot"という名前をもつ最初のアイテムを検索し、存在すればそのアイテムを返します。アイテムが存在しない場合はnullptrが返ります。この関数では階層化した名前も指定できます。item->findChildItem("World/Robot/Controller");とすると、itemを起点として以下の構造でControllerまでたどり着ければ、それを返します。ここで指定している階層化された名前は「検索パス」と言い換えることができます。item->findChildItem<BodyItem>("Robot");とすると、"Robot"という名前を持つBodyItem型の子アイテムがある場合に、それを返します。この場合同じ名前のアイテムがあっても型が異なればnullptrが返ります。findChildItemと同様ですが少し挙動の異なるfindItemという関数もあります。そちらは検索パスについて必ずしも対象のアイテムを起点としていなくてもマッチします。item->findItem("Robot/Controller");とすると、Controllerが返ります。このfindItem関数も、findChildItemと同様にテンプレート版があります。上記の関数では、それぞれの基本となる検索方法をベースに、任意の検索条件を指定することもできます。その場合検索条件はアイテムを引数にとりbool値を返す関数オブジェクトとして与えます。});とすると、item以下でSubItem属性のあるアイテムを返します。findChildItemやfindItemにおいても検索パスを任意の検索条件で置き換えたり、検索条件を追加の引数で指定することができます。これまであるアイテムを起点としたサブツリー内を検索する機能を紹介しましたが、逆にあるアイテムの親アイテムを辿って検索する機能として、findOwnerItemという関数があります。bodyItem->findOwnerItem<WorldItem>();とします。するとbodyItemから親アイテムを探索していき、WorldItem型のアイテムが見つかった時点でそれを返します。見つからなければやはりnullptrを返します。この機能は実際にChoreonoidの各種機能の実装において頻繁に使用されています。Chorenoidは基本的にアイテム間の関係性をその親子関係で判断する設計となっていますので、あるアイテムの処理をその親(や祖先)のアイテムと連携して行うことも多く、その場合に上記の方法で連携するアイテムを検索することができます。アイテムの選択/チェック状態¶アイテムには「選択状態」と「チェック状態」があります。Choreonoidの使用経験があれば既にこれらの状態についてはどのようなものかお分かりかと思います。これらの状態はもちろんプログラムから設定・取得することが可能です。Itemクラスのメンバ関数として以下を利用できます。on)onで指定したチェック状態に切り替えるこれらの関数により、アイテムの選択/チェック状態を設定したり取得できます。プログラムから上記の設定関数を呼び出すと、GUI上の状態も直ちに切り替わります。上記関数の引数isCurrentやcheckIdはやや高度な利用方法で使用する引数で、特に理由がなければデフォルト引数で使用していただければOKです。アイテムツリー全体の中での選択状態を取得したい場合はRootItemの関数を使用します。RootItem::instance()->selectedItems();とします。この関数はアイテム型を指定するテンプレート版もあります。RootItem::instance()->selectedItems<BodyItem>();とすることで、選択中のBodyアイテムを取得することができます。チェック状態についても同様です。RootItem::instance()->checkedItems<BodyItem>();とすることができます。アイテムの選択状態やチェック状態の参照は、各機能の操作対象とするアイテムを決定するのによく利用されています。独自プラグインの開発においてもこれらの状態を適切に活用してください。アイテム関連シグナル¶アイテムツリーの変化やアイテムの状態変化について、対応するシグナルが定義されています。それらのシグナルを用いることで、アイテムに対して適切なタイミングで適切な処理を行ったり、各機能の対象となるアイテムを適切に切り替えることが可能となります。個々のアイテムの変化を知らせるシグナルが、Itemクラスのメンバとして定義されています。以下に利用可能なものを挙げます。いずれもSignalProxyを返すメンバ関数として定義されています。位置の変化には、パスの変化に加えて、兄弟アイテム間の順序の変化も含まれる。sigUpdated()自身の内容が更新されたときまたRootItemクラスに定義されている以下のシグナルも利用可能です。いずれもルートアイテムを起点とするアイテムツリー内での変化をしらせるもので、ルートアイテムに接続されていないアイテムについてはこれらのシグナルの対象となりません。sigItemAdded()アイテムが追加されたとき。sigSubTreeAddedと同じタイミングで、追加されたサブツリー内の各アイテムごとに本シグナルが送出される。sigItemMoved();アイテムが移動したとき。sigSubTreeMovedと同じタイミングで、移動したサブツリー内の各アイテムごとに本シグナルが送出される。sigSubTreeRemoving()itemを起点とするサブツリーが削除される直前。このシグナルはサブツリー移動時にも送出される。その場合sigSubTreeMovedの前に送出され、isMovingがtrueとなる。sigSubTreeRemoved()itemを起点とするサブツリーが削除されたとき。このシグナルは完全に削除されたときのみ送出され、移動時には送出されない。sigItemNameChanged()ItemクラスのsigNameChangedと同様のシグナル。PrimaryCheck)チェック状態のアイテムが変化したときどれもChoreonoid本体の実装で活用されているシグナルです。いくつかのシグナルについて、利用の指針を紹介します。ItemクラスのsigTreePathChangedは、対象アイテムの上位(親側)のアイテムとの関係が処理内容に影響する場合に、処理の準備や後始末を行うために利用します。これはルートアイテムに接続するタイミングでも送出されるので、その際にアイテムの初期化を行うこともよくあります。一方で対象アイテムの下位(子側)のアイテムとの関係が処理内容に影響する場合は、sigSubTreeChangedによってそれを関係の変化を検知し、処理の準備や後始末を行うために利用します。ItemクラスのsigDisconnectedFromRootは、それ以降そのアイテムはChoreonoid上での操作対象でなくなることを意味するので、アイテムの後始末などに利用します。アイテムがChoreooidのオブジェクトやOSのリソースなどを利用している場合に、それらの解放処理を行います。アイテムの選択状態やチェック状態についても、シグナルによって状態検知を行うことが多いです。ItemクラスのsigSelectionChangedやsigCheckToggledは特定のインスタンスの状態変化を検知するのに使えますし、アイテムツリー全体の中で変化したアイテムを検知したいのであれば、RootItemクラスのsigSelectedItemsChangedやsigCheckToggledを使用します。RootItemのsigSelectedItemsChangedでは選択されているアイテムの一覧がItemListとして与えられます。これについては、このリストを特定のアイテム型用のItemListに渡すことで、そのアイテム型を対象とした選択状態の変化を検知することが可能となります。});とすれば、onSelectedBodyIemsChangedには選択されているBodyItemのみを格納したItemListが渡されます。»プロジェクトアイテムの。