# 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): if context.area and context.area.type == 'VIEW_3D': 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, context.preferences.system.dpi) 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 context.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)