MRが楽しい

MRやVRについて学習したことを書き残す

Blender 2.8のPython APIドキュメントを少しずつ読み解く 落とし穴 その7

本日は Blender2.8 の調査枠です。
Blender 2.8 の Python API ドキュメントを少しずつ読みつつ試していきます。
前回記事の続きです。
bluebirdofoz.hatenablog.com

Blender 2.8 Python API Documentation

以下のページを日本語訳しつつ実際に試して記事を進めていきます。
docs.blender.org
docs.blender.org

今日は「落とし穴」の「相対ファイルパス」と「Unicodeの問題」です。
f:id:bluebirdofoz:20200117114924j:plain

相対ファイルパス

Blenderの相対ファイルパスは、sys や os などの標準Pythonモジュールと互換性がありません。
組み込みのPython関数は、blend ファイルのパスを示す "//" プレフィックスを認識しません。

この問題が発生する一般的なケースは、画像パスが関連付けられたマテリアルをエクスポートする場合です。

>>> bpy.path.abspath(image.filepath)

f:id:bluebirdofoz:20200117114938j:plain

特にリンクされたライブラリからブレンダーデータを使用する場合、複雑です。
パスは開いている blend ファイルではなくライブラリを基準とするためです。

データブロックが外部 blend ファイルのものである場合は、bpy.types.ID からライブラリ引数を渡します。

>>> bpy.path.abspath(image.filepath, library=image.library)

f:id:bluebirdofoz:20200117114949j:plain

これらはネイティブPythonモジュールで使用できる絶対パスを返します。

Unicodeの問題

Pythonは多くの異なるエンコーディングをサポートしています。
latin1 または iso-8859-15 でスクリプトを記述出来ます。

PEP 263 を参照してください。
www.python.org

ただし、blend ファイルには明示的なエンコーディングがありません。
これは BlenderPython API の問題を複雑にします。

Python統合およびスクリプト作成者の問題を回避するために、blend ファイル内のすべての文字列に UTF-8, ASCII の互換性が必要と判断しました。
これは、例えば異なるエンコーディングの文字列をオブジェクト名に割り当てるとエラーが発生することを意味します。

ユーザーファイルシステム上の非 UTF-8 パスの存在を無視できないため、パスはこのルールの例外です。
これは、一見すると問題のないコードでもエラーが発生する可能性があることを意味します。

>>> print(bpy.data.filepath)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 10-21: ordinal not in range(128)
>>> bpy.context.object.name = bpy.data.filepath
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
TypeError: bpy_struct: item.attr= val: Object.name expected a string type, not str

f:id:bluebirdofoz:20200117115010j:plain

ファイルシステムエンコードの問題を回避する2つの記述方法を次に示します。

>>> print(repr(bpy.data.filepath))
>>> import os
>>> filepath_bytes = os.fsencode(bpy.data.filepath)
>>> filepath_utf8 = filepath_bytes.decode('utf-8', "replace")
>>> bpy.context.object.name = filepath_utf8

f:id:bluebirdofoz:20200117115030j:plain

Unicodeエンコーディング/デコーディングは包括的なPythonドキュメントで大きなトピックになっています。

・常にutf-8エンコードを使用するか、入力が不明な場合はutf-8に変換します。
・ファイルパスを文字列として直接操作することは避け、代わりに os.path 関数を使用してください。
・パスを操作するときは、組み込みの関数の代わりに os.fsencode() または os.fsdecode()を使用します。
・パスを表示するか、UI に含めるに 、repr(path を)最初に使用するか、"%r" % path フォーマットで使用します。

注意

Pythonの文字列の代わりにバイトを使用して文字列エンコードの問題を回避する方が望ましい場合があります。
一部の入力を読み取るとき、バイナリデータとして読み取るのは簡単です。
しかし、Blende で使用する文字列をどのように扱うかを決定する必要があります。