新ChoregrapheではHTMLのパス問題を解決する「おまじない」は必要ない

Choregraphe 2.5.5でPepperプログラミング（以下Pepper本）を試そうとすると、いくつかChoregrapheの動作が書籍と異なる点が出てきて戸惑う部分が多いかもしれません。

Pepper本では、胸のディスプレイで画像や動画を表示する際に、ある「おまじない」[^1]が必要であると書かれていました[^2]。
しかし、2.5.5では「おまじない」は必要なくなりました[^3]。
この記事では、本当に「おまじない」は必要ないのか検証したあと、なぜ必要なくなったかを見ていきます。

# 「おまじない」とは

Pepper本3.3.2「タブレットに画像を表示する」のステップ6では、以下のように書かれています。

> 最後に、おまじないが必要です。Show Imageボックスを動作させるためには、ビヘイビアの位置を変更しなければなりません。ビヘイビアは、プロジェクトの新規作成後は**behavior_1/behavior.xar**というファイルとして保存されています。このbehavior.xarを、behavior_1フォルダの外、プロジェクトファイルの最上位階層（ルート）のファイルとなるように、ドラッグ＆ドロップで移動します。図3.3.4のようになっていればOKです。
> ― *"Pepperプログラミング 基本動作からアプリの企画・演出まで" P66 より*

図3.3.4は下図のような図でした。

![image](https://qiita-image-store.s3.amazonaws.com/0/173797/992625d3-f479-87e7-d7ab-3340e407190d.png)

「おまじない」とは、behavior.xarをルートディレクトリに移動することだったわけです。

# 2.5.5で「おまじない」をせずにディスプレイに画像を表示してみる

2.5.5でも「おまじない」は必要なのでしょうか。
試しに、Pepper本を参考に、2.5.5でディスプレイに1枚の画像を表示するだけのプログラムを作って動かしてみます。

1. まず、Multimedia→Tablet→Show Imageボックスをビヘイビアに配置し、下図のように接続します。
    ![Show Imageボックスの配置と接続](https://qiita-image-store.s3.amazonaws.com/0/173797/cff7d0ee-435b-90ae-5b1f-45481e98f7f0.png)

2. ルート直下に `html` ディレクトリを作成し、その下に適当な画像ファイル `image.png` を配置します。
    ![プロジェクトファイルの構造](https://qiita-image-store.s3.amazonaws.com/0/173797/4071817f-485b-0541-5e54-3b4a0c26f15d.png)

3. 配置したShow Imageボックスの設定ダイアログを開き、ImageUrl変数に `image.png` を指定します。
    ![Show Imageボックスの設定ダイアログ](https://qiita-image-store.s3.amazonaws.com/0/173797/f69d27e6-cf5d-b22c-a901-468ff7e144fe.png)

4. アップロードして再生ボタンをクリックし、実行します。
5. Pepperのディスプレイに指定した画像が表示されたら成功です。
    ![実行時のディスプレイ](https://qiita-image-store.s3.amazonaws.com/0/173797/e0615547-ddb8-2bc6-4916-934a5880b36d.png)

正常に動作しました。
ちなみに、「おまじない」をしてもうまく動きます。

# なぜ「おまじない」が必要なくなったのか

なぜ2.5.5では「おまじない」が必要ないのでしょうか。
その答えは少しあとにして、まずは「おまじない」の背景を詳しく見ていきましょう。

## 旧バージョンで「おまじない」が必要だった理由

「おまじない」の背景を理解するためには、Pepper本の想定環境であるChoregraphe2.3のShow Imageボックス、Play Videoボックス、Show AppボックスのPythonスクリプトをみていく必要があります。

これらのボックスでは、_getAbsoluteUrlメソッドおよび_getAppNameメソッドとして、それぞれ以下のようなコードが定義されていました。

```py:
def _getAppName(self):
    import os
    if self.frameManager:
        behaviorPath = os.path.normpath(self.frameManager.getBehaviorPath(self.behaviorId))
        appsFolderFragment = os.path.join("PackageManager", "apps")
        if not (appsFolderFragment in behaviorPath):
            self.logger.error("appsFolderFragment is not in behaviorPath")
        fragment = behaviorPath.split(appsFolderFragment, 1)[1]
        return fragment.lstrip("\\/")
    else:
        self.logger.warning("No ALFrameManager")

def _getAbsoluteUrl(self, partial_url):
    import os
    subPath = os.path.join(self._getAppName(), os.path.normpath(partial_url).lstrip("\\/"))
    # We create TabletService here in order to avoid
    # problems with connections and disconnections of the tablet during the life of the application
    return "http://%s/apps/%s" %(self._getTabletService().robotIp(), subPath.replace(os.path.sep, "/"))
```

_getAbsoluteUrlメソッドは、ディスプレイに対して、「このURLからページをダウンロードして表示して」と指示するためのURLを作成するメソッドです。

少しややこしい話ですが、 [PepperタブレットにおけるJavaScript活用コンテンツ(QiMessaging JavaScript)#タブレットでの画像や動画の表示](http://qiita.com/Atelier-Akihabara/items/716e88b306db12751c9b#%E3%82%BF%E3%83%96%E3%83%AC%E3%83%83%E3%83%88%E3%81%A7%E3%81%AE%E7%94%BB%E5%83%8F%E3%82%84%E5%8B%95%E7%94%BB%E3%81%AE%E8%A1%A8%E7%A4%BA)で示されているように、Pepperとディスプレイはそれぞれ独立したシステムとして動いていて、ディスプレイからはPepperはあたかもWebサーバーであるかのように見えています。
そこで指示すべきURLのベースは、 `http://robotのIPアドレス/apps/アプリケーション名/` という形になるようで、この`アプリケーション名`の部分を_getAppNameメソッドで得ています。

この_getAppNameメソッド内では、`self.frameManager.getBehaviorPath(self.behaviorId)` を実行することで、ビヘイビアの(ファイルシステム内の)パスを取得して、そのパスを分解して(パス中に定義されているはずの)アプリケーション名を得ています。
実は、このロジックが、 behavior.xarがプロジェクト中のルートディレクトリに存在することを前提に書かれてしまっていたので、「おまじない」をするよう記載していたわけです。

## 2.5.5 で「おまじない」が必要ない理由

では、2.5のボックスではどのようにこのコードが変化したのか、見てみましょう。
_getAppNameメソッドはなくなり、_getAbsoluteUrlメソッドのみになっています。

```py
def _getAbsoluteUrl(self, partial_url):
    import os
    subPath = os.path.join(self.packageUid(), os.path.normpath(partial_url).lstrip("\\/"))
    # We create TabletService here in order to avoid
    # problems with connections and disconnections of the tablet during the life of the application
    return "http://%s/apps/%s" %(self._getTabletService().robotIp(), subPath.replace(os.path.sep, "/"))
```

2.3と見比べてみると非常にシンプルになっていることがわかります。
`self.packageUid()`は、そのボックスが所属するアプリケーションの識別名を返すメソッドで、Pythonボックスに標準で用意されているものです。

つまり、今までは、ビヘイビアのパスしか取得できなかったために、ボックス内でアプリケーションの名前を切り出す処理を自前で実装する必要があったのが、2.5からはボックスの機能としてアプリケーションの名前を得られるようなり、その機能はビヘイビアの位置に依存せず正しい名前を返すようになったため、「おまじない」が必要なくなったということのようです。

# まとめ

今回見てきたディスプレイ系ボックスのように、Choregrapheのボックスライブラリの中身もバージョンにより更新されており、バグが解消している場合もあります。
ただし、今回の例のように、Choregrapheが(NAOqiが)標準で用意するようになったメソッドを使用するように変更されているケースもあるため、NAOqi 2.3以前で動かす想定のアプリをChoregraphe 2.5で作る、などとしてしまうと、ボックスが前提とする機能のサポートが正常に行われず、想定外のエラーに遭遇してしまう、ということも考えられます。

Choregrapheの各ボックスの詳細な変更履歴はリリースノートでは提供されていないため([新しいSayボックスと発話のカスタマイズ](http://qiita.com/vr-lab/items/8f5e6b305118535ee570)のような大きな変更は別ですが)、常に追うのは難しい部分もありますが、たまに変更点を追いかけておくと思いもがけないところではまることを防げるかもしれません。

[^1]: または_getAppNameメソッドの修正（ただしこのメソッドは2.5.5のスクリプトでは定義されていません）。
[^2]: Pepper本P66、P69、P139など。
[^3]: 厳密には、2.3までは「おまじない」が必要でしたが、2.4から必要なくなりました。