Although the exact functionality of the Stored Views add-on doesn't cut it, we're going to leverage it in our own script. Since it's shipped with Blender for free, we can assume pretty much everybody has access to it.
We're going to add a Panel in the View Layer properties to display our minimalistic interface. It should have 2 buttons (Operators), one to save the view, and one to restore the view.
At first we assume the user didn't install the add-on and suggest enabling it directly from the panel if it isn't already enabled.
For some reason the add-on needs to be initialized every time we save & load the file, but we're not here to discuss that or refactor its code so we'll just roll with it.
Now that we have initialized the add-on, we can click on the "Save" button to store the current view.
Move the camera, tweak some view properties, and click on "Load" to return back to the saved view !
bl_label = "Save or Load View Layer Stored View"
bl_idname = "view_layer.save_or_load_stored_view"
index: bpy.props.IntProperty(description="Stored View Index")
save_or_load: bpy.props.BoolProperty(description= "False : Save, True : Load")
def poll(cls, context):
# Check if at least one View 3D area exists in the current screen
if any(a for a in context.screen.areas if a.type == "VIEW_3D"):
if bpy.app.version >= (3, 0, 0):
cls.poll_message_set("No 3D Viewport available")
def execute(self, context):
# Import the POV class from stored views add-on which offers view saving & loading features
from space_view3d_stored_views.core import POV
view_3d_area = next(a for a in context.screen.areas if a.type == "VIEW_3D")
if self.index >= 0:
view = context.scene.stored_views.view_list[self.index]
# We add a new entry since this view layer is not registered
view = context.scene.stored_views.view_list.add()
view.name = context.view_layer.name
pov = POV(self.index)
pov.view3d = view_3d_area.spaces # Patching this since it references a wrong space data
bl_label = "Stored View"
bl_idname = "GU_PT_view_layer_stored_view"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "view_layer"
def draw(self, context):
scene = context.scene
layout = self.layout
if not hasattr(scene, "stored_views"):
# Check if add-on is registered
if hasattr(bpy.types, "VIEW3D_OT_stored_views_initialize"):
layout.label(text="\"Stored Views\" add-on is needed to access Stored Views", icon="ERROR")
layout.operator("preferences.addon_enable").module = "space_view3d_stored_views"
view_layer = context.view_layer
# If the view layer's view has already been saved, we find its entry index
index = -1
for i, view in enumerate(scene.stored_views.view_list):
if view_layer.name == view.name:
index = i
row = layout.row()
save_op = row.operator("view_layer.save_or_load_stored_view", icon="EXPORT", text="Save")
save_op.index = index
save_op.save_or_load = False
sub_row = row.row()
load_op = sub_row.operator("view_layer.save_or_load_stored_view", icon="IMPORT", text="Load")
load_op.index = index
load_op.save_or_load = True
sub_row.enabled = index >= 0 # Prevent loading view if it doesn't exist
if __name__ == "__main__":
- A major limitation is that the saved entries are referencing the view layers by name. If you change the view layer name, the reference will be lost and you won't be able to load the view from the interface. Despair not, for we can find the lost view in the regular add-on interface, in the View panel of the Right hand side panel of the 3D viewport :
Use the cube icon to load the view, click in the name field to rename to your current view layer name, click on the dot to save the current view, and click on the cross to delete the entry.
It relies on another add-on being shipped with blender, it may not be available in some versions ? I don't know.
Viewport camera movements do not register with the undo system, so it's not possible to undo the operations.
I'm not aware of other limitations at the moment.
Turning it into an add-on
I've voluntarily kept the scope as small as possible so currently this is just a script that's supposed to be run in the text editor. Here are great resources to turn the script into a full-on add-on :