Context overriding in Blender 3.2 and later

In the Blender Python API, some operators can only be called in the area they are intended for. If we call such an operator in another area, it will not be executed or Blender will throw an error. To call a context-related operator from an area not intended for it, we need to redefine the context.

To override context in Blender 3.1 and older, we need to pass a structure copied from the real context of the desired area in the first parameter of the operator, commonly hidden.

However, starting from Blender 3.2, this method is marked as deprecated. This means that in 3.2 and later versions of Blender, it can still be used, however, no stability guarantees are given. And in the future, it will be fully removed from the Blender API.

Instead of passing an overridden context in the first parameter of an operator, the Blender API since version 3.2 offers the use of the temp_override() context method, which returns a handle to the overridden context when called.

To override the context, we need to call this method, passing in its parameters a pointer to the region whose context we need to override.

We can override the context for:

  • whole windows (window)
  • working area (area)
  • workspace region (region)
  • as well as specifying the exact key that points to the context of the object (for example, bone or active_object)

To override the context, for example, for the 3D viewport area now we need:

  1. to get a pointer of the desired area:

  1. and call the context override function:

As long as the overridden context handle is active, all the following operators will be executed as if they were called in the right context.

In order not to worry about closing the handle, it is better to call the temp_override() function using the “with” block of code. In this case, after the completion of the “with” block, the descriptor will be automatically removed, and further code will be executed in the original context.

For example, to call the operator for changing the method of selecting objects in the 3D viewport area from box to circle, from the Text Editor area, we need to call the following code:

First, we get a pointer to the desired area – the area of the 3D viewport. Next, we pass the resulting pointer in the temp_override(area=area) function parameter, and call the operator with the desired context inside the “with” block.

4 1 vote
Article Rating
Subscribe
Notify of
guest

5 Comment
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Christophe
Christophe
6 months ago

Do you know how to apply this principle to : bpy.ops.view3d.walk()

Andrew
Andrew
1 year ago

Hi Nikita – I really appreciate all your articles, thank you, I have learned so much!

I’m having trouble with this one…? I can load my script from the text editor and it functions as expected – I am trying to add a sound to a scene – but it ONLY works if I have the sequencer opened in another window. If I just have the text editor and viewport open, I get this error: Error: Python: Traceback (most recent call last):
 File “/loadSounds.py”, line 43, in <module>
 File “/loadSounds.py”, line 36, in add_Sound_to_VSE
IndexError: list index out of range

Anything obvious occur to you why this is happening & how to fix?

Thanks again for all you have contributed.

PS: here is my script for reference in case it helps:

import bpy

import pathlib
   
def get_sound_directory():
  # check if we are running from the Text Editor
  if bpy.context.space_data != None and bpy.context.space_data.type == “TEXT_EDITOR”:
    # get the path to the SAVED TO DISK script when running from blender
    print(“using the bpy.context.space_data script_path”)
    script_path = bpy.context.space_data.text.filepath
  else:
    print(“using the __file__ script_path”)
    # __file__ is built-in Python variable that represents the path to the script
    script_path = __file__

  print(f”script_path -> {script_path}”)

  sound_directory = pathlib.Path(script_path).resolve().parent
  #print(f”sound_directory = {sound_directory}/”)
   
  return sound_directory

def load_Sound(file_name, sound_directory):
  # get a path to a file that is next to the script
  sound_file_path = str(sound_directory / file_name)
  #print(f”path_to_file -> {path_to_file}”)
  return sound_file_path

sound_directory = get_sound_directory()
sound_file_path = load_Sound(“kaboom.mp3″,sound_directory)
print(f”sound_file_path -> {sound_file_path}”)

def add_Sound_to_VSE():
   
  area = [area for area in bpy.context.screen.areas if area.type == “SEQUENCE_EDITOR”][0]
 
  with bpy.context.temp_override(area=area):
    bpy.ops.sequencer.sound_strip_add(filepath=sound_file_path, directory=sound_directory, files=[{“name”:”kaboom.mp3″, “name”:”kaboom.mp3″}], relative_path=True, frame_start=24)

add_Sound_to_VSE()

Peeter
Peeter
1 year ago

I have a modal operator which catches event.mouse_region_x and y. It runs in the Node Editor. When I close the Node Editor, event.mouse_region_x will print as -1.

I would like to be able to keep catching event_mouse_region_x even when I close the Node Editor and open a new one (modal is running the whole time). The bpy.context.temp_override() didn’t do the trick, at least the way I used it…