2
$\begingroup$

To align active camera to match the current view I could simply pan, zoom or rotate the view. Select active camera from outliner then hit Ctrl + Alt + Numpad 0. I only wonder, is there a way to write this in python?

I have dug into sites and look for a way to hack it. Most of it are related on aligning view to camera, align camera to objects, or outdated. No way I could find on camera to view.

So far, i have found few scripts and this script...

bpy.ops.view3d.camera_to_view()

... this would get me into runtime error "RuntimeError: Operator bpy.ops.view3d.view_selected.poll() expected a view3d region"

and this script...

bpy.ops.view3d.camera_to_view_selected()

...seem made all sense, but I wouldn't know how or where to start with it. Is it possible to write a script on Ctrl + Alt Numpad 0?

$\endgroup$

1 Answer 1

2
$\begingroup$

You have found the right command bpy.ops.view3d.camera_to_view() but it can't be run as is from the Python console. The error message means that the op can only be run from a 3D Viewport context. There are (at least) two solutions:

  • Create an operator class that performs the operation and make sure you invoke it from a 3D Viewport context. Here's a class I use for adding a new camera to a scene.
class TOOL_OT_add_camera(Operator):
    bl_idname = "camera.add"
    bl_label = "add camera"
    bl_description = "add a camera in the viewport"
    bl_options = {"REGISTER", "UNDO"}

    def execute(self, context):
        """ Add a new camera to the scene, make it the active camera,
            and match it to the current view.
        """

        camera_data = bpy.data.cameras.new(name="Camera")
        if camera_data is None:
            return {"CANCELLED"}

        my_camera = bpy.data.objects.new("Camera", camera_data)

        if my_camera is None:
            return {"CANCELLED"}

        bpy.context.scene.collection.objects.link(my_camera)
        context.scene.camera = my_camera

        bpy.ops.view3d.camera_to_view()
        return {"FINISHED"}
  • Override the context. Here's sample code that will do that:
win = bpy.context.window
scr = win.screen
areas3d  = [area for area in scr.areas if area.type == 'VIEW_3D']
region   = [region for region in areas3d[0].regions if region.type == 'WINDOW']

override = {
    'window': win,
    'screen': scr,
    'area'  : areas3d[0],
    'region': region[0],
    'scene' : bpy.context.scene,
}

bpy.ops.view3d.camera_to_view(override)

Note that this code makes a couple of assumptions; mainly that the interesting 3D viewport will be the first one on the list of 3D viewport areas. Since mostly we operate with only one 3D viewport this is usually true.

In this case, the example override is overkill and can be simplified to

areas3d  = [area for area in bpy.context.window.screen.areas if area.type == 'VIEW_3D']
bpy.ops.view3d.camera_to_view({'area':areas3d[0]})

You can read more about operator overrides in the bpy ops introduction. The above code was adapted from this answer to "python - How do I override context for bpy.ops.mesh.loopcut?"

Note that this will only work if you have an active camera.

3.2 Update

Context overrides are deprecated in Blender 3.2 and are scheduled to be removed in Blender 3.3 The replacement is temp_override. The manual has examples of how to use the new function.

$\endgroup$
2
  • $\begingroup$ Works great, thanks for clear insights on operator overrides. Worth of knowledge $\endgroup$
    – sirrus
    Feb 28 at 16:01
  • $\begingroup$ @sirrus you're welcome. If you haven't already you might want to read What should I do when someone answers my question? and perhaps accept my answer. $\endgroup$ Feb 28 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.