Files
blender-addons/space_view3d_stored_views/ui.py
Campbell Barton e8da6131fd License headers: use SPDX-FileCopyrightText for all addons
Move copyright text to SPDX-FileCopyrightText or set to the
Blender Foundation so "make check_licenses" now runs without warnings.
2023-06-15 16:54:05 +10:00

285 lines
10 KiB
Python

# SPDX-FileCopyrightText: 2017-2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Authors: nfloyd, Francesco Siddi
import logging
module_logger = logging.getLogger(__name__)
import bpy
import blf
from . import core
from bpy.types import (
Operator,
Panel,
)
"""
If view name display is enabled,
it will check periodically if the view has been modified
since last set.
get_preferences_timer() is the time in seconds between these checks.
It can be increased, if the view become sluggish
It is set in the add-on preferences
"""
# Utility function get_preferences_timer for update of 3d view draw
def get_preferences_timer():
# replace the key if the add-on name changes
# TODO: expose refresh rate to ui???
addon = bpy.context.preferences.addons[__package__]
timer_update = (addon.preferences.view_3d_update_rate if addon else False)
return timer_update
def init_draw(context=None):
if context is None:
context = bpy.context
if "stored_views_osd" not in context.window_manager:
context.window_manager["stored_views_osd"] = False
if not context.window_manager["stored_views_osd"]:
context.window_manager["stored_views_osd"] = True
bpy.ops.stored_views.draw()
def _draw_callback_px(self, context):
area = context.area
if area and area.type == 'VIEW_3D':
ui_scale = context.preferences.system.ui_scale
r_width = text_location = context.region.width
r_height = context.region.height
font_id = 0 # TODO: need to find out how best to get font_id
blf.size(font_id, 11 * ui_scale)
text_size = blf.dimensions(0, self.view_name)
# compute the text location
text_location = 0
overlap = context.preferences.system.use_region_overlap
if overlap:
for region in area.regions:
if region.type == "UI":
text_location = r_width - region.width
text_x = text_location - text_size[0] - 10
text_y = r_height - text_size[1] - 8
blf.position(font_id, text_x, text_y, 0)
blf.draw(font_id, self.view_name)
class VIEW3D_stored_views_draw(Operator):
bl_idname = "stored_views.draw"
bl_label = "Show current"
bl_description = "Toggle the display current view name in the view 3D"
_handle = None
_timer = None
@staticmethod
def handle_add(self, context):
VIEW3D_stored_views_draw._handle = bpy.types.SpaceView3D.draw_handler_add(
_draw_callback_px, (self, context), 'WINDOW', 'POST_PIXEL')
VIEW3D_stored_views_draw._timer = \
context.window_manager.event_timer_add(get_preferences_timer(), window=context.window)
@staticmethod
def handle_remove(context):
if VIEW3D_stored_views_draw._handle is not None:
bpy.types.SpaceView3D.draw_handler_remove(VIEW3D_stored_views_draw._handle, 'WINDOW')
if VIEW3D_stored_views_draw._timer is not None:
context.window_manager.event_timer_remove(VIEW3D_stored_views_draw._timer)
VIEW3D_stored_views_draw._handle = None
VIEW3D_stored_views_draw._timer = None
@classmethod
def poll(cls, context):
# return context.mode == 'OBJECT'
return True
def modal(self, context, event):
if context.area:
context.area.tag_redraw()
if not context.area or context.area.type != "VIEW_3D":
return {"PASS_THROUGH"}
data = core.DataStore()
stored_views = context.scene.stored_views
if len(data.list) > 0 and \
data.current_index >= 0 and \
not stored_views.view_modified:
if not stored_views.view_modified:
sv = data.list[data.current_index]
self.view_name = sv.name
if event.type == 'TIMER':
is_modified = False
if data.mode == 'VIEW':
is_modified = core.View.is_modified(context, sv)
elif data.mode == 'POV':
is_modified = core.POV.is_modified(context, sv)
elif data.mode == 'LAYERS':
is_modified = core.Layers.is_modified(context, sv)
elif data.mode == 'DISPLAY':
is_modified = core.Display.is_modified(context, sv)
if is_modified:
module_logger.debug(
'view modified - index: %s name: %s' % (data.current_index, sv.name)
)
self.view_name = ""
stored_views.view_modified = is_modified
return {"PASS_THROUGH"}
else:
module_logger.debug('exit')
context.window_manager["stored_views_osd"] = False
VIEW3D_stored_views_draw.handle_remove(context)
return {'FINISHED'}
def execute(self, context):
if context.area.type == "VIEW_3D":
self.view_name = ""
VIEW3D_stored_views_draw.handle_add(self, context)
context.window_manager.modal_handler_add(self)
return {"RUNNING_MODAL"}
else:
self.report({"WARNING"}, "View3D not found. Operation Cancelled")
return {"CANCELLED"}
class VIEW3D_PT_properties_stored_views(Panel):
bl_label = "Stored Views"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "View"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
self.logger = logging.getLogger('%s Properties panel' % __name__)
layout = self.layout
if bpy.ops.view3d.stored_views_initialize.poll():
layout.operator("view3d.stored_views_initialize")
return
stored_views = context.scene.stored_views
# UI : mode
col = layout.column(align=True)
col.prop_enum(stored_views, "mode", 'VIEW')
row = layout.row(align=True)
row.operator("view3d.camera_to_view", text="Camera To view")
row.operator("stored_views.newcamera")
row = col.row(align=True)
row.prop_enum(stored_views, "mode", 'POV')
# row.prop_enum(stored_views, "mode", 'LAYERS')
# row.prop_enum(stored_views, "mode", 'DISPLAY')
# UI : operators
row = layout.row()
row.operator("stored_views.save").index = -1
# IO Operators
if core.get_preferences():
row = layout.row(align=True)
row.operator("stored_views.import_from_scene", text="Import from Scene")
row.operator("stored_views.import_blsv", text="", icon="IMPORT")
row.operator("stored_views.export_blsv", text="", icon="EXPORT")
data_store = core.DataStore()
list = data_store.list
# UI : items list
if len(list) > 0:
row = layout.row()
box = row.box()
# items list
mode = stored_views.mode
for i in range(len(list)):
# associated icon
icon_string = "MESH_CUBE" # default icon
# TODO: icons for view
if mode == 'POV':
persp = list[i].perspective
if persp == 'PERSP':
icon_string = "MESH_CUBE"
elif persp == 'ORTHO':
icon_string = "MESH_PLANE"
elif persp == 'CAMERA':
if list[i].camera_type != 'CAMERA':
icon_string = 'OBJECT_DATAMODE'
else:
icon_string = "OUTLINER_DATA_CAMERA"
if mode == 'LAYERS':
if list[i].lock_camera_and_layers is True:
icon_string = 'SCENE_DATA'
else:
icon_string = 'RENDERLAYERS'
if mode == 'DISPLAY':
shade = list[i].viewport_shade
if shade == 'TEXTURED':
icon_string = 'TEXTURE_SHADED'
if shade == 'MATERIAL':
icon_string = 'MATERIAL_DATA'
elif shade == 'SOLID':
icon_string = 'SOLID'
elif shade == 'WIREFRAME':
icon_string = "WIRE"
elif shade == 'BOUNDBOX':
icon_string = 'BBOX'
elif shade == 'RENDERED':
icon_string = 'MATERIAL'
# stored view row
subrow = box.row(align=True)
# current view indicator
if data_store.current_index == i and context.scene.stored_views.view_modified is False:
subrow.label(text="", icon='CHECKMARK')
subrow.operator("stored_views.set",
text="", icon=icon_string).index = i
subrow.prop(list[i], "name", text="")
subrow.operator("stored_views.save",
text="", icon="REC").index = i
subrow.operator("stored_views.delete",
text="", icon="PANEL_CLOSE").index = i
layout = self.layout
scene = context.scene
layout.label(text="Camera Selector")
cameras = sorted([o for o in scene.objects if o.type == 'CAMERA'],
key=lambda o: o.name)
if len(cameras) > 0:
for camera in cameras:
row = layout.row(align=True)
row.context_pointer_set("active_object", camera)
row.operator("cameraselector.set_scene_camera",
text=camera.name, icon='OUTLINER_DATA_CAMERA')
row.operator("cameraselector.preview_scene_camera",
text='', icon='RESTRICT_VIEW_OFF')
row.operator("cameraselector.add_camera_marker",
text='', icon='MARKER')
else:
layout.label(text="No cameras in this scene")
classes = (
VIEW3D_stored_views_draw,
VIEW3D_PT_properties_stored_views
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)