【Blender】Enumプロパティの整数値の取得に失敗するとき

投稿者: | 2023-06-07

パネルにドロップダウンを表示して、Enumプロパティの値を変更できるとき、ドロップダウンで一度も選択してない場合に整数値の取得に失敗するときがあります。

KeyError: ‘bpy_struct[key]: key “my_enum_prop” not found’

ChatGPTを使って、エラーにならない初期値の取得コードを作ってみました。

タプルのリストを定義

まず、Enumプロパティの定義に使用するタプルのリストを、専用のスクリプトに定義しました。

enum_items.py

items = [
    ("0_25K", "256px", "", 256),
    ("0_5K", "512px", "", 512),
    ("1K", "1024px", "", 1024),
    ("2K", "2048px", "", 2048),
    ("4K", "4096px", "", 4096)
]

パネルを表示

別のスクリプトを使ってパネルをサイドバーに表示します。パネルには、ドロップダウンと現在選択されている値を表示するラベルがあります。

import bpy
#from .enum_items import items
items = bpy.data.texts["enum_items"].as_module().items

class SimpleToolPanel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_idname = "VIEW3D_PT_simple_tool"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'My Category'

    def draw(self, context):
        layout = self.layout
        layout.label(text="Current Value: " + context.scene.my_enum_prop)
        layout.prop(context.scene, "my_enum_prop")

def register():
    bpy.types.Scene.my_enum_prop = bpy.props.EnumProperty(
        name="Size",
        description="Choose a texture resolution",
        items=items,
        default="1K",
    )
    bpy.utils.register_class(SimpleToolPanel)

def unregister():
    bpy.utils.unregister_class(SimpleToolPanel)
    del bpy.types.Scene.my_enum_prop

register()

テキストエディタにコピペして再生ボタンを押すと、3Dビューポートのサイドバー(N)にパネルが表示されます。ドロップダウンは操作しないでおきます。

ラベルには初期値が表示されています。

registerメソッドの中で、シーンオブジェクトのmy_enum_propを定義しています。型はEnumPropertyで、itemsパラメーターにenum_items.pyで定義したリストを入れています。初期値を「1K」に設定しています。

    bpy.types.Scene.my_enum_prop = bpy.props.EnumProperty(
        name="Size",
        description="Choose a texture resolution",
        items=items,
        default="1K",
    )

パネルのdrawメソッドの中で、UILayout.propメソッドの引数にプロパティの取得元と、プロパティを渡すことで、プロパティの値を変更するためのドロップダウンを表示できます。

    def draw(self, context):
        layout = self.layout
        // ...
        layout.prop(context.scene, "my_enum_prop")

ドロップダウンの値を取得する

さらに、別のスクリプトでドロップダウンの値を取得します。ドット記法とブラケット記法でプロパティにアクセスします。

import bpy
#from .enum_items import items
items = bpy.data.texts["enum_items"].as_module().items


print(bpy.context.scene.my_enum_prop)
print(bpy.context.scene["my_enum_prop"])

これをテキストエディタで実行すると、ブラケット記法だけエラーになります。

bpy.context.scene.my_enum_prop と書くとEnumプロパティの初期値に指定した文字列が表示されます。

Enumプロパティの値が始めて選択されるまでは、シーンプロパティは定義されていて、シーンオブジェクトにはプロパティが存在しない状態のようです。

初期値の文字列(identifier)は取得できるので、別に定義されているタプルのリストを走査して、値を検索すると整数値を取得できました。

import bpy
#from .enum_items import items
items = bpy.data.texts["enum_items"].as_module().items

def print_enum_value():
    enum_key = bpy.context.scene.my_enum_prop
    for item in items:
        if item[0] == enum_key:
            print(item[3])
            break

print_enum_value()

print(bpy.context.scene.my_enum_prop)
print(bpy.context.scene["my_enum_prop"])

取得した文字列を、タプルの0番目と比較して3番目の整数値を表示してます。

ドロップダウンで何か選択した後は、キーを指定して整数値を取得できます。

1024pxを選択
256pxを選択

スクリプト詳細

enum_items.py

Enumプロパティの定義や値の取得に使うタプルは、(identifier, name, description, number) という形式にしています。

items = [
    ("0_25K", "256px", "", 256),
    ("0_5K", "512px", "", 512),
    ("1K", "1024px", "", 1024),
    ("2K", "2048px", "", 2048),
    ("4K", "4096px", "", 4096)
]

はじめの3つの要素は必須です。本来は (identifier, name, description, icon, number) という形式ですが、項目が4つしか無い場合の4番目は number になります。

addon_panel.py

パネルを表示するスクリプトではまずbpyモジュールと、タプルのリストをインポートします。

import bpy
#from .enum_items import items
items = bpy.data.texts["enum_items"].as_module().items

インストールして使うアドオンにする場合は、from .enum_items import items が使えます。今回はテキストデータブロックを取得しています。

カスタムパネルを定義するには、Panelの派生クラスを作り、識別子や表示場所、タブの名前などを定義します。カスタムパネルの作り方

class SimpleToolPanel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_idname = "VIEW3D_PT_simple_tool"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'My Category'

このパネルは、3Dビューポートにサイドバーの「My Category」タブに「My Panel」という名前で表示されます。

パネルの内容はdrawメソッドに実装します。

    def draw(self, context):
        layout = self.layout
        layout.label(text="Current Value: " + context.scene.my_enum_prop)
        layout.prop(context.scene, "my_enum_prop")

UILayout.labelメソッドでtextパラメータに渡された文字列を表示します。ドロップダウンも、UILayout.propにプロパティの取得元とプロパティの識別子を渡すだけで簡単に表示できます。

パネルはbpy.utils.register_classメソッドを使ってBlenderのシステムに登録する必要があります。シーンプロパティも定義しています。

def register():
    bpy.types.Scene.my_enum_prop = bpy.props.EnumProperty(
        name="Size",
        description="Choose a texture resolution",
        items=items,
        default="1K",
    )
    bpy.utils.register_class(SimpleToolPanel)

シーンプロパティを定義するには「bpy.types.Scene.新しいプロパティ」にEnumプロパティ定義を代入します。bpy.props.EnumPropertyメソッドが新しいEnumプロパティの定義を返します。引数に名前やアイテム、初期値などを渡します。

アイテム(items)には他のスクリプトで定義したタプルのリストを渡しています。リストはEnumPropertyと同時に定義できます。

    bpy.types.Scene.my_enum_prop = bpy.props.EnumProperty(
        name="Size",
        description="Choose a texture resolution",
        items=[
        ("0_25K", "256px", "", 256),
        ("0_5K", "512px", "", 512),
        ("1K", "1024px", "", 1024),
        ("2K", "2048px", "", 2048),
        ("4K", "4096px", "", 4096)],
        default="1K",
    )

登録解除するメソッドも作りました。そして、テキストエディタの再生ボタンを押すとregisterメソッドを呼びます。

def unregister():
    bpy.utils.unregister_class(SimpleToolPanel)
    del bpy.types.Scene.my_enum_prop

register()

print_enum.py

Enumプロパティの値を取得するスクリプトでも、他のスクリプトで定義したタプルのリストを持っておきます。

import bpy
#from .enum_items import items
items = bpy.data.texts["enum_items"].as_module().items

bpy.context.scene.my_enum_prop でEnumプロパティの初期値や選択中の項目の identifier を得られるので、タプルのリストの項目を一つずつしらべて、1番目の項目と等しい場合は、3番目に定義した整数値を表示します。

def print_enum_value():
    enum_key = bpy.context.scene.my_enum_prop
    for item in items:
        if item[0] == enum_key:
            print(item[3])
            break

ドロップダウンが選択されている場合は、ブラケット記法でも整数値がわかります。

print(bpy.context.scene.my_enum_prop)
print(bpy.context.scene["my_enum_prop"])

プロパティの存在を確認

シーンオブジェクトにプロパティがあるかどうかを確認してから表示すると、エラーになりません。

import bpy

print("my_enum_prop" in bpy.context.scene)

if("my_enum_prop" in bpy.context.scene):
    print(bpy.context.scene["my_enum_prop"])
ある場合
ない場合
参考:
https://docs.blender.org/api/current/bpy.types.Scene.html#bpy.types.Scene
https://docs.blender.org/api/current/bpy.props.html#bpy.props.EnumProperty
https://docs.blender.org/api/current/bpy.types.UILayout.html#bpy.types.UILayout.prop

コメントを残す

メールアドレスが公開されることはありません。