hello this is a really simple case, im creating a new window, i just want to do some operator in this window but unfortunately the context is still the old one.
bpy.context.area.ui_type = 'VIEW_3D'
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
bpy.ops.view3d.view_camera() #i want to do this operator in the new window
bpy.context.space_data.lock_camera = True #i want to do this boolean change in the new window
It depends on the area type and the operator used. Some operators require extensive context members, some require only one.
You could write a function that generates a context dict based on a window and its area type. This one is specifically made for 3d view, but should mostly work in other area types.
import bpy
def gen_C_dict(context, win, area_type='VIEW_3D'):
C_dict = context.copy()
for area in win.screen.areas:
if area.type == area_type:
for region in area.regions:
if region.type == 'WINDOW':
break
for space in area.spaces:
if space.type == area_type:
region_data = None
if area_type == 'VIEW_3D':
region_data = space.region_3d
break
break
C_dict.update(
area=area,
region=region,
region_data=region_data,
screen=win.screen,
space_data=space)
return C_dict
if __name__ == '__main__':
# gen_C_dict arguments
context = bpy.context
win = context.window # just an example. use new window instead
C_dict = gen_C_dict(context, win, area_type='VIEW_3D')
bpy.ops.view3d.view_camera(C_dict)
Usage:
Create your new window
Set the desired area of the new window
Generate an override by passing context, window and area type to gen_C_dict
There are no callbacks for window management so you would have to implement a monitor. You can use a pass-through modal operator or a timer that checks context.window_manager.windows at an interval, but itās a hacky approach.
This example runs a timer every 100 ms and checks windows by their hash() value using a set(). When new windows are created, the new set is compared against the old. It has negligible impact on performance if you donāt go much lower than 0.1, but Iāve never used something like this in the past myself.
Usage: Run in text editor, open / close windows and watch the console for output.
import bpy
context = bpy.context
wm = context.window_manager
def hash_windows():
return {hash(w) for w in wm.windows}
def winmon():
new_hash = hash_windows()
diff = windows.difference(new_hash)
if diff:
for hsh in diff:
print(f"window with hash {hsh} closed")
else:
sym = windows.symmetric_difference(new_hash)
if sym:
for hsh in sym:
print(f"window with hash {hsh} opened")
windows.clear()
windows.update(hash_windows())
return 0.1
if __name__ == '__main__':
windows = hash_windows()
bpy.app.timers.register(winmon)
thanks for your answer, im quite afraid of running a script every 100ms,it look unstable isnāt it ?
speaking of our context thread, i still have some misundestanding, why does some operator are still noe affected by this double change of context ? some operator like
Not really. Itās common to leave modal operators running in the background depending on the amount and type of add-ons you run. An operator polling every 100ms isnāt bad, but it depends on the complexity of the code it runs. You pretty much get python sets and area looping for free.
Updated example that supports the operators you mention.
import bpy
context = bpy.context
# supply window and area type
def gen_C_dict(context, window, area_type='VIEW_3D'):
C_dict = {}
for area in window.screen.areas:
if area.type == area_type:
for region in area.regions:
if region.type == 'WINDOW':
print("found region")
break
for space in area.spaces:
if space.type == area_type:
region_data = None
if area_type == 'VIEW_3D':
region_data = space.region_3d
break
break
C_dict.update(
window=window,
area=area,
region=region,
region_data=region_data,
screen=window.screen,
space_data=space)
return C_dict
# spawn your window
bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
new_window = context.window_manager.windows[-1]
new_window.screen.areas[-1].type = 'VIEW_3D'
# generate cdict based on new window
C_dict = gen_C_dict(context, new_window, area_type='VIEW_3D')
if __name__ == '__main__':
# toggle between these:
bpy.ops.view3d.view_camera(C_dict, 'INVOKE_DEFAULT')
# bpy.ops.view3d.walk(C_dict, 'INVOKE_DEFAULT')
# bpy.ops.view3d.view_axis(C_dict, 'INVOKE_DEFAULT', type='TOP')
how did you knew which context to redefine from just an operator code ? i didnāt find anything about what you just did on the API did i miss something ? and why do suddenly āāINVOKE_DEFAULTāā is inside the code ?
isnāt that problematic when blender is freezing or rendering ?
Just an educated guess. You can also take a peek at the C definition of an operator by searching for its rna identifier in the blender repository. There you can see what context arguments it requires.
3d view operators need āINVOKE_DEFAULTā to be able to perform camera transitioning. āINVOKE_DEFAULTā is implied by default in the keymap, however when an operator is called outside a keymap (eg. inside a script using bpy.ops we need to explicitly pass the argument)
Modal handlers are persistent until they are explicitly removed by the operator itself or blender exit. Any script execution, even the user interface may be blocked during rendering. Usually they just resume afterwards.