はじめてのBlenderアドオン開発
Last Update: 2023.3.1
Blender 2.8~3.0
はじめてのBlenderアドオン開発
Blender 2.8~3.0
Last Update: 2023.3.1
3-7. プリファレンスを活用する
プリファレンスには、アドオンごとに提供されるアドオンの設定情報があります。 ユーザはこの設定情報から、アドオンが提供する設定内容を変更できます。 アドオン開発者は、アドオンの機能に割り当てるキーボードのキーやUIのフォントなど、ユーザに設定させたい情報をプリファレンスに配置できます。 プリファレンスを活用することで、Sidebarをはじめとした既存のUIの邪魔にならないように、アドオンの設定を変更するUIを提供できます。
プリファレンス とは、トップバーの [編集] > [プリファレンス...] から開くことのできるウィンドウを指します。 これまでの節では、プリファレンスを使ってBlenderのUIを日本語化したり、アドオンの有効/無効化を行ったりしてきました。 これらに加えて、アドオンの設定情報を配置できる場所が、プリファレンスに存在します。 ユーザは、プリファレンスに配置されたアドオンの設定を行うためのUIから、ユーザの好みに合わせてアドオンの設定を行います。
プリファレンス上にアドオンの設定情報を配置する方法を説明する前に、アドオンの設定情報が配置される場所を知っておく必要があります。 ここでは、筆者が作成したアドオン『Magic UV』を使って確認します。
1 | トップバーの [編集] > [プリファレンス...] を実行して表示される プリファレンス で、[アドオン] タブを選択し、『Magic UV』を有効化した状態で左の矢印をクリックして、アドオンの詳細情報を表示します。 |
2 | [プリファレンス:] と書かれているところが、本節で説明するアドオンの設定情報の配置先になります。 |
これまで、アドオンの設定を変更するUIの配置場所として、Sidebarやオペレータプロパティを紹介しましたが、本節では新たに、プリファレンスと呼ばれる配置先が登場しました。 頻繁に変更しないアドオンの設定情報をプリファレンスに配置することで、SidebarやオペレータプロパティのUIが複雑になることを避けることができます。
プリファレンスに配置する情報として、適切だと考えられるものを次に示します。
bl_info
に記載できなかったアドオンの詳細情報(特に、location
や description
は長文になることが多い)本節のサンプルアドオンでは、上から2番目の用途で、プリファレンスにアドオンの設定を変更するUIを配置します。
本節では、3-5節 で紹介したサンプルアドオンを改造します。
1-5節 を参考にして次のソースコードを入力し、ファイル名 sample_3-7.py
として保存してください。
import datetime
import bpy
import blf
from bpy.props import IntProperty, IntVectorProperty
bl_info = {
"name": "サンプル 3-7: 日時を表示するアドオン③",
"author": "ぬっち(Nutti)",
"version": (3, 0),
"blender": (2, 80, 0),
"location": "3Dビューポート > Sidebar > サンプル 3-7",
"description": "現在の日時を表示するアドオン(プリファレンス利用版)",
"warning": "",
"support": "TESTING",
"doc_url": "",
"tracker_url": "",
"category": "3D View"
}
# リージョン情報の取得
def get_region(context, area_type, region_type):
region = None
area = None
# 指定されたエリアの情報を取得する
for a in context.screen.areas:
if a.type == area_type:
area = a
break
else:
return None
# 指定されたリージョンの情報を取得する
for r in area.regions:
if r.type == region_type:
region = r
break
return region
# 日時を表示するオペレータ
class SAMPLE37_OT_ShowDatetime(bpy.types.Operator):
bl_idname = "object.sample37_show_datetime"
bl_label = "日時を表示"
bl_description = "日時を表示します"
# 描画ハンドラ
__handle = None
@classmethod
def is_running(cls):
# 描画中はTrue
return True if cls.__handle else False
@classmethod
def __handle_add(cls, context):
if not cls.is_running():
# 描画関数の登録
cls.__handle = bpy.types.SpaceView3D.draw_handler_add(
cls.__draw, (context, ), 'WINDOW', 'POST_PIXEL'
)
@classmethod
def __handle_remove(cls, context):
if cls.is_running():
# 描画関数の登録を解除
bpy.types.SpaceView3D.draw_handler_remove(
cls.__handle, 'WINDOW'
)
cls.__handle = None
@classmethod
def __draw(cls, context):
prefs = context.preferences.addons[__name__].preferences
# リージョンの幅を取得するため、描画先のリージョンを得る
region = get_region(context, 'VIEW_3D', 'WINDOW')
# 描画先のリージョンへテキストを描画
if region is not None:
blf.color(0, 1.0, 1.0, 1.0, 1.0)
blf.size(0, prefs.font_size, 72)
blf.position(0, prefs.position[0],
region.height - prefs.position[1], 0)
date_str = datetime.datetime.now().strftime("%Y.%m.%d")
blf.draw(0, date_str)
def invoke(self, context, event):
op_cls = SAMPLE37_OT_ShowDatetime
if context.area.type == 'VIEW_3D':
# [開始] ボタンが押された時の処理
if not op_cls.is_running():
self.__handle_add(context)
print("サンプル 3-7: 日時の表示処理を開始しました。")
# [終了] ボタンが押された時の処理
else:
self.__handle_remove(context)
print("サンプル 3-7: 日時の表示処理を終了しました。")
# エリアを再描画
if context.area:
context.area.tag_redraw()
return {'FINISHED'}
else:
return {'CANCELLED'}
# UI
class SAMPLE37_PT_ShowDatetime(bpy.types.Panel):
bl_label = "日時を表示"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "サンプル 3-7"
bl_context = "objectmode"
def draw(self, context):
op_cls = SAMPLE37_OT_ShowDatetime
layout = self.layout
# [開始] / [停止] ボタンを追加
if not op_cls.is_running():
layout.operator(op_cls.bl_idname, text="開始", icon="PLAY")
else:
layout.operator(op_cls.bl_idname, text="終了", icon="PAUSE")
# プリファレンスのアドオン設定情報
class SAMPLE37_Preferences(bpy.types.AddonPreferences):
bl_idname = __name__
position: IntVectorProperty(
name="位置",
description="描画位置",
size=2,
min=0,
default=(100, 120),
subtype='TRANSLATION'
)
font_size: IntProperty(
name="フォントサイズ",
description="描画フォントサイズ",
min=10,
max=100,
default=30,
)
def draw(self, context):
layout = self.layout
sp = layout.split(factor=0.3)
col = sp.column()
col.prop(self, "position")
sp = sp.split(factor=0.4)
col = sp.column()
col.label(text="フォントサイズ:")
col.prop(self, "font_size", text="")
classes = [
SAMPLE37_OT_ShowDatetime,
SAMPLE37_PT_ShowDatetime,
SAMPLE37_Preferences,
]
def register():
for c in classes:
bpy.utils.register_class(c)
print("サンプル 3-7: アドオン『サンプル 3-7』が有効化されました。")
def unregister():
for c in classes:
bpy.utils.unregister_class(c)
print("サンプル 3-7: アドオン『サンプル 3-7』が無効化されました。")
if __name__ == "__main__":
register()
1-5節 を参考にして作成したアドオンを有効化すると、コンソールウィンドウに文字列が出力されます。
サンプル 3-7: アドオン『サンプル 3-7』が有効化されました。
アドオンを有効化し、アドオンの設定情報が表示されることを確認します。
有効化したアドオンの機能を使い、動作を確認します。
1 | プリファレンスのアドオン設定情報から、フォントサイズや描画位置を変更します。 |
2 | 3-5節 の機能によって描画されるテキストが、1で設定した内容にしたがって変化することを確認します。 |
1-5節 を参考にして有効化したアドオンを無効化すると、コンソールウィンドウに文字列が出力されます。
サンプル 3-7: アドオン『サンプル 3-7』が無効化されました。
本節のサンプルアドオンは、3-5節 のサンプルアドオンを改造したものです。 このため、プリファレンスにアドオンの設定情報を追加し、アドオンの動作に反映させる方法のみを説明します。 その他の処理については、3-5節 を参照してください。
プリファレンスにアドオンの設定情報を追加するためには、bpy.types.AddonPreferences
クラスを継承したクラス(アドオン設定情報クラス と呼ぶことにします)を定義する必要があります。
# プリファレンスのアドオン設定情報
class SAMPLE37_Preferences(bpy.types.AddonPreferences):
bl_idname = __name__
position: IntVectorProperty(
name="位置",
description="描画位置",
size=2,
min=0,
default=(100, 120),
subtype='TRANSLATION'
)
font_size: IntProperty(
name="フォントサイズ",
description="描画フォントサイズ",
min=10,
max=100,
default=30,
)
def draw(self, context):
layout = self.layout
sp = layout.split(factor=0.3)
col = sp.column()
col.prop(self, "position")
sp = sp.split(factor=0.4)
col = sp.column()
col.label(text="フォントサイズ:")
col.prop(self, "font_size", text="")
アドオン設定情報クラスのクラス変数 bl_idname
には、プリファレンスに追加するアドオンの設定情報に対応した、パッケージ名やモジュール名を指定します。 アドオンのソースコードが単一のファイルで構成される場合は、自身のモジュール名を表す __name__
を指定します。 一方、アドオンのソースコードが複数のファイルで構成される場合は、パッケージ名を指定する必要がありますが、指定方法について注意する必要があります。 アドオン設定情報クラスが __init__.py
に定義されている場合は、__name__
がパッケージ名を表すため __name__
を指定すれば問題ありません(__package__
を指定してもよいです)。 しかし、__init__.py
以外に定義されている場合は、パッケージ名を表す __package__
を指定する必要があります。
プリファレンスに追加するアドオンの設定情報は、プロパティクラスの変数としてクラス変数に定義します。 そして、アドオン設定情報クラスの draw
メソッドに、UIを構築するための処理を定義します。 draw
メソッドの書き方は、基本的にパネルクラスの draw
メソッドの書き方と同じです。 しかし、row.prop
メソッドなどのUIを追加する関数の第1引数には、プロパティクラスの変数を定義したインスタンスを指定する必要があることに注意が必要です。 サンプルアドオンでは、draw
メソッドが呼び出されたインスタンスと、プロパティクラスの変数を定義したインスタンスが同じであることから、self
を指定します。
ここまでの処理で、プリファレンスにアドオンの設定情報が追加できます。 続いて、プリファレンスに追加したアドオンの設定情報を、オペレータクラスの処理内で受け取る方法を説明します。
アドオンの設定情報へは、次のコードでアクセスできます。
prefs = context.preferences.addons[__name__].preferences
本節のサンプルアドオンのソースコードは、単一のファイルで構成されます。 このため、context.preferences.addons[__name__].preferences
でアドオンの設定情報にアクセスできます。 アドオンが複数のファイルで構成されている場合は、前述のアドオン設定情報クラスと同様に bl_idname
に __name__
または __package__
を指定して、アドオンの設定情報にアクセスします。
これで、プリファレンスに追加されたアドオンの設定情報にアクセスできるようになりました。 アドオンの設定情報を使ってテキストを描画する処理を、次に示します。
blf.color(0, 1.0, 1.0, 1.0, 1.0)
blf.size(0, prefs.font_size, 72)
blf.position(0, prefs.position[0],
region.height - prefs.position[1], 0)
date_str = datetime.datetime.now().strftime("%Y.%m.%d")
blf.draw(0, date_str)
プリファレンスにアドオンの設定情報を配置し、設定された情報を取得する方法を紹介しました。 プリファレンスを活用することで、SidebarやオペレータプロパティのUIが煩雑になる問題を解消できます。 プリファレンスに配置する設定情報の選択基準を決めるのは難しいところがありますが、設定情報の変更の頻度が1つの基準になるかと思います。
本節までで、BlenderのUIフレームワークの中でUIを構築する方法が、何度か出てきました。 幸いなことに、対象がSidebarであるかプリファレンスであるかに関わらず、基本的には同じような方法でUIを構築できます。 一度覚えたことを他の場面でも活用できるのは、BlenderのUIが一貫した方法で構築されているからです。 なお、UIの構築については、2-7節 にて詳細に説明しているため、参考にしてください。
bpy.types.AddonPreferences
クラスを継承したアドオン設定情報クラスを定義する必要があるdraw
メソッドで定義するcontext.preferences.addons
のキーに指定することで参照できる