Since node.dimensions
get evaluated on UI redraw, we're limited to using bpy.ops.wm.redraw_timer(...)
to trigger a redraw. The only problem that appears is that there has to be a ShaderEditor opened. The quickest and the easiest solution turned out to be switching the current area.ui_type
to ShaderNodeTree
and executing bpy.ops.wm.redraw_timer(...)
just after. Below is the modified script.
Solution
Switch the current area.ui_type
to ShaderNodeTree
and we won't have to ensure Shader Editor is present.
import bpy
def remove_all_nodes(mat):
nodes = mat.node_tree.nodes
for node in reversed(nodes):
nodes.remove(node)
def create_tex_image_nodes(context, mat):
nodes = mat.node_tree.nodes
# Create Image Texture nodes
image_nodes = []
for _ in range(5):
node = nodes.new(type='ShaderNodeTexImage')
image_nodes.append(node)
# Change active area.ui_type to Shader Editor
area = context.area
old_area_ui_type = area.ui_type
area.ui_type = 'ShaderNodeTree'
# Redraw nodes in the node tree
bpy.ops.wm.redraw_timer(type='DRAW_WIN', iterations=1)
for node in image_nodes:
print(node.dimensions[:])
# Restore active area.ui_type
area.ui_type = old_area_ui_type
return image_nodes
context = bpy.context
mat = bpy.context.object.active_material
remove_all_nodes(mat) # for testing
tex_image_nodes = create_tex_image_nodes(context, mat)
Console:
Warning: 1 x Draw window: 31.9231 ms, average: 31.92305565 ms
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
I tested and seems like 'DRAW_WIN'
is enough for bpy.ops.wm.redraw_timer(...)
. With 'DRAW_WIN_SWAP'
there's a minor UI area flicker (on Linux at least).
The solution can be further improved to change area.ui_type
only if there's no Shader Editor present on the screen, and with additional safety in case if active area is None.
Some may say that there could be a workaround with context override, but I couldn't get it working - blender would crash at the moment bpy.ops.wm.redraw_timer(...)
was called.
Improved Solution
Change area.ui_type
only if there's no Shader Editor present on the screen, and with additional safety in case if active area is None.
import bpy
def remove_all_nodes(mat):
nodes = mat.node_tree.nodes
for node in reversed(nodes):
nodes.remove(node)
def get_prepared_ui_area(context):
# Skip if Shader Editor exists
for area in context.screen.areas:
if area is None:
continue
if area.ui_type == 'ShaderNodeTree':
return {'area': area, 'old_ui_type': area.ui_type}
# Change area.ui_type to Shader Editor otherwise
# Loop as a safety if the current area is None
for area in context.screen.areas:
if area is None:
continue
area_data = {'area': area, 'old_ui_type': area.ui_type}
area.ui_type = 'ShaderNodeTree'
return area_data
# unlikely but present for code safety
return {'area': None, 'old_ui_type': None}
def create_tex_image_nodes(context, mat):
nodes = mat.node_tree.nodes
# Create Image Texture nodes
image_nodes = []
for _ in range(5):
node = nodes.new(type='ShaderNodeTexImage')
image_nodes.append(node)
area_data = get_prepared_ui_area(context)
# Redraw nodes in the node tree
bpy.ops.wm.redraw_timer(type='DRAW_WIN', iterations=1)
for node in image_nodes:
print(node.dimensions[:])
# Restore active area.ui_type
area = area_data['area']
old_area_ui_type = area_data['old_ui_type']
if area is not None and area.ui_type != old_area_ui_type:
area.ui_type = old_area_ui_type
return image_nodes
context = bpy.context
mat = bpy.context.object.active_material
remove_all_nodes(mat) # for testing
tex_image_nodes = create_tex_image_nodes(context, mat)
Console (I ran the script four times):
Warning: 1 x Draw window: 31.9231 ms, average: 31.92305565 ms
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
Warning: 1 x Draw window: 19.2859 ms, average: 19.28591728 ms
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
Warning: 1 x Draw window: 19.6609 ms, average: 19.66094971 ms
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
Warning: 1 x Draw window: 24.5559 ms, average: 24.55592155 ms
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
(240.0, 202.0)
node.width
andnode.height
$\endgroup$node.dimensions
is a read-only property, you can't edit it $\endgroup$