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:
- to get a pointer of the desired area:
1 |
area = [area for area in bpy.context.screen.areas if area.type == "VIEW_3D"][0] |
- and call the context override function:
1 2 3 |
bpy.context.temp_override(area=area) # <ContextTempOverride object at 0x00000150E29E5080> |
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:
1 2 3 4 5 6 |
import bpy area = [area for area in bpy.context.screen.areas if area.type == "VIEW_3D"][0] with bpy.context.temp_override(area=area): bpy.ops.wm.tool_set_by_id(name='builtin.select_circle') |
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.
Do you know how to apply this principle to : bpy.ops.view3d.walk()
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()
You ara trying to get area in this line
area = [area for area in bpy.context.screen.areas if area.type == “SEQUENCE_EDITOR”][0]
but if you have no such area opened you get None and next when you are trying to override context with None you are getting error.
You need to have the VSE area opened to get right context for overriding
or add sound like this https://b3d.interplanety.org/en/adding-strips-to-the-blender-video-sequencer-editor/
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…
Context override is commonly used to execute one operator from the area which is not its own.
If you use modal operator to get mouse position, event.mouse_x, event.mouse_y, right? It executes through the window manager, and available for the whole window.
I think you need just to analyze on what area the mouse cursor is, get coordinates of all areas, something like [(area.x, area.y) for area in bpy.context.screen.areas] and having mouse coordinates count on what area is it placed now.