BoneとMode
Copilotに聞きました。Bone は Mode に応じて参照すらできないので Bone を参照するにはまずはモードを確認しておいたほうが良さそうです。
bpy.types.Panel をそのまま継承するのではなく、親クラスを作っておくと便利
親クラスのコードは以下です。
なぜ抽象クラスにしなかったかですが、抽象クラスにしてもも本環境ではインスタンスを作成しようとして失敗します。
なので普通のクラスとして作成して、blenderにregisterしないようにします。とりあえずこれで動いています。
import bpy class RH_PT_Base(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Rig Helper" toggle_panel: str = "" icon_header: str = "BLANK1" icon_preset: str = "BLANK1" @classmethod def poll(cls, context): prop_name = cls.toggle_panel if not prop_name: return True return getattr(context.scene, prop_name, False) def draw_header(self, context): self.layout.label(text="", icon=self.icon_header) def draw_header_preset(self, context): self.layout.label(text="", icon=self.icon_preset) def draw(self, context): pass
呼び出し方は以下。
親クラスでbl_space_type, bl_region_type, bl_category は定義してあるのでこのクラスではいちいち定義しなくて済む。
私は親でアイコンの表示もできるようにしてある。デフォルトでブランクなので必要であれば指定すればよい。
from . import transition, utils, errors, messages, ui_base import bpy class RH_PT_transition(ui_base.RH_PT_Base): bl_idname, bl_label = "RH_PT_transition", "Transition" toggle_panel, icon_header = "toggle_transition", "INTERNET" def draw(self, context): layout = self.layout row = layout.row(align=False) row.operator(RH_OT_weight_mesh.bl_idname, icon="OUTLINER_OB_MESH") row.operator(RH_OT_pose_armature.bl_idname, icon="ARMATURE_DATA") row = layout.row(align=False) row.operator(RH_OT_edit_armature.bl_idname, icon="ARMATURE_DATA") row.operator(RH_OT_edit_mesh.bl_idname, icon="OUTLINER_OB_MESH")
子の中で Bone の順番を入れ替えるには
まず、特定の Bone (Old Bone と呼ぶ)を最後尾の子にするメソッドを作成します。以下の手順です。
1. Bone を再作成する。(New Bone と呼ぶ)
2. Old Bone を削除する
3. New Bone に Old Bone 属性をコピーする。(Bone Constraint 等はコピー不要)
※属性をコピーする時に Old Bone の属性を記憶しておく必要があります。
import bpy def reorder_edit_bone_to_tail(bone_name): obj = bpy.context.object if not obj or obj.type != 'ARMATURE': print("アクティブなアーマチュアが必要です。") return bpy.ops.object.mode_set(mode='EDIT') edit_bones = obj.data.edit_bones old_bone = edit_bones.get(bone_name) if not old_bone: print(f"ボーン '{bone_name}' が見つかりません。") return # 編集可能な属性をすべて保存(読み取り専用は除外) preserved_attrs = {} for prop in old_bone.bl_rna.properties: if prop.is_readonly or prop.identifier in {"rna_type", "name"}: continue try: preserved_attrs[prop.identifier] = getattr(old_bone, prop.identifier) except Exception: pass # 削除して末尾に再作成 edit_bones.remove(old_bone) print(old_bone) new_bone = edit_bones.new(bone_name) for key, value in preserved_attrs.items(): try: setattr(new_bone, key, value) except Exception: pass # 非互換または古いプロパティ(例: layers)を無視 bpy.ops.object.mode_set(mode='OBJECT') print(f"'{bone_name}' を末尾に再作成し、属性も保持しました。") reorder_edit_bone_to_tail("Bone.001")
bpy.types.Operator もそのまま継承するのではなく、親クラスを作っておくと便利
class RH_OT_Base(bpy.types.Operator): bl_options = {"UNDO"} prefix = "rig_helper." def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) if hasattr(cls, "bl_idname") and not cls.bl_idname.startswith(cls.prefix): cls.bl_idname = cls.prefix + cls.bl_idname
context.active_object と context.view_layer.objects.active
context.active_object は読み取り専用
object mode では context.active_object 参照できない。context.view_layer.objects.active を参照する。書き込みもできる。
基本的に同じ参照を指しており(詳細は理解していない)context.active_object を参照する方が良いらしい。
以上より mode != "OBJECT" の時にcontext.active_object を参照すればよい 。
operatorを呼び出すと Info には表示されない
bpy.app.timers.registerを使っても、Infoにはログは出ない。Console logには出る。
そういうもんかと思って本件は諦めた。
getattr(bpy.ops.rig_helper, self.append_method_prop)()
クラス名は Camel Case か Snake Case か
FILE_OT_hello_operator のような命名が多いのですが、AIに聞くと私の思っているのと同じ回答が来たので採用することにします。
今後以下のように命名します。
register 時に bpy.context.scene.prop を参照できないのを解決する
解決方法は、非同期で参照することです。
要はタイマーでいけます。
def register(): def timer(): print(bpy.context.scene.sn_load_interval) bpy.app.timers.register(timer, first_interval=0.1)
このように書かないとエラーが発生する。恐らく初期化が終わってないんじゃないかな。