I have a modal operator to get the face index under mouse position from raycast (close to the "operator_modal_view3d_raycast.py" template).

class VaSelect(bpy.types.Operator):
    bl_idname = "wm.add_va"
    bl_label = "add va"

    def modal(self, context, event):

        if bpy.context.scene.VA_Mode != "VaEditMode":
            return {'CANCELLED'}

        else:
            get_face_index(context, event)
            return {'PASS_THROUGH'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        wm = context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

If i use a panel button to start it, it works fine and as intended.

When i try to invoke it from an EnumProperty update function:

def va_mode_update(self, context):
    if bpy.context.scene.VA_Mode == 'VaEditMode':
        bpy.ops.wm.add_va('INVOKE_DEFAULT')

i get this error message:

[...]
File "C:\Program Files\Blender3D\2.79\scripts\modules\bpy_extras\view3d_utils.py", line 46, in region_2d_to_vector_3d
    viewinv = rv3d.view_matrix.inverted()
AttributeError: 'NoneType' object has no attribute 'view_matrix'

Edit: What i found out so far: When i start from Panel Button bpy.context.region.type is WINDOW but when i start from update function, it is TOOLS. So, as long as i cannot change the context while modal is running it will not work, right? (And.. i cannot.. or can i?)

Edit2: it seems to be possible: https://docs.blender.org/api/2.79/bpy.ops.html#execution-context

Edit3: I used override method (in the raycast_function) to feed the raycast with the correct context:

update function
    override = {'region': "", 'rv3d': ""}
    for window in bpy.context.window_manager.windows:
        screen = window.screen

        for area in screen.areas:
            if area.type == 'VIEW_3D':
                for region in area.regions:
                    if region.type == 'WINDOW':
                        override['region'] = region
                for rv3d in area.spaces:
                    if hasattr(rv3d, 'region_3d'):
                        override['rv3d'] = rv3d.region_3d

    # get the context arguments
    scene = bpy.context.scene
    region = override['region']
    rv3d = override['rv3d']
    coord = event.mouse_region_x, event.mouse_region_y

It works now... somehow. Unfortunately the mouse cursor is offset. -.-

edit4:

When i use override as intended (at least i think i do, most likely i do something wrong, i am a very beginner :/):

class VaHighlight(bpy.types.Operator):
    bl_idname = "wm.add_va"
    bl_label = "Add Va"

    #override context to be able to call the operator from a enum property update function
    override = bpy.context.copy()
    for window in bpy.context.window_manager.windows:
        screen = window.screen

        for area in screen.areas:
            if area.type == 'VIEW_3D':
                for region in area.regions:
                    if region.type == 'WINDOW':
                        override['region'] = region

    def modal(self, override, event):

        if bpy.context.scene.VA_Mode != "VaEditMode":
            return {'CANCELLED'}

        else:              
            get_face_index(override, event)
            return {'PASS_THROUGH'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        wm = bpy.context.window_manager
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

def get_face_index(context, event):

    # get the context arguments
    scene = context.scene
    region = context.region
    [...]

the function does still not use the overwritten context but bpy.context (that is why i did it the way i did and move it into the function. This way it worked... except the offset. But that should be the same even if i get the override stuff working as intended.

Sure there is no attribute rv3d in bpy.context, but here i just pass the right context to the variable "rv3d" using the key rv3d of the dic named override ...)

area.regions[-1] does work but the region is not the (main) problem, it is rv3d = context.region_data. Took me beginner a good part of the day to find the "solution" i have atm.

  • You override context and pass that dictionary to an operator. There is no context attribute rv3d. This is why when you have an operator method like def modal(self, context, event): in its code, use area = context.area and NOT area = bpy.context.area This way if we override context override["area"] = some_3d_view_area and pass it to the op bpy.ops.some.operator(override) it will use the overridden context elements. – batFINGER Feb 25 at 14:31
  • If the enum is in the TOOLS region, the hassle is prob in the operator code that uses region = context.region, get the WINDOW region from the context.area instead by looking at its type like above (or its generally area.regions[-1] in my experience). There should IMO be no need to override context in this case. – batFINGER Feb 25 at 14:51
  • @batFINGER I answered as edit of my question, it is too long for comment. – CoD Feb 25 at 15:22
  • Ok . area = context.area ... region = area.regions[-1] .... r3dv = area.spaces.active.region_3d You are not using override as intended. You make a dictionary with context members and pass it as first argument to the operator. Use the context argument in operator methods. Dont use bpy.context or your overriden context members wont work. Eg you would have an override["region_data"] member not a r3vd. Check out override = context.copy() in the console. – batFINGER Feb 25 at 15:42
  • @batFINGER That works to replace the whole override stuff, thx :) (i feel dumb that it took me so long to find a verbose ugly solution when it is so easy and short sigh). Sadly the problem with the offset remains. No cursor offset when using a panel button, but (quite large) cursor offset when using the enum prop update function. – CoD Feb 25 at 15:42

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Browse other questions tagged or ask your own question.