1
$\begingroup$

In Blender/Python 2.8, I am trying to use knife project to cut a circle through a cube. I am attempting to automate a process I can do manually.

My code is below; the last line throws this error:

RuntimeError: Operator bpy.ops.mesh.knife_project.poll() expected a view3d region & editmesh

When I do the process manually, I set the view as front ortho (numpad=1) so that the circle cuts the cube correctly. My code leaves that out as I don't know how to do the equivalent of setting the view in python for the purposes of knife project; and I am unsure if emulating the numpad keystroke will solve the problem, anyway.

Thanks for whatever help you can offer!


bpy.ops.object.select_all(action='DESELECT')
bpy.ops.curve.primitive_bezier_circle_add(radius=.25, enter_editmode=False, location=(0, 0, 0))
bpy.ops.transform.rotate(value=1.5708, orient_axis='X', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
bpy.ops.transform.translate(value=(0, -2, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
bpy.data.objects['Cube'].select_set(True)
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.knife_project()
```
$\endgroup$

1 Answer 1

4
$\begingroup$

Here is the code corrected,... I have used a function from here for override context: link and an operator described here for updating the view3D camera change: link

import bpy

def AssembleOverrideContextForView3dOps():
#=== Iterates through the blender GUI's windows, screens, areas, regions to find the View3D space and its associated window.  Populate an 'oContextOverride context' that can be used with bpy.ops that require to be used from within a View3D (like most addon code that runs of View3D panels)
# Tip: If your operator fails the log will show an "PyContext: 'xyz' not found".  To fix stuff 'xyz' into the override context and try again!
for oWindow in bpy.context.window_manager.windows:          ###IMPROVE: Find way to avoid doing four levels of traversals at every request!!
    oScreen = oWindow.screen
    for oArea in oScreen.areas:
        if oArea.type == 'VIEW_3D':                         ###LEARN: Frequently, bpy.ops operators are called from View3d's toolbox or property panel.  By finding that window/screen/area we can fool operators in thinking they were called from the View3D!
            for oRegion in oArea.regions:
                if oRegion.type == 'WINDOW':                ###LEARN: View3D has several 'windows' like 'HEADER' and 'WINDOW'.  Most bpy.ops require 'WINDOW'
                    #=== Now that we've (finally!) found the damn View3D stuff all that into a dictionary bpy.ops operators can accept to specify their context.  I stuffed extra info in there like selected objects, active objects, etc as most operators require them.  (If anything is missing operator will fail and log a 'PyContext: error on the log with what is missing in context override) ===
                    oContextOverride = {'window': oWindow, 'screen': oScreen, 'area': oArea, 'region': oRegion, 'scene': bpy.context.scene, 'edit_object': bpy.context.edit_object, 'active_object': bpy.context.active_object, 'selected_objects': bpy.context.selected_objects}   # Stuff the override context with very common requests by operators.  MORE COULD BE NEEDED!
                    #print("-AssembleOverrideContextForView3dOps() created override context: ", oContextOverride)
                    return oContextOverride
raise Exception("ERROR: AssembleOverrideContextForView3dOps() could not find a VIEW_3D with WINDOW region to create override context to enable View3D operators.  Operator cannot function.")

bpy.ops.object.select_all(action='DESELECT')
bpy.ops.curve.primitive_bezier_circle_add(radius=.25, enter_editmode=False, location=(0, 0, 0))
bpy.ops.transform.rotate(value=1.5708, orient_axis='X', orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(True, False, False), mirror=True, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.translate(value=(0, -2, 0), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', constraint_axis=(False, True, False), mirror=True, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.object.convert(target='MESH')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = bpy.data.objects['Cube']
bpy.data.objects['Cube'].select_set(True)
bpy.data.objects['BezierCircle'].select_set(True)
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
oContextOverride = AssembleOverrideContextForView3dOps() 
bpy.ops.view3d.view_axis(oContextOverride, type='FRONT')
bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1) 
bpy.ops.mesh.knife_project(oContextOverride)
$\endgroup$
2
  • $\begingroup$ When I run your code, I get the following error: AttributeError: 'IndentationError' object has no attribute 'msg' ... I can't figure out why it doesn't work (I think it is in the def...raise section of the code) and a web search doesn't turn up anything other than this: developer.blender.org/T65863 $\endgroup$
    – vndep
    Jul 17, 2019 at 18:11
  • 1
    $\begingroup$ I think the last character is not correct in the code, it must be deleted "}", I will try to edit the original post ;) $\endgroup$ Jul 18, 2019 at 0:51

You must log in to answer this question.

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