Closed2

Developer wiki > Code Doc. > context の訳のような何か

nikogolinikogoli

Context

context をより明示的にするため、Blender 2.5 で bContext 構造体が追加された。適切な context の中では、オペレーター/python スクリプト/描画処理のコードを常に機能させることができる。

context は bContext構造体として表現され、関連する APIは BKE_context.h で定義されている。

  • context の要素の呼び出し
    ほとんどの場合、bContext を引数として関数 CTX_data_*CTX_wm_ *を呼び出す。
  • context の要素の設定
    要素が呼ばれたとき、screen や area、region で定義されたコールバック関数によって設定される。

What's in the context?


コードの種類に応じた、アクセスする context の持ち主

User Preferences

全てのコードは User Preferences が設定する context の要素にアクセスする。
このデータは Blender のどんな状態でも存在し、本来の意味の"context"ではない。

Main

全てのコードは Main が設定する context の要素にアクセスする。
これは blendファイルであり、 それ自身のデータブロックと、他の blend ファイルからリンクされているデータブロックを含んでいる。

Window Manager

(元ページの図ではわかりにくいが、実際には"window manager"というデータの持ち主がいるわけではなく、wm.screen, wm.area, wm.regionの3つに分離して存在している、はず)

  • Window Manager の context にアクセスするコード:drawing と operator のコード
    これらのコードは window manager が設定する context の要素に基づいて処理を行う。window manager による実際の context の設定は、screen、area、 region のうちのそれぞれの階層において行われている。この3つの階層は固有のデータ、例えば screen はアクティブな Scene のデータ、area や region は何らかの Data を持っており、それぞれが自分の持つデータを context に追加する。

  • Window Manager の context にアクセスしないコード:Evaluation と レンダリング のコード
    window manager が設定する context にアクセスしないコードを書くこともできる。これらのコードは、window の設定のないバックグラウンドでの起動時でも実行することが可能で、例えばレンダリングのために利用できる。レンダリングやモディファイアやコンストレイントなどの Evaluation では、scene やその他の data が必要になるが、上記のようなバックグラウンドでの実行を想定する場合は、 context にアクセスする以外の方法でこれらのデータを取得するべきである。

その他

ファイルの読み取りと書き込み、カーネルの様々な機能、window manager のコードがある。一般的に、これらの処理は context の極一部しか必要としない、あるいは context を全く必要としない。

Setting Context

context は永続的ではなく、呼び出しに応じて作成されるもので、operator や drawing の処理が実行された時点での「一時的で局所的な状態」である。

アクティブオブジェクトを変更する場合を考えてみる

  1. context.active_objectを変更することができても、アクティブオブジェクトは変更されない
  2. ViewLayer.objects.activeを変更することで、アクティブオブジェクトは変更される
    → その後でcontext.active_objectを呼ぶと、新しいアクティブオブジェクトが取得できる

つまり、context とは、

  • ユーザーがあちこちにアクセスする手間を省くために、実際にデータを管理している部分から必要な情報を収集し、ひとまとめにして提供するもの
  • 予め作成しておいて随時更新するのではなく、必要になったときに情報を集めて作成される

Callback

context の多くの要素は、screen, space, region が持つコールバック関数xx_context()で設定される。

画像/UVエディタ(Space)が持つコールバック関数
static int image_context(const bContext *C, const char *member, bContextDataResult *result)

この関数の3つの引数は、以下のように利用される(はず)

  • C:大元のデータの持ち主(この場合は SpaceImageEditor)を取得するために利用
  • member:context に追加することが要求されている要素(メンバ)の名前
  • result:コールバック関数の様々な処理の結果を格納するもの

コールバック関数における処理は、

  1. 要求されている context の要素(member)が、その関数が設定を担当する要素であるか確認する
  2. 担当する要素であった場合は、RNA ポインタあるいはコレクションをresultに代入する
コードともう少し詳しめの説明 (正確性は全く保証しない)
static int image_context(const bContext *C, const char *member, bContextDataResult *result)
{
  SpaceImage *sima= CTX_wm_space_image(C);

  if(CTX_data_dir(member)) {
      static const char *dir[] = {"edit_image", NULL};
      CTX_data_dir_set(result, dir);
  }
  else if(CTX_data_equals(member, "edit_image")) {
      CTX_data_id_pointer_set(result, (ID*)ED_space_image(sima));
      return 1;
  }

  return 0;
}

ポインタ/実体の違い は、ここでは無視

  1. CTX_wm_space_image によって、C から SpaceImageEditor のデータを取得
  2. CTX_data_dirによって memberが空文字列がどうか判定
    • 空文字列ならば、result.dir{"edit_image", NULL}を代入し、処理を終える
      (→ C.edit_image = None)
  3. 空文字列でないならば、CTX_data_equalsによって、member"edit_image"かどうかを判定
  4. True ならば、ED_space_imageで Space から Imageのデータを取得し、CTX_data_id_pointer_setでそのRNAポインタを result.ptrに代入し、処理を終える
    (→ C.edit_image = SpaceImageEditor で開いている画像)
  5. どの条件にも合致しない、つまりmemberが画像エディタとは無関係な文字列のときは、なにもせず終了
    (→ C.edit_image自体が作成されない)

コメント:memberが空文字列 → C.edit_image = Noneは直感的でない
そもそも空文字列のmemberを渡すときってどんなとき? 要確認



UILayout

UIレイアウトでは、bpy.types.UILayout.set_context_pointerを利用してユーザーが context の要素を設定することで、特定のデータへのアクセス手段を確保することができる。

特定のモディファイアを context に追加する
def draw(self, context):
	box = self.layout.box()
	box.set_context_pointer("modifier", 特定のモディファイア)
	box.operator(何かのオペレータ)

上記のコードを実行すると、このボックス内で設定されるオペレータでは、context.modifierを使うことでこのモディファイアのデータにアクセスすることができる。

Lookups


context を設定する担当者の間で、順番に context の要素が検索される

operator や drawing の処理で context の要素xxが要求されたとき、以下の順で確認が行われる。

  1. UIレイアウトの context の要素にxxが含まれる?
  2. xxは、region のコールバック関数が設定する context の要素?
  3. xxは、area のコールバック関数が設定する context の要素?
  4. xxは、screen のコールバック関数が設定する context の要素?

  • xxを設定する担当者が存在すれば、そこでxxが設定され結果が返される。
  • どの部分でもxxが設定されなければ、NULLポインタあるいは空のコレクションが返される。

したがって、もし異なる階層で同じ名前の context の要素が設定される場合、より下の階層の設定するデータが返り値となる。

コメントとか

UIレイアウトの context ってなに? Buttons のこと?



Getting Context

Window Manager

window manager に関する context の要素は、screen, are, space data, region/region data への単なるポインタである。
これらは data に関する context を設定する起点となるので、コードでは、これらの要素が context 内に実際に存在することを確認する必要がある。

  • drawing のコード:処理が呼ばれたときにこれらが存在することは明白で、確認する必要はない
  • operator のコード:poll()関数の中や処理の実行時の最初の時点で確認する必要がある。
    この確認を怠ると、ユーザーが独自のキーマップを使い想定されていない場所で operator を呼び出した場合、Blender がクラッシュする可能性がある。

Data

Data に関する context の要素はRNAポインタとコレクションである。

C では、context からデータを取得するために呼び出す関数として、2種類が存在する。

  1. 要素に対応して定義されたアクセサ関数:
    CTX_data_edit_objectのようなもの
  2. 文字列を用いて context のメンバを検索する方法:
    CTX_data_pointer_get_type(C, "edit_object", &RNA_Object).dataのようなもの
    (CTX_data_pointer_get_typeの返り値は PointerRNA なので、.dataを使ってデータにアクセスする)

2 の方法は、得られたデータの型が適正であることは保証されない。そのため、必要な型の情報(例では&RNA_Object)も指定することが望ましい。

Where to Look

context の要素を調べるには、それを設定するコールバック関数を確認するのが最善である。例えば、画像ウィンドウに関しては、 space_image.c で定義されているコールバック関数を調べれば良い。

Pythonでは、print(dir(context))を実行することで、その時点の context の内容を確認できる。

Always Available

ファイル読み込み処理のコードを除けば、コードの中では以下の context の要素が常に存在すると考えて良い。つまり、これらは operator の poll()関数で存在を確認しなくて良いが、これら以外の全ての要素は確認の対象になりうる。

CTX_wm_manager CTX_wm_window CTX_wm_screen
CTX_data_main CTX_data_scene CTX_data_tool_settings

API

より詳しい情報が必要な場合は、以下のファイルを確認する。

このスクラップは2021/03/06にクローズされました