
# ========================================================================
# Blender 5.0+ Cube Generator (with Links, Detach & Remove)
# 立方体生成パネル + 切離し機能 + コピー機能 + リンク&削除パネル
# ========================================================================
bl_info = {
"name": "Cube Generator & Links",
"author": "zionad",
"version": (1, 1, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar > Cube Gen",
"description": "立方体の生成・独立化、初期値コードのコピー、リンク・削除パネル",
"category": "Add Mesh",
}
import bpy
import bmesh
import webbrowser
from bpy.app.handlers import persistent
# ========================================================================
# 1. 初期パラメータ(ここをコピーしたコードで書き換え可能)
# ========================================================================
INIT_LOC_X = 0.00
INIT_LOC_Y = 0.00
INIT_LOC_Z = 0.00
INIT_SIZE_X = 2.00
INIT_SIZE_Y = 2.00
INIT_SIZE_Z = 2.00
INIT_COLOR = (0.10, 0.50, 0.80)
INIT_ALPHA = 1.00
# ========================================================================
# 2. Links データ
# ========================================================================
ADDON_LINKS = {
"20261005 円柱作成": "<https://app.notion.com/p/20261005-3ef691cbf906807d9521eeb3fecabcf6>",
"20261003 円錐頂点矢印 と 球体_002": "<https://app.notion.com/p/20261003-3ed691cbf90680a187f7d9c5ea268c2b>",
"338 Sphere Cam": "<https://app.notion.com/p/338-Sphere-Cam-3c0347b3454a80abbb61ff2cd585792c>",
"posfie": "<https://posfie.com/@timekagura/t/zionad2022?sort=0>",
"zionadchat": "<https://x.com/zionadchat>",
"背後霊カメラ 最新版リンク": "<https://app.notion.com/p/Cam-blender-39f347b3454a806490a6f0107c2347dd>",
"blender コンテナ 20260713": "<https://note.com/zionadmillion/n/n9a47d88fe7b4>",
"骨格立方体": "<https://app.notion.com/p/351-2017-3cb347b3454a8086b15fec2f69408bd5>",
}
_IS_UPDATING = False
_TIMER_RETRY_COUNT = 0
MAX_TIMER_RETRIES = 30 # 0.1秒 × 最大30回 (最大3秒でタイムアウト終了)
# ========================================================================
# 3. 処理ロジック (マテリアル & 立方体生成 & ビュー設定)
# ========================================================================
def apply_viewport_settings():
"""3Dビューポートのシェーディングをマテリアルプレビューにし、サイドバーを開く (タイマー実行)"""
global _TIMER_RETRY_COUNT
_TIMER_RETRY_COUNT += 1
# ウィンドウマネージャーやウィンドウが未初期化の場合は再試行
wm = getattr(bpy.context, "window_manager", None)
if not wm or not getattr(wm, "windows", None):
if _TIMER_RETRY_COUNT >= MAX_TIMER_RETRIES:
return None
return 0.1
found_view3d = False
for window in wm.windows:
screen = getattr(window, "screen", None)
if not screen:
continue
for area in screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D':
# 1. マテリアルプレビュー表示に設定
space.shading.type = 'MATERIAL'
# 2. サイドバー(Nパネル)を開く
space.show_region_ui = True
found_view3d = True
# サイドバーのアクティブタブを「Cube Gen」にフォーカス
for region in area.regions:
if region.type == 'UI' and hasattr(region, "active_panel_category"):
region.active_panel_category = 'Cube Gen'
# 3Dビューポートが見つかり設定が完了した場合、または上限回数に達した場合はタイマー終了
if found_view3d or _TIMER_RETRY_COUNT >= MAX_TIMER_RETRIES:
return None
return 0.1
def schedule_viewport_settings_switch():
"""タイマー重複登録を防止しながらビュー設定切り替えを予約実行"""
global _TIMER_RETRY_COUNT
_TIMER_RETRY_COUNT = 0
if not bpy.app.timers.is_registered(apply_viewport_settings):
bpy.app.timers.register(apply_viewport_settings, first_interval=0.1)
@persistent
def on_blend_load_post(dummy):
"""ファイル読み込み完了時にビュー設定切り替えを予約"""
schedule_viewport_settings_switch()
def setup_material(mat, color, alpha):
mat.use_nodes = True
tree = mat.node_tree
tree.nodes.clear()
out_node = tree.nodes.new(type="ShaderNodeOutputMaterial")
out_node.location = (400, 0)
bsdf = tree.nodes.new(type="ShaderNodeBsdfPrincipled")
bsdf.location = (0, 0)
if "Base Color" in bsdf.inputs:
bsdf.inputs["Base Color"].default_value = (*color[:3], 1.0)
if "Alpha" in bsdf.inputs:
bsdf.inputs["Alpha"].default_value = alpha
tree.links.new(bsdf.outputs["BSDF"], out_node.inputs["Surface"])
mat.blend_method = 'BLEND'
mat.diffuse_color = (*color[:3], alpha)
def update_cube(self, context):
global _IS_UPDATING
if _IS_UPDATING:
return
_IS_UPDATING = True
try:
props = context.scene.cube_gen_props
obj_name = "GeneratedCube"
mat_name = "GeneratedCube_Mat"
# オブジェクトの取得または新規作成
obj = bpy.data.objects.get(obj_name)
if not obj:
mesh = bpy.data.meshes.new(f"{obj_name}_Mesh")
obj = bpy.data.objects.new(obj_name, mesh)
try:
context.scene.collection.objects.link(obj)
except RuntimeError:
pass
# メッシュの生成 (BMesh)
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=1.0)
# 大きさの適用
bmesh.ops.scale(bm, vec=(max(0.001, props.size_x),
max(0.001, props.size_y),
max(0.001, props.size_z)), verts=bm.verts)
bm.to_mesh(obj.data)
bm.free()
obj.data.update()
# 中心位置の適用
obj.location = (props.loc_x, props.loc_y, props.loc_z)
# マテリアル設定
mat = bpy.data.materials.get(mat_name)
if not mat:
mat = bpy.data.materials.new(mat_name)
setup_material(mat, props.color, props.alpha)
if not obj.data.materials:
obj.data.materials.append(mat)
else:
obj.data.materials[0] = mat
finally:
_IS_UPDATING = False
# ========================================================================
# 4. Property Group
# ========================================================================
class CubeGenProperties(bpy.types.PropertyGroup):
loc_x: bpy.props.FloatProperty(name="X", default=INIT_LOC_X, update=update_cube)
loc_y: bpy.props.FloatProperty(name="Y", default=INIT_LOC_Y, update=update_cube)
loc_z: bpy.props.FloatProperty(name="Z", default=INIT_LOC_Z, update=update_cube)
size_x: bpy.props.FloatProperty(name="X", default=INIT_SIZE_X, min=0.001, update=update_cube)
size_y: bpy.props.FloatProperty(name="Y", default=INIT_SIZE_Y, min=0.001, update=update_cube)
size_z: bpy.props.FloatProperty(name="Z", default=INIT_SIZE_Z, min=0.001, update=update_cube)
color: bpy.props.FloatVectorProperty(
name="色", subtype='COLOR', default=INIT_COLOR,
min=0.0, max=1.0, size=3, update=update_cube)
alpha: bpy.props.FloatProperty(
name="透明度", default=INIT_ALPHA, min=0.0, max=1.0,
update=update_cube)
# ========================================================================
# 5. Operators
# ========================================================================
class CUBE_GEN_OT_create(bpy.types.Operator):
bl_idname = "cube_gen.create"
bl_label = "作成 / 更新"
bl_description = "立方体を生成・更新します"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
update_cube(None, context)
self.report({'INFO'}, "立方体を生成/更新しました")
return {'FINISHED'}
class CUBE_GEN_OT_detach(bpy.types.Operator):
bl_idname = "cube_gen.detach"
bl_label = "完成Objectとして切離し"
bl_description = "現在の生成Objectを完成品としてアドオン制御から切り離し、マテリアルも完全独立化します"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
obj_name = "GeneratedCube"
obj = bpy.data.objects.get(obj_name)
if not obj:
self.report({'WARNING'}, "生成Objectがありません")
return {'CANCELLED'}
suffix = "_Fixed"
# 1. オブジェクトとメッシュのリネーム(アドオンのターゲットから外す)
obj.name = obj.name + suffix
if obj.data:
obj.data.name = obj.data.name + suffix
# 2. マテリアルを複製して独立させる
for slot in obj.material_slots:
if slot.material:
new_mat = slot.material.copy()
new_mat.name = slot.material.name + suffix
slot.material = new_mat
self.report({'INFO'}, "完成Objectとして切離しました。マテリアルも独立しました。")
return {'FINISHED'}
class CUBE_GEN_OT_copy_code(bpy.types.Operator):
bl_idname = "cube_gen.copy_code"
bl_label = "設定をコード形式でコピー"
bl_description = "現在の数値をPythonの初期値コードとしてクリップボードへコピーします"
def execute(self, context):
props = context.scene.cube_gen_props
lines = [
"# ========================================================================",
"# 1. 初期パラメータ(ここをコピーしたコードで書き換え可能)",
"# ========================================================================",
"",
f"INIT_LOC_X = {props.loc_x:.2f}",
f"INIT_LOC_Y = {props.loc_y:.2f}",
f"INIT_LOC_Z = {props.loc_z:.2f}",
"",
f"INIT_SIZE_X = {props.size_x:.2f}",
f"INIT_SIZE_Y = {props.size_y:.2f}",
f"INIT_SIZE_Z = {props.size_z:.2f}",
"",
f"INIT_COLOR = ({props.color[0]:.2f}, {props.color[1]:.2f}, {props.color[2]:.2f})",
f"INIT_ALPHA = {props.alpha:.2f}",
]
context.window_manager.clipboard = "\n".join(lines)
self.report({'INFO'}, "現在の数値をコード形式でコピーしました")
return {'FINISHED'}
class CUBE_GEN_OT_open_url(bpy.types.Operator):
bl_idname = "cube_gen.open_url"
bl_label = "リンクを開く"
url: bpy.props.StringProperty()
def execute(self, context):
if self.url:
webbrowser.open(self.url)
return {'FINISHED'}
class CUBE_GEN_OT_remove_addon(bpy.types.Operator):
bl_idname = "wm.cube_gen_remove_addon"
bl_label = "アドオン削除"
def execute(self, context):
unregister()
return {'FINISHED'}
# ========================================================================
# 6. UI Panels
# ========================================================================
class VIEW3D_PT_cube_generator(bpy.types.Panel):
bl_label = "Cube Generator"
bl_order = 0
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
props = context.scene.cube_gen_props
# --- 立方体設定パネル ---
box = layout.box()
box.label(text="立方体設定", icon='MESH_CUBE')
col = box.column(align=True)
col.label(text="中心位置:")
row = col.row(align=True)
row.prop(props, "loc_x")
row.prop(props, "loc_y")
row.prop(props, "loc_z")
col.separator()
col.label(text="大きさ:")
row = col.row(align=True)
row.prop(props, "size_x")
row.prop(props, "size_y")
row.prop(props, "size_z")
col.separator()
col.label(text="マテリアル:")
row = col.row(align=True)
row.prop(props, "color", text="")
row.prop(props, "alpha", slider=True)
layout.separator()
layout.operator("cube_gen.create", text="作成 / 更新", icon='MESH_CUBE')
layout.operator("cube_gen.detach", text="完成Objectとして切離し", icon='UNLINKED')
# --- コードコピーパネル ---
box_out = layout.box()
box_out.label(text="現在の数値を初期値に", icon='TEXT')
box_out.operator("cube_gen.copy_code", text="コード形式でコピー", icon='COPYDOWN')
class VIEW3D_PT_cube_gen_links(bpy.types.Panel):
bl_label = "Links"
bl_order = 100
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
for name, url in ADDON_LINKS.items():
if name and url:
layout.operator("cube_gen.open_url", text=name, icon='URL').url = url
class VIEW3D_PT_cube_gen_remove(bpy.types.Panel):
bl_label = "Addon Remove"
bl_order = 101
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
layout.operator("wm.cube_gen_remove_addon", icon='CANCEL')
# ========================================================================
# 7. Register / Unregister
# ========================================================================
classes = (
CubeGenProperties,
CUBE_GEN_OT_create,
CUBE_GEN_OT_detach,
CUBE_GEN_OT_copy_code,
CUBE_GEN_OT_open_url,
CUBE_GEN_OT_remove_addon,
VIEW3D_PT_cube_generator,
VIEW3D_PT_cube_gen_links,
VIEW3D_PT_cube_gen_remove,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.cube_gen_props = bpy.props.PointerProperty(type=CubeGenProperties)
# 1. ファイル読み込みハンドラーの登録 (二重登録防止)
if on_blend_load_post not in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.append(on_blend_load_post)
# 2. アドオン有効化時・Blender起動時のビュー設定を予約実行
schedule_viewport_settings_switch()
def unregister():
# 1. 実行中タイマーの安全な解除
if bpy.app.timers.is_registered(apply_viewport_settings):
try:
bpy.app.timers.unregister(apply_viewport_settings)
except ValueError:
pass
# 2. ファイル読み込みハンドラーの安全な解除
if on_blend_load_post in bpy.app.handlers.load_post:
bpy.app.handlers.load_post.remove(on_blend_load_post)
# 3. プロパティおよびクラスの解除
if hasattr(bpy.types.Scene, "cube_gen_props"):
del bpy.types.Scene.cube_gen_props
for cls in reversed(classes):
try:
bpy.utils.unregister_class(cls)
except Exception:
pass
if __name__ == "__main__":
try:
unregister()
except Exception:
pass
register()
# ========================================================================
# アドオン開発用ひな形(テンプレート)仕様指示プロンプト
# ========================================================================
"""
【AI / 開発者向けプロンプト:本ひな形を基にしたアドオン改修・新規作成ルール】
本スクリプトは、Blender 5.0+ 向けアドオンの標準ひな形(テンプレート)です。
機能を差し替える、または新機能を追加して新しいアドオンを作成する場合は、
以下の「必須保護機能」および「構造ルール」を厳守してください。
1. マテリアルプレビュー自動切り替え機能の維持
- アドオン有効化時、Blender起動時、ファイル読み込み完了時(load_post)に、
3Dビューポートの表示モードを「マテリアルプレビュー」に自動遷移させること。
(API: space.shading.type = 'MATERIAL')
- タイマー(bpy.app.timers)による非同期処理、二重登録防止、
および上限回数(MAX_TIMER_RETRIES)による安全終了ループを維持すること。
2. サイドバー(Nパネル)起動時自動オープンの維持
- 起動時や読み込み時に、3Dビューポートのサイドバーを自動で展開すること。
(API: space.show_region_ui = True)
- サイドバー内のタブ(bl_category)をアドオン専用タブへ自動フォーカスする
処理(region.active_panel_category)を維持すること。
※ 新規作成で bl_category 名を変更した場合は、フォーカス先文字列も同期させること。
3. アドオン削除パネル・機能コードの変更禁止(完全保護)
- パネル最下段(bl_order = 101)の「VIEW3D_PT_cube_gen_remove」
および削除オペレーター「CUBE_GEN_OT_remove_addon」のコードは一切削除・変更しないこと。
- 他の機能拡張の都合で削除処理の構造や配置順序を変えないこと。
4. パネル配置ルール
- メイン操作パネル:上部(bl_order = 0〜)
- Links(関連リンク)パネル:下から2番目(bl_order = 100)
- Addon Remove(アドオン削除)パネル:最下段(bl_order = 101)
5. 登録・解除の安全性(二重登録・解除エラー防止)
- ハンドラー(load_post)登録時の二重登録判定(if ... not in ...)を維持すること。
- unregister() 時のタイマー解除、ハンドラー解除における例外保護(try-except)を維持し、
アドオンの無効化や再読み込みが何度行われても安全に終了できるようにすること。
"""
# ========================================================================
# Blender 5.0+ Cube Generator (with Links, Detach & Remove)
# 立方体生成パネル + 切離し機能 + コピー機能 + リンク&削除パネル
# ========================================================================
bl_info = {
"name": "Cube Generator & Links",
"author": "zionad",
"version": (1, 1, 0),
"blender": (5, 0, 0),
"location": "View3D > Sidebar > Cube Gen",
"description": "立方体の生成・独立化、初期値コードのコピー、リンク・削除パネル",
"category": "Add Mesh",
}
import bpy
import bmesh
import webbrowser
# ========================================================================
# 1. 初期パラメータ(ここをコピーしたコードで書き換え可能)
# ========================================================================
INIT_LOC_X = 0.00
INIT_LOC_Y = 0.00
INIT_LOC_Z = 0.00
INIT_SIZE_X = 2.00
INIT_SIZE_Y = 2.00
INIT_SIZE_Z = 2.00
INIT_COLOR = (0.10, 0.50, 0.80)
INIT_ALPHA = 1.00
# ========================================================================
# 2. Links データ
# ========================================================================
ADDON_LINKS = {
"20261005 円柱作成": "<https://app.notion.com/p/20261005-3ef691cbf906807d9521eeb3fecabcf6>",
"20261003 円錐頂点矢印 と 球体_002": "<https://app.notion.com/p/20261003-3ed691cbf90680a187f7d9c5ea268c2b>",
"338 Sphere Cam": "<https://app.notion.com/p/338-Sphere-Cam-3c0347b3454a80abbb61ff2cd585792c>",
"posfie": "<https://posfie.com/@timekagura/t/zionad2022?sort=0>",
"zionadchat": "<https://x.com/zionadchat>",
"背後霊カメラ 最新版リンク": "<https://app.notion.com/p/Cam-blender-39f347b3454a806490a6f0107c2347dd>",
"blender コンテナ 20260713": "<https://note.com/zionadmillion/n/n9a47d88fe7b4>",
"骨格立方体": "<https://app.notion.com/p/351-2017-3cb347b3454a8086b15fec2f69408bd5>",
}
_IS_UPDATING = False
# ========================================================================
# 3. 処理ロジック (マテリアル & 立方体生成)
# ========================================================================
def setup_material(mat, color, alpha):
mat.use_nodes = True
tree = mat.node_tree
tree.nodes.clear()
out_node = tree.nodes.new(type="ShaderNodeOutputMaterial")
out_node.location = (400, 0)
bsdf = tree.nodes.new(type="ShaderNodeBsdfPrincipled")
bsdf.location = (0, 0)
if "Base Color" in bsdf.inputs:
bsdf.inputs["Base Color"].default_value = (*color[:3], 1.0)
if "Alpha" in bsdf.inputs:
bsdf.inputs["Alpha"].default_value = alpha
tree.links.new(bsdf.outputs["BSDF"], out_node.inputs["Surface"])
mat.blend_method = 'BLEND'
mat.diffuse_color = (*color[:3], alpha)
def update_cube(self, context):
global _IS_UPDATING
if _IS_UPDATING:
return
_IS_UPDATING = True
try:
props = context.scene.cube_gen_props
obj_name = "GeneratedCube"
mat_name = "GeneratedCube_Mat"
# オブジェクトの取得または新規作成
obj = bpy.data.objects.get(obj_name)
if not obj:
mesh = bpy.data.meshes.new(f"{obj_name}_Mesh")
obj = bpy.data.objects.new(obj_name, mesh)
try:
context.scene.collection.objects.link(obj)
except RuntimeError:
pass
# メッシュの生成 (BMesh)
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=1.0)
# 大きさの適用
bmesh.ops.scale(bm, vec=(max(0.001, props.size_x),
max(0.001, props.size_y),
max(0.001, props.size_z)), verts=bm.verts)
bm.to_mesh(obj.data)
bm.free()
obj.data.update()
# 中心位置の適用
obj.location = (props.loc_x, props.loc_y, props.loc_z)
# マテリアル設定
mat = bpy.data.materials.get(mat_name)
if not mat:
mat = bpy.data.materials.new(mat_name)
setup_material(mat, props.color, props.alpha)
if not obj.data.materials:
obj.data.materials.append(mat)
else:
obj.data.materials[0] = mat
finally:
_IS_UPDATING = False
# ========================================================================
# 4. Property Group
# ========================================================================
class CubeGenProperties(bpy.types.PropertyGroup):
loc_x: bpy.props.FloatProperty(name="X", default=INIT_LOC_X, update=update_cube)
loc_y: bpy.props.FloatProperty(name="Y", default=INIT_LOC_Y, update=update_cube)
loc_z: bpy.props.FloatProperty(name="Z", default=INIT_LOC_Z, update=update_cube)
size_x: bpy.props.FloatProperty(name="X", default=INIT_SIZE_X, min=0.001, update=update_cube)
size_y: bpy.props.FloatProperty(name="Y", default=INIT_SIZE_Y, min=0.001, update=update_cube)
size_z: bpy.props.FloatProperty(name="Z", default=INIT_SIZE_Z, min=0.001, update=update_cube)
color: bpy.props.FloatVectorProperty(
name="色", subtype='COLOR', default=INIT_COLOR,
min=0.0, max=1.0, size=3, update=update_cube)
alpha: bpy.props.FloatProperty(
name="透明度", default=INIT_ALPHA, min=0.0, max=1.0,
update=update_cube)
# ========================================================================
# 5. Operators
# ========================================================================
class CUBE_GEN_OT_create(bpy.types.Operator):
bl_idname = "cube_gen.create"
bl_label = "作成 / 更新"
bl_description = "立方体を生成・更新します"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
update_cube(None, context)
self.report({'INFO'}, "立方体を生成/更新しました")
return {'FINISHED'}
class CUBE_GEN_OT_detach(bpy.types.Operator):
bl_idname = "cube_gen.detach"
bl_label = "完成Objectとして切離し"
bl_description = "現在の生成Objectを完成品としてアドオン制御から切り離し、マテリアルも完全独立化します"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
obj_name = "GeneratedCube"
obj = bpy.data.objects.get(obj_name)
if not obj:
self.report({'WARNING'}, "生成Objectがありません")
return {'CANCELLED'}
suffix = "_Fixed"
# 1. オブジェクトとメッシュのリネーム(アドオンのターゲットから外す)
obj.name = obj.name + suffix
if obj.data:
obj.data.name = obj.data.name + suffix
# 2. マテリアルを複製して独立させる
for slot in obj.material_slots:
if slot.material:
new_mat = slot.material.copy()
new_mat.name = slot.material.name + suffix
slot.material = new_mat
self.report({'INFO'}, "完成Objectとして切離しました。マテリアルも独立しました。")
return {'FINISHED'}
class CUBE_GEN_OT_copy_code(bpy.types.Operator):
bl_idname = "cube_gen.copy_code"
bl_label = "設定をコード形式でコピー"
bl_description = "現在の数値をPythonの初期値コードとしてクリップボードへコピーします"
def execute(self, context):
props = context.scene.cube_gen_props
lines = [
"# ========================================================================",
"# 1. 初期パラメータ(ここをコピーしたコードで書き換え可能)",
"# ========================================================================",
"",
f"INIT_LOC_X = {props.loc_x:.2f}",
f"INIT_LOC_Y = {props.loc_y:.2f}",
f"INIT_LOC_Z = {props.loc_z:.2f}",
"",
f"INIT_SIZE_X = {props.size_x:.2f}",
f"INIT_SIZE_Y = {props.size_y:.2f}",
f"INIT_SIZE_Z = {props.size_z:.2f}",
"",
f"INIT_COLOR = ({props.color[0]:.2f}, {props.color[1]:.2f}, {props.color[2]:.2f})",
f"INIT_ALPHA = {props.alpha:.2f}",
]
context.window_manager.clipboard = "\n".join(lines)
self.report({'INFO'}, "現在の数値をコード形式でコピーしました")
return {'FINISHED'}
class CUBE_GEN_OT_open_url(bpy.types.Operator):
bl_idname = "cube_gen.open_url"
bl_label = "リンクを開く"
url: bpy.props.StringProperty()
def execute(self, context):
if self.url:
webbrowser.open(self.url)
return {'FINISHED'}
class CUBE_GEN_OT_remove_addon(bpy.types.Operator):
bl_idname = "wm.cube_gen_remove_addon"
bl_label = "アドオン削除"
def execute(self, context):
unregister()
return {'FINISHED'}
# ========================================================================
# 6. UI Panels
# ========================================================================
class VIEW3D_PT_cube_generator(bpy.types.Panel):
bl_label = "Cube Generator"
bl_order = 0
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
props = context.scene.cube_gen_props
# --- 立方体設定パネル ---
box = layout.box()
box.label(text="立方体設定", icon='MESH_CUBE')
col = box.column(align=True)
col.label(text="中心位置:")
row = col.row(align=True)
row.prop(props, "loc_x")
row.prop(props, "loc_y")
row.prop(props, "loc_z")
col.separator()
col.label(text="大きさ:")
row = col.row(align=True)
row.prop(props, "size_x")
row.prop(props, "size_y")
row.prop(props, "size_z")
col.separator()
col.label(text="マテリアル:")
row = col.row(align=True)
row.prop(props, "color", text="")
row.prop(props, "alpha", slider=True)
layout.separator()
layout.operator("cube_gen.create", text="作成 / 更新", icon='MESH_CUBE')
layout.operator("cube_gen.detach", text="完成Objectとして切離し", icon='UNLINKED')
# --- コードコピーパネル ---
box_out = layout.box()
box_out.label(text="現在の数値を初期値に", icon='TEXT')
box_out.operator("cube_gen.copy_code", text="コード形式でコピー", icon='COPYDOWN')
class VIEW3D_PT_cube_gen_links(bpy.types.Panel):
bl_label = "Links"
bl_order = 100
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
for name, url in ADDON_LINKS.items():
if name and url:
layout.operator("cube_gen.open_url", text=name, icon='URL').url = url
class VIEW3D_PT_cube_gen_remove(bpy.types.Panel):
bl_label = "Addon Remove"
bl_order = 101
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'Cube Gen'
def draw(self, context):
layout = self.layout
layout.operator("wm.cube_gen_remove_addon", icon='CANCEL')
# ========================================================================
# 7. Register
# ========================================================================
classes = (
CubeGenProperties,
CUBE_GEN_OT_create,
CUBE_GEN_OT_detach,
CUBE_GEN_OT_copy_code,
CUBE_GEN_OT_open_url,
CUBE_GEN_OT_remove_addon,
VIEW3D_PT_cube_generator,
VIEW3D_PT_cube_gen_links,
VIEW3D_PT_cube_gen_remove,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.cube_gen_props = bpy.props.PointerProperty(type=CubeGenProperties)
def unregister():
if hasattr(bpy.types.Scene, "cube_gen_props"):
del bpy.types.Scene.cube_gen_props
for cls in reversed(classes):
try:
bpy.utils.unregister_class(cls)
except Exception:
pass
if __name__ == "__main__":
try:
unregister()
except Exception:
pass
register()