diff --git a/dev-note/plans/2026-06-12-milestone.md b/dev-note/plans/2026-06-12-milestone.md index 92c820c..3eb2236 100644 --- a/dev-note/plans/2026-06-12-milestone.md +++ b/dev-note/plans/2026-06-12-milestone.md @@ -116,7 +116,7 @@ _find_unused_min_counter_value と FORCE 関連コメントは §3.5 の 設計判断用に意図的に残置。conftest スタブも 31 行縮小 (_Operator + _Object/_PoseBone/_EditBone/_Node = 結合が減った証明) -- [ ] ID 型ターゲットのパラメータ化(550行 → 60行)— 構造変更のため別 PR。 +- [x] ID 型ターゲットのパラメータ化(550行 → 60行)— 2026-06-13 発注 #3 で完了。 **設計時に併せて検討(発注者の意図、2026-06-13 確認)**: ターゲット登録の 自動化のロマン。旧 `_initialize_defaults`(PR #5 で削除、939c5a9 に履歴)は inspect スキャン+issubclass 方式でリロードに敗北した(ポストモーテムの @@ -125,6 +125,19 @@ 前例あり。リロード時は再 import で自然に再登録され、既存の __name__ 重複排除と 共生する)。ただし import 副作用とテスト隔離のコストがあるため、まず パラメータ化で登録が宣言的テーブルに畳まれた景色を見てから判断する + **実績(2026-06-13)**: 同一クラスタ10ファイル600行 → + `targets/id_datablock.py` 178行(共通基底 `IDDatablockTarget` ~68行 + + 宣言テーブル=属性6個だけの class 文10連)+ material.py 互換シム 11行。 + targets/ 全体で -413行。設計は判定パネル(リロード安全性/可読性の2視点)で + ファクトリ案・globals 注入案と比較のうえ「基底+単一モジュール統合」を採用 + (共通基底がメソッド3つを吸収すると class 文も属性6行になり、ファクトリの + 行数優位が消えて名前文字列の二重化と grep 不能化だけが残るため)。 + 型判定三重化は基底の can_create_from_scope 1箇所に収束(create_from_scope は + 委譲)。object / collection は IDDatablockTarget を継承しつつスコープ判定 + 2メソッドのみ独自実装を保持。レジストリの3インデックスは実機ダンプで + リファクタ前後 byte 同一を確認。**自動登録の景色はこれで見える状態になった** — + 採否判断は id_datablock.py の宣言テーブルと targets/__init__.py の + 明示リストを見比べて行えばよい - [x] position_element.py の register_element 遅延 import 化 (text_element と同方式に統一。要素層→target 層のモジュールレベル結合が解消) - [x] ロガー修復(強制 WARNING 上書きの除去)。実機プローブで diff --git a/src/structured_renamer/targets/__init__.py b/src/structured_renamer/targets/__init__.py index 9036d6e..ab35d66 100644 --- a/src/structured_renamer/targets/__init__.py +++ b/src/structured_renamer/targets/__init__.py @@ -1,32 +1,33 @@ """ Rename Targets - リネーム対象の具体的実装 -Phase 1: 基本的な3Dデータ (Object, Mesh, Material, Armature) -Phase 2: アニメーション/ライティング (Action, Camera, Light, Curve) -Phase 3: アセット管理 (Image, Texture, Collection, Scene) -Special: 特殊なデータタイプ (ShapeKey など) +- ID 型データブロック: id_datablock.py の宣言テーブル + (固有の収集経路を持つ Object / Collection のみ個別ファイル) +- ボーン系 / 特殊データタイプ / エディタ固有: 各ファイル """ from ..core.utils import register_target -# Phase 1: 基本的な3Dデータ -from .object import ObjectRenameTarget -from .bone import BoneRenameTarget, PoseBoneRenameTarget, EditBoneRenameTarget -from .mesh import MeshRenameTarget -from .material import MaterialRenameTarget -from .armature import ArmatureRenameTarget - -# Phase 2: アニメーション/ライティング -from .action import ActionRenameTarget -from .camera import CameraRenameTarget -from .light import LightRenameTarget -from .curve import CurveRenameTarget +# ID 型データブロック (宣言テーブル: id_datablock.py) +from .id_datablock import ( + ActionRenameTarget, + ArmatureRenameTarget, + CameraRenameTarget, + CurveRenameTarget, + ImageRenameTarget, + LightRenameTarget, + MaterialRenameTarget, + MeshRenameTarget, + SceneRenameTarget, + TextureRenameTarget, +) -# Phase 3: アセット管理 -from .image import ImageRenameTarget -from .texture import TextureRenameTarget +# ID 型のうち固有の収集経路を持つもの +from .object import ObjectRenameTarget from .collection import CollectionRenameTarget -from .scene import SceneRenameTarget + +# ボーン系 +from .bone import BoneRenameTarget, PoseBoneRenameTarget, EditBoneRenameTarget # Special: 特殊なデータタイプ (TSE_RNA_STRUCT など) from .shapekey import ShapeKeyRenameTarget @@ -36,7 +37,6 @@ from .strip import StripRenameTarget from .file import FileRenameTarget - # 登録するターゲットクラスのリスト target_classes = [ # Phase 1: 基本的な3Dデータ diff --git a/src/structured_renamer/targets/action.py b/src/structured_renamer/targets/action.py deleted file mode 100644 index 7bb48e7..0000000 --- a/src/structured_renamer/targets/action.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Action リネームターゲット -""" - -from ._base import * - - -class ActionRenameTarget(BaseRenameTarget): - """アクションのリネームターゲット""" - - bl_type = "ACTION" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_AC - namespace_key = "actions" - collection_type = bpy.types.Action - - display_name = "Action" - icon = "ACTION" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {action.name for action in data.actions} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_AC - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_action: Optional[bpy.types.Action] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_AC - ): - target_action = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Action - ) - - if target_action: - return cls(target_action, context) - - return None diff --git a/src/structured_renamer/targets/armature.py b/src/structured_renamer/targets/armature.py deleted file mode 100644 index fc4b86b..0000000 --- a/src/structured_renamer/targets/armature.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Armature リネームターゲット (データブロック) -""" - -from ._base import * - - -class ArmatureRenameTarget(BaseRenameTarget): - """アーマチュアデータブロックのリネームターゲット""" - - bl_type = "ARMATURE" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_AR - namespace_key = "armatures" - collection_type = bpy.types.Armature - - display_name = "Armature" - icon = "ARMATURE_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {arm.name for arm in data.armatures} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_AR - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_armature: Optional[bpy.types.Armature] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_AR - ): - target_armature = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Armature - ) - - if target_armature: - return cls(target_armature, context) - - return None diff --git a/src/structured_renamer/targets/camera.py b/src/structured_renamer/targets/camera.py deleted file mode 100644 index 35b6995..0000000 --- a/src/structured_renamer/targets/camera.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Camera リネームターゲット -""" - -from ._base import * - - -class CameraRenameTarget(BaseRenameTarget): - """カメラデータブロックのリネームターゲット""" - - bl_type = "CAMERA" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_CA - namespace_key = "cameras" - collection_type = bpy.types.Camera - - display_name = "Camera" - icon = "CAMERA_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {cam.name for cam in data.cameras} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_CA - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_camera: Optional[bpy.types.Camera] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_CA - ): - target_camera = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Camera - ) - - if target_camera: - return cls(target_camera, context) - - return None diff --git a/src/structured_renamer/targets/collection.py b/src/structured_renamer/targets/collection.py index 87c2c54..64bd245 100644 --- a/src/structured_renamer/targets/collection.py +++ b/src/structured_renamer/targets/collection.py @@ -7,13 +7,17 @@ """ from ._base import * +from .id_datablock import IDDatablockTarget -class CollectionRenameTarget(BaseRenameTarget): - """コレクションのリネームターゲット""" +class CollectionRenameTarget(IDDatablockTarget): + """コレクションのリネームターゲット + + ID 型の共通機構に加えて View Layer モード (TSE_LAYER_COLLECTION) の + フォールバック経路を持つため、スコープ判定2メソッドは独自実装のまま。 + """ bl_type = "COLLECTION" - ol_type = OT.TSE_SOME_ID ol_idcode = BID.ID_GR namespace_key = "collections" collection_type = bpy.types.Collection @@ -21,12 +25,6 @@ class CollectionRenameTarget(BaseRenameTarget): display_name = "Collection" icon = "OUTLINER_COLLECTION" - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {col.name for col in data.collections} - return set() - @classmethod def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: if scope.mode == CollectionSource.OUTLINER: @@ -48,7 +46,7 @@ def create_from_scope( context: Context, source_item: Any, scope: OperationScope, - pointer_cache: PointerCache, + pointer_cache: Optional[PointerCache] = None, ) -> Optional["IRenameTarget"]: """ソースアイテムからターゲットを作成""" target_collection: Optional[bpy.types.Collection] = None @@ -59,6 +57,7 @@ def create_from_scope( if ( source_item.type == OT.TSE_SOME_ID and source_item.idcode == BID.ID_GR + and pointer_cache is not None ): target_collection = pointer_cache.get_object_by_pointer( source_item.id, bpy.types.Collection diff --git a/src/structured_renamer/targets/curve.py b/src/structured_renamer/targets/curve.py deleted file mode 100644 index c021bf6..0000000 --- a/src/structured_renamer/targets/curve.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Curve リネームターゲット -""" - -from ._base import * - - -class CurveRenameTarget(BaseRenameTarget): - """カーブデータブロックのリネームターゲット""" - - bl_type = "CURVE" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_CU_LEGACY - namespace_key = "curves" - collection_type = bpy.types.Curve - - display_name = "Curve" - icon = "CURVE_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {curve.name for curve in data.curves} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_CU_LEGACY - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_curve: Optional[bpy.types.Curve] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_CU_LEGACY - ): - target_curve = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Curve - ) - - if target_curve: - return cls(target_curve, context) - - return None diff --git a/src/structured_renamer/targets/id_datablock.py b/src/structured_renamer/targets/id_datablock.py new file mode 100644 index 0000000..4a99c23 --- /dev/null +++ b/src/structured_renamer/targets/id_datablock.py @@ -0,0 +1,178 @@ +""" +ID 型データブロックターゲットの共通実装と宣言テーブル + +material / mesh など bpy.data 直下の ID 型データブロックは、識別子 +(bl_type / ol_idcode / namespace_key / collection_type / display_name / icon) +以外まったく同じ手順でリネームできる。共通ロジックは IDDatablockTarget に +1箇所だけ実装し、各タイプは識別子を宣言するだけのサブクラスとして +このファイルに並べる(class 文の並びが宣言テーブル)。 + +新しい ID タイプの追加手順: +1. このファイル末尾に属性6個のサブクラスを1件追加 +2. targets/__init__.py の import と target_classes に1行ずつ追加 + +object / collection も IDDatablockTarget を継承するが、固有の収集経路 +(VIEW3D 選択 / View Layer フォールバック)を持つため各ファイルに残る。 +""" + +from ._base import * + + +class IDDatablockTarget(BaseRenameTarget): + """ID 型データブロックターゲットの共通基底 + + 型判定の情報源はクラス属性 (ol_type / ol_idcode) のみ。 + can_create_from_scope が唯一の判定実装で、create_from_scope は + そこへ委譲する。 + + 前提: namespace_key は bpy.data のコレクション属性名と一致する + (例: "materials" → bpy.data.materials)。 + """ + + ol_type = OT.TSE_SOME_ID + + def create_namespace(self) -> Set[str]: + data = self._context.blend_data + if data: + return {datablock.name for datablock in getattr(data, self.namespace_key)} + return set() + + @classmethod + def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: + if scope.mode == CollectionSource.OUTLINER: + if isinstance(source_item, OutlinerElementInfo): + return ( + source_item.type == cls.ol_type + and source_item.idcode == cls.ol_idcode + ) + return False + + @classmethod + def create_from_scope( + cls, + context: Context, + source_item: Any, + scope: OperationScope, + pointer_cache: Optional[PointerCache] = None, + ) -> Optional["IRenameTarget"]: + """ソースアイテムからターゲットを作成""" + if not cls.can_create_from_scope(source_item, scope) or pointer_cache is None: + return None + + datablock = pointer_cache.get_object_by_pointer( + source_item.id, cls.collection_type + ) + if datablock: + return cls(datablock, context) + + return None + + +class MeshRenameTarget(IDDatablockTarget): + """メッシュデータブロックのリネームターゲット""" + + bl_type = "MESH" + ol_idcode = BID.ID_ME + namespace_key = "meshes" + collection_type = bpy.types.Mesh + display_name = "Mesh" + icon = "MESH_DATA" + + +class MaterialRenameTarget(IDDatablockTarget): + """マテリアルのリネームターゲット""" + + bl_type = "MATERIAL" + ol_idcode = BID.ID_MA + namespace_key = "materials" + collection_type = bpy.types.Material + display_name = "Material" + icon = "MATERIAL" + + +class ArmatureRenameTarget(IDDatablockTarget): + """アーマチュアデータブロックのリネームターゲット""" + + bl_type = "ARMATURE" + ol_idcode = BID.ID_AR + namespace_key = "armatures" + collection_type = bpy.types.Armature + display_name = "Armature" + icon = "ARMATURE_DATA" + + +class ActionRenameTarget(IDDatablockTarget): + """アクションのリネームターゲット""" + + bl_type = "ACTION" + ol_idcode = BID.ID_AC + namespace_key = "actions" + collection_type = bpy.types.Action + display_name = "Action" + icon = "ACTION" + + +class CameraRenameTarget(IDDatablockTarget): + """カメラデータブロックのリネームターゲット""" + + bl_type = "CAMERA" + ol_idcode = BID.ID_CA + namespace_key = "cameras" + collection_type = bpy.types.Camera + display_name = "Camera" + icon = "CAMERA_DATA" + + +class LightRenameTarget(IDDatablockTarget): + """ライトデータブロックのリネームターゲット""" + + bl_type = "LIGHT" + ol_idcode = BID.ID_LA + namespace_key = "lights" + collection_type = bpy.types.Light + display_name = "Light" + icon = "LIGHT_DATA" + + +class CurveRenameTarget(IDDatablockTarget): + """カーブデータブロックのリネームターゲット""" + + bl_type = "CURVE" + ol_idcode = BID.ID_CU_LEGACY + namespace_key = "curves" + collection_type = bpy.types.Curve + display_name = "Curve" + icon = "CURVE_DATA" + + +class ImageRenameTarget(IDDatablockTarget): + """イメージのリネームターゲット""" + + bl_type = "IMAGE" + ol_idcode = BID.ID_IM + namespace_key = "images" + collection_type = bpy.types.Image + display_name = "Image" + icon = "IMAGE_DATA" + + +class TextureRenameTarget(IDDatablockTarget): + """テクスチャのリネームターゲット""" + + bl_type = "TEXTURE" + ol_idcode = BID.ID_TE + namespace_key = "textures" + collection_type = bpy.types.Texture + display_name = "Texture" + icon = "TEXTURE_DATA" + + +class SceneRenameTarget(IDDatablockTarget): + """シーンのリネームターゲット""" + + bl_type = "SCENE" + ol_idcode = BID.ID_SCE + namespace_key = "scenes" + collection_type = bpy.types.Scene + display_name = "Scene" + icon = "SCENE_DATA" diff --git a/src/structured_renamer/targets/image.py b/src/structured_renamer/targets/image.py deleted file mode 100644 index bf1d9ad..0000000 --- a/src/structured_renamer/targets/image.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Image リネームターゲット -""" - -from ._base import * - - -class ImageRenameTarget(BaseRenameTarget): - """イメージのリネームターゲット""" - - bl_type = "IMAGE" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_IM - namespace_key = "images" - collection_type = bpy.types.Image - - display_name = "Image" - icon = "IMAGE_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {img.name for img in data.images} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_IM - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_image: Optional[bpy.types.Image] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_IM - ): - target_image = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Image - ) - - if target_image: - return cls(target_image, context) - - return None diff --git a/src/structured_renamer/targets/light.py b/src/structured_renamer/targets/light.py deleted file mode 100644 index c3a7f19..0000000 --- a/src/structured_renamer/targets/light.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Light リネームターゲット -""" - -from ._base import * - - -class LightRenameTarget(BaseRenameTarget): - """ライトデータブロックのリネームターゲット""" - - bl_type = "LIGHT" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_LA - namespace_key = "lights" - collection_type = bpy.types.Light - - display_name = "Light" - icon = "LIGHT_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {light.name for light in data.lights} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_LA - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_light: Optional[bpy.types.Light] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_LA - ): - target_light = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Light - ) - - if target_light: - return cls(target_light, context) - - return None diff --git a/src/structured_renamer/targets/material.py b/src/structured_renamer/targets/material.py index a37b76f..507af5a 100644 --- a/src/structured_renamer/targets/material.py +++ b/src/structured_renamer/targets/material.py @@ -1,60 +1,11 @@ -""" -Material リネームターゲット -""" - -from ._base import * - - -class MaterialRenameTarget(BaseRenameTarget): - """マテリアルのリネームターゲット""" - - bl_type = "MATERIAL" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_MA - namespace_key = "materials" - collection_type = bpy.types.Material - - display_name = "Material" - icon = "MATERIAL" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {mat.name for mat in data.materials} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_MA - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_material: Optional[bpy.types.Material] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_MA - ): - target_material = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Material - ) - - if target_material: - return cls(target_material, context) - - return None +""" +Material リネームターゲット(互換 re-export) + +実体は id_datablock.py の宣言テーブルへ移動した。このモジュールは +`structured_renamer.targets.material` という import パス +(tests/blender/test_id_targets.py が参照)を維持するためだけに残している。 +""" + +from .id_datablock import MaterialRenameTarget + +__all__ = ["MaterialRenameTarget"] diff --git a/src/structured_renamer/targets/mesh.py b/src/structured_renamer/targets/mesh.py deleted file mode 100644 index b075068..0000000 --- a/src/structured_renamer/targets/mesh.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Mesh リネームターゲット -""" - -from ._base import * - - -class MeshRenameTarget(BaseRenameTarget): - """メッシュデータブロックのリネームターゲット""" - - bl_type = "MESH" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_ME - namespace_key = "meshes" - collection_type = bpy.types.Mesh - - display_name = "Mesh" - icon = "MESH_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {mesh.name for mesh in data.meshes} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_ME - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_mesh: Optional[bpy.types.Mesh] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_ME - ): - target_mesh = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Mesh - ) - - if target_mesh: - return cls(target_mesh, context) - - return None diff --git a/src/structured_renamer/targets/object.py b/src/structured_renamer/targets/object.py index 2206c41..eb133ad 100644 --- a/src/structured_renamer/targets/object.py +++ b/src/structured_renamer/targets/object.py @@ -1,66 +1,65 @@ -""" -Object リネームターゲット -""" - -from ._base import * - - -class ObjectRenameTarget(BaseRenameTarget): - """オブジェクトのリネームターゲット""" - - bl_type = "OBJECT" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_OB - namespace_key = "objects" - collection_type = bpy.types.Object - - display_name = "Object" - icon = "OBJECT_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {obj.name for obj in data.objects} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.VIEW3D: - return isinstance(source_item, bpy.types.Object) - - elif scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_OB - ) - - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_object: Optional[bpy.types.Object] = None - - if scope.mode == CollectionSource.VIEW3D: - target_object = source_item - - elif scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - ): - target_object = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Object - ) - - if target_object: - return cls(target_object, context) - - return None +""" +Object リネームターゲット +""" + +from ._base import * +from .id_datablock import IDDatablockTarget + + +class ObjectRenameTarget(IDDatablockTarget): + """オブジェクトのリネームターゲット + + ID 型の共通機構に加えて VIEW3D の選択オブジェクトからの収集経路を + 持つため、スコープ判定2メソッドは独自実装のまま。 + """ + + bl_type = "OBJECT" + ol_idcode = BID.ID_OB + namespace_key = "objects" + collection_type = bpy.types.Object + + display_name = "Object" + icon = "OBJECT_DATA" + + @classmethod + def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: + if scope.mode == CollectionSource.VIEW3D: + return isinstance(source_item, bpy.types.Object) + + elif scope.mode == CollectionSource.OUTLINER: + if isinstance(source_item, OutlinerElementInfo): + return ( + source_item.type == OT.TSE_SOME_ID + and source_item.idcode == BID.ID_OB + ) + + return False + + @classmethod + def create_from_scope( + cls, + context: Context, + source_item: Any, + scope: OperationScope, + pointer_cache: Optional[PointerCache] = None, + ) -> Optional["IRenameTarget"]: + """ソースアイテムからターゲットを作成""" + target_object: Optional[bpy.types.Object] = None + + if scope.mode == CollectionSource.VIEW3D: + target_object = source_item + + elif scope.mode == CollectionSource.OUTLINER: + if ( + isinstance(source_item, OutlinerElementInfo) + and source_item.type == OT.TSE_SOME_ID + and pointer_cache is not None + ): + target_object = pointer_cache.get_object_by_pointer( + source_item.id, bpy.types.Object + ) + + if target_object: + return cls(target_object, context) + + return None diff --git a/src/structured_renamer/targets/scene.py b/src/structured_renamer/targets/scene.py deleted file mode 100644 index 9226f0e..0000000 --- a/src/structured_renamer/targets/scene.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Scene リネームターゲット -""" - -from ._base import * - - -class SceneRenameTarget(BaseRenameTarget): - """シーンのリネームターゲット""" - - bl_type = "SCENE" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_SCE - namespace_key = "scenes" - collection_type = bpy.types.Scene - - display_name = "Scene" - icon = "SCENE_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {scene.name for scene in data.scenes} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_SCE - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_scene: Optional[bpy.types.Scene] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_SCE - ): - target_scene = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Scene - ) - - if target_scene: - return cls(target_scene, context) - - return None diff --git a/src/structured_renamer/targets/texture.py b/src/structured_renamer/targets/texture.py deleted file mode 100644 index b7d9042..0000000 --- a/src/structured_renamer/targets/texture.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Texture リネームターゲット -""" - -from ._base import * - - -class TextureRenameTarget(BaseRenameTarget): - """テクスチャのリネームターゲット""" - - bl_type = "TEXTURE" - ol_type = OT.TSE_SOME_ID - ol_idcode = BID.ID_TE - namespace_key = "textures" - collection_type = bpy.types.Texture - - display_name = "Texture" - icon = "TEXTURE_DATA" - - def create_namespace(self) -> Set[str]: - data = self._context.blend_data - if data: - return {tex.name for tex in data.textures} - return set() - - @classmethod - def can_create_from_scope(cls, source_item: Any, scope: OperationScope) -> bool: - if scope.mode == CollectionSource.OUTLINER: - if isinstance(source_item, OutlinerElementInfo): - return ( - source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_TE - ) - return False - - @classmethod - def create_from_scope( - cls, - context: Context, - source_item: Any, - scope: OperationScope, - pointer_cache: PointerCache, - ) -> Optional["IRenameTarget"]: - """ソースアイテムからターゲットを作成""" - target_texture: Optional[bpy.types.Texture] = None - - if scope.mode == CollectionSource.OUTLINER: - if ( - isinstance(source_item, OutlinerElementInfo) - and source_item.type == OT.TSE_SOME_ID - and source_item.idcode == BID.ID_TE - ): - target_texture = pointer_cache.get_object_by_pointer( - source_item.id, bpy.types.Texture - ) - - if target_texture: - return cls(target_texture, context) - - return None