# Calling modal operator from prop update function ---> wrong context (how to change context for modal operator)

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):

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
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':


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):

#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
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 '18 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 '18 at 14:51
• @batFINGER I answered as edit of my question, it is too long for comment. – CoD Feb 25 '18 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 '18 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 '18 at 15:42