基本アドオン設定 更新

bl_info = {
"name": "Viewport & Automation Master",
"author": "Your Name",
"version": (2, 7),
"blender": (5, 0, 0),
"location": "View3D > Sidebar (Nパネル)",
"description": "画面中央化、ビュー詳細情報取得、グリッド・背景色設定、各種リンク",
"category": "3D View",
}
# 【作成日】 2026年7月11日 12:44
# ===================================
# 【初期設定・定数定義】 (1行ずつ定義)
# ===================================
PREFIX = "front_tools"
TAB_NAME = " back color "
GRID_DEFAULT_1 = 10.0
GRID_DEFAULT_2 = 100.0
LINK_NAME_1 = "基本アドオン設定 20260711b"
LINK_URL_1 = "<https://app.notion.com/p/20260711-39af5dacaf438027811dcd877db7c630>"
LINK_NAME_2 = "posfie"
LINK_URL_2 = "<https://posfie.com/@timekagura/t/zionad2022?sort=0>"
LINK_NAME_3 = "zionadchat"
LINK_URL_3 = "<https://x.com/zionadchat>"
import bpy
import math
import mathutils
import webbrowser
# ===================================
# 【ビュー情報取得 関数】
# ===================================
def get_rv3d(context):
for a in context.window.screen.areas:
if a.type == 'VIEW_3D':
return a.spaces.active.region_3d
return None
def get_view_info_data(context):
rv3d = None
area = None
space = None
for a in context.window.screen.areas:
if a.type == 'VIEW_3D':
rv3d = a.spaces.active.region_3d
area = a
space = a.spaces.active
break
if not rv3d or not area or not space: return None
mode = rv3d.view_perspective
target_pos = rv3d.view_location
view_dist = rv3d.view_distance
cam_pos = rv3d.view_matrix.inverted().translation
view_dir = rv3d.view_rotation @ mathutils.Vector((0.0, 0.0, -1.0))
aspect = area.height / area.width if area.width > 0 else 1.0
sensor = 32.0
if mode == 'PERSP':
proj_text = "透視投影"
is_persp = True
elif mode == 'ORTHO':
proj_text = "平行投影"
is_persp = False
elif mode == 'CAMERA':
proj_text = "カメラビュー"
is_persp = True
else:
proj_text = "不明"
is_persp = False
if is_persp:
fov_h = 2.0 * math.atan(sensor / (2.0 * space.lens))
w = 2.0 * view_dist * math.tan(fov_h / 2.0)
h = w * aspect
else:
w = view_dist * (sensor / space.lens)
h = w * aspect
return {
'proj_text': proj_text,
'cam_pos': cam_pos,
'view_dir': view_dir,
'target_pos': target_pos,
'dist': view_dist,
'w': w,
'h': h,
'is_persp': is_persp
}
# ===================================
# 【オペレーター】
# ===================================
class VGM_OT_reset_view(bpy.types.Operator):
bl_idname = f"view3d.{PREFIX}_reset_view"
bl_label = "選択オブジェクトを画面中央にする"
def execute(self, context):
rv3d = get_rv3d(context)
if not rv3d: return {'CANCELLED'}
target = context.active_object
rv3d.view_location = target.matrix_world.to_translation() if target else rv3d.view_location.copy()
rv3d.view_distance = max(target.dimensions.length * 1.5, 10.0) if target else 30.0
rv3d.view_perspective = 'PERSP'
context.view_layer.update()
return {'FINISHED'}
class VGM_OT_update_info(bpy.types.Operator):
bl_idname = f"view3d.{PREFIX}_update_info"
bl_label = "情報を更新"
def execute(self, context):
for area in context.screen.areas:
if area.type == 'VIEW_3D': area.tag_redraw()
return {'FINISHED'}
class VGM_OT_copy_info(bpy.types.Operator):
bl_idname = f"view3d.{PREFIX}_copy_info"
bl_label = "情報をコピー"
def execute(self, context):
info = get_view_info_data(context)
if not info: return {'CANCELLED'}
lines = []
lines.append(f"投影モード: {info['proj_text']}")
if info['is_persp']:
lines.append(f"架空の視座位置 X: {info['cam_pos'].x:.1f}, Y: {info['cam_pos'].y:.1f}, Z: {info['cam_pos'].z:.1f}")
lines.append(f"画面中央への視線方向 X: {info['view_dir'].x:.1f}, Y: {info['view_dir'].y:.1f}, Z: {info['view_dir'].z:.1f}")
lines.append(f"画面中央位置 X: {info['target_pos'].x:.1f}, Y: {info['target_pos'].y:.1f}, Z: {info['target_pos'].z:.1f}")
lines.append(f"画面水平の大きさ: {info['w']:.1f}")
lines.append(f"画面上下の大きさ: {info['h']:.1f}")
if info['is_persp']:
lines.append(f"視座からの距離: {info['dist']:.1f}")
context.window_manager.clipboard = "\n".join(lines)
self.report({'INFO'}, "ビュー情報をクリップボードにコピーしました")
return {'FINISHED'}
class VGM_OT_set_grid_scale(bpy.types.Operator):
bl_idname = f"view3d.{PREFIX}_set_grid_scale"
bl_label = "Grid間隔を設定"
scale_val: bpy.props.FloatProperty(name="Scale")
use_custom: bpy.props.BoolProperty(name="カスタム値", default=False)
def execute(self, context):
props = getattr(context.scene, f"{PREFIX}_props")
val = props.custom_grid_scale if self.use_custom else self.scale_val
for area in context.screen.areas:
if area.type == 'VIEW_3D':
for space in area.spaces:
if space.type == 'VIEW_3D': space.overlay.grid_scale = val
return {'FINISHED'}
class VGM_OT_open_url(bpy.types.Operator):
bl_idname = f"wm.{PREFIX}_open_url"
bl_label = "リンクを開く"
url: bpy.props.StringProperty()
def execute(self, context):
if self.url:
webbrowser.open(self.url)
return {'FINISHED'}
class VGM_OT_remove_addon(bpy.types.Operator):
bl_idname = f"wm.{PREFIX}_remove_addon"
bl_label = "アドオン削除"
def execute(self, context): unregister(); return {'FINISHED'}
# ===================================
# 【プロパティ定義】
# ===================================
class VGM_Properties(bpy.types.PropertyGroup):
custom_grid_scale: bpy.props.FloatProperty(name="指定値", default=50.0, min=0.001)
# ===================================
# 【UIパネル (1つに統合)】
# ===================================
class VGM_PT_main_panel(bpy.types.Panel):
bl_idname = f"{PREFIX.upper()}_PT_main_panel"
bl_label = "Viewport & Grid Master"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = TAB_NAME
def draw(self, context):
layout = self.layout
props = getattr(context.scene, f"{PREFIX}_props")
info = get_view_info_data(context)
# --- 1. ビュー操作 (先頭) ---
layout.operator(f"view3d.{PREFIX}_reset_view", text="選択オブジェクトを画面中央にする", icon='ZOOM_ALL')
layout.separator(factor=1.5)
# --- 2. ビュー情報 ---
box_info = layout.box()
row_info = box_info.row(align=True)
row_info.label(text=f"情報: [{info['proj_text'] if info else '不明'}]", icon='INFO')
row_info.operator(f"view3d.{PREFIX}_update_info", text="更新", icon='FILE_REFRESH')
if info:
col_info = box_info.column(align=True)
col_info.label(text="画面中央位置:")
col_info.label(text=f" X: {info['target_pos'].x:.1f}, Y: {info['target_pos'].y:.1f}, Z: {info['target_pos'].z:.1f}")
col_info.separator()
col_info.label(text="画面中央への視線方向:")
col_info.label(text=f" X: {info['view_dir'].x:.1f}, Y: {info['view_dir'].y:.1f}, Z: {info['view_dir'].z:.1f}")
col_info.separator()
col_info.label(text=f"画面水平の大きさ: {info['w']:.1f}")
col_info.label(text=f"画面上下の大きさ: {info['h']:.1f}")
if info['is_persp']:
col_info.separator()
col_info.label(text=f"視座からの距離: {info['dist']:.1f}")
box_info.separator()
box_info.operator(f"view3d.{PREFIX}_copy_info", text="情報をコピー (1行ずつペースト)", icon='COPYDOWN')
else:
box_info.label(text="3Dビューが見つかりません")
layout.separator(factor=1.5)
# --- 3. Grid / 背景 設定 ---
box_grid = layout.box()
box_grid.label(text="Grid / 背景 設定", icon='GRID')
row_preset = box_grid.row(align=True)
row_preset.operator(f"view3d.{PREFIX}_set_grid_scale", text=f"間隔 {GRID_DEFAULT_1}").scale_val = GRID_DEFAULT_1
row_preset.operator(f"view3d.{PREFIX}_set_grid_scale", text=f"間隔 {GRID_DEFAULT_2}").scale_val = GRID_DEFAULT_2
row_custom = box_grid.row(align=True)
row_custom.prop(props, "custom_grid_scale")
row_custom.operator(f"view3d.{PREFIX}_set_grid_scale", text="指定値にする").use_custom = True
box_grid.separator()
prefs = context.preferences
if len(prefs.themes) > 0:
theme_view3d = prefs.themes[0].view_3d
col_theme = box_grid.column(align=True)
col_theme.prop(theme_view3d, "grid", text="Grid 色変更")
col_theme.separator()
col_theme.prop(theme_view3d.space.gradients, "high_gradient", text="背景色 (上)")
col_theme.prop(theme_view3d.space.gradients, "low_gradient", text="背景色 (下)")
layout.separator(factor=1.5)
# --- 4. リンク / アドオン削除 ---
box_sys = layout.box()
box_sys.label(text="システム / リンク", icon='PREFERENCES')
op1 = box_sys.operator(f"wm.{PREFIX}_open_url", text=LINK_NAME_1, icon='URL')
op1.url = LINK_URL_1
op2 = box_sys.operator(f"wm.{PREFIX}_open_url", text=LINK_NAME_2, icon='URL')
op2.url = LINK_URL_2
op3 = box_sys.operator(f"wm.{PREFIX}_open_url", text=LINK_NAME_3, icon='URL')
op3.url = LINK_URL_3
box_sys.separator()
box_sys.operator(f"wm.{PREFIX}_remove_addon", text="アドオンの登録を解除", icon='CANCEL')
# ===================================
# 【登録処理】
# ===================================
classes = [
VGM_Properties,
VGM_OT_reset_view,
VGM_OT_update_info,
VGM_OT_copy_info,
VGM_OT_set_grid_scale,
VGM_OT_open_url,
VGM_OT_remove_addon,
VGM_PT_main_panel
]
def register():
for c in classes: bpy.utils.register_class(c)
setattr(bpy.types.Scene, f"{PREFIX}_props", bpy.props.PointerProperty(type=VGM_Properties))
def unregister():
if hasattr(bpy.types.Scene, f"{PREFIX}_props"): delattr(bpy.types.Scene, f"{PREFIX}_props")
for c in reversed(classes):
try: bpy.utils.unregister_class(c)
except: pass
if __name__ == "__main__":
try: unregister()
except: pass
register()