2
$\begingroup$

I'm now trying to change Navigation into walk Navigation in the 3D viewport. So I write the code below.

import bpy

for area in bpy.context.screen.areas:
    if area.type == 'VIEW_3D':
        for region in area.regions:
            if region.type == 'WINDOW':
                bpy.ops.view3D.walk()      

this returns error

  File "D:\3Dmodels\python_demos.blend\operator_modal_view3d.py", line 11, in <module>
  File "D:\programs\Blender\stable\blender-2.92.0-windows64\2.92\scripts\modules\bpy\ops.py", line 132, in __call__
    ret = _op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.view3D.walk.poll() expected a view3d region

So I added a line override = {'area': area, 'region': region} and change walk() into walk(override).

import bpy

for area in bpy.context.screen.areas:
    if area.type == 'VIEW_3D':
        for region in area.regions:
            if region.type == 'WINDOW':
                override = {'area': area, 'region': region}
                bpy.ops.view3D.walk(override)   

However, Blender returns another error

ERROR (wm.operator): C:\b\buildbot-worker-windows\windows_292\blender.git\source\blender\windowmanager\intern\wm_event_system.c:1321 wm_operator_invoke: invalid operator call 'VIEW3D_OT_walk'

So if anyone knows how to run bpy.ops.view3D.walk(), please teach me and I would be very happy!

$\endgroup$

2 Answers 2

2
$\begingroup$

Putting 3D viewport in walk mode from py console.

Think you were 99% there.

IMO always a good idea to try things out in the python console

>>> C.area.type
'CONSOLE'

Override the context by finding a view3d area in the screen. Make sure an area aside from console is a view3d.

>>> for a in C.screen.areas:
...     if a.type == 'VIEW_3D':
...         break
...         
>>> a.type
'VIEW_3D'

Make a dictionary and fill with usual suspects area, space_data, and region. AFAIK It is always the case that the 'WINDOW' region is the last in the area regions. If not of this opinion can iterate them as shown in question.

>>> c = {}
>>> c["area"] = a
>>> c["space_data"] = a.spaces.active
>>> c["region"] = a.regions[-1]

Ok now give the override a go... oh and remember all letters in an operator are expected to be lower case eg 3d not 3D.... (However it does appear to accept view3D, but autocompletes on view3d).

>>> with C.temp_override(**c):
...    bpy.ops.view3d.walk('INVOKE_DEFAULT')
...     
{'RUNNING_MODAL'}

BINGO.. 3D view is in walk mode.

Ok all that done and shown, if you are going to write an operator that is invoked from within the 3d viewport, all the override stuff is not required. (Similarly if you change the area to a 3d viewport as shown by @Chris, but in reality we rarely want to do that way... hard to type into console if it becomes the 3d viewport (for example)) The default execution context of a button or menu pressed is 'INVOKE_DEFAULT'.

See the revisions to this answer for a version of this answer prior to Blender 3.2.

$\endgroup$
2
  • $\begingroup$ Thank you for your amazing answer! This worked perfectly and helped me to understand it deeply! $\endgroup$
    – tatmius
    Commented Mar 8, 2021 at 21:06
  • $\begingroup$ I found out that we only need to override area and region, blender.stackexchange.com/a/230836/31447 $\endgroup$
    – brockmann
    Commented Jul 19, 2021 at 7:53
0
$\begingroup$

Personally, I like having a simple test button add-on that appears as a sidebar n-panel in the 3d view. Because the panel is already in the 3D Viewport, you don't have to change context, you can simply test the commands to see if they work. Also, have you ever tried "Fly Mode"? I've enabled it in the following. Run this in Blender's text editor and you'll see the "Test" tab show up in your 3D View N-Panel area.

bl_info = {
    "name": "Test Button Addon",
    "blender": (2, 80, 0),
    "category": "3D View",
}

import bpy

class VIEW3D_PT_test_button(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Test Button Panel"
    bl_idname = "VIEW3D_PT_test_button"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Test'

    def draw(self, context):
        layout = self.layout

        # Create a simple row
        row = layout.row()
        row.operator("view3d.test_operator")

class VIEW3D_OT_test_operator(bpy.types.Operator):
    """Run the Test Operator"""
    bl_idname = "view3d.test_operator"
    bl_label = "Run Operator"
    bl_options = {'REGISTER', 'UNDO'}

#Code to execute when button is pressed
    def execute(self, context):
        #bpy.ops.view3d.walk('INVOKE_DEFAULT')
        bpy.ops.view3d.fly('INVOKE_DEFAULT')
        return {'FINISHED'}

#registration
def register():
    bpy.utils.register_class(VIEW3D_PT_test_button)
    bpy.utils.register_class(VIEW3D_OT_test_operator)

def unregister():
    bpy.utils.unregister_class(VIEW3D_PT_test_button)
    bpy.utils.unregister_class(VIEW3D_OT_test_operator)

if __name__ == "__main__":
    register()

screenshot of test panel and button

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .