Node Wrangler: Fixes for Save Viewer operator
## Fix: Error in Node Wrangler UI when in Sequencer mode
Node Wrangler tried to find the current scene compositing node tree,
even when there was none. This could happen if working on a sequencer
compositing modifier.
This commit hides the Save This Image button when not in Scene
compositing mode.
## Fix: Error in Node Wrangler's Save Image op when file path is empty
If the file path is empty in Save Image operator, it returns `None`
and errors out.
This commit makes it return `{'CANCELLED'}` early in that case.
## Add webp to list of file formats for Save Viewer
## Fix: Error when Viewer Node image renamed and saved
The "Viewer Node" image that contains the current compositing node
tree's viewer data has a hardcoded name by default. However it can be
renamed by the user, which will make the Save Viewer operator fail.
This commit extracts a utility function to get the current viewer
image based on its properties, and simply saves that.
In addition, this avoids having to change the area type to an image
editor to get the current viewer node, and restoring the node editor
afterwards. This action did not restore the current node tree if the
editor was inside a node group.
## Rename "Save This Image" to "Save Viewer Image"
When calling the operator through Menu Search, it is not clear what
"Save This Image" refers to. This commit renames the operator to "Save
Viewer Image", but keeps the old name for the button label.
Pull Request: https://projects.blender.org/blender/blender/pulls/147085
This commit is contained in:
committed by
Nika Kutsniashvili
parent
d4e254a552
commit
8b351bdee8
@@ -7,8 +7,8 @@ bl_info = {
|
||||
# This is now displayed as the maintainer, so show the foundation.
|
||||
# "author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer", # Original Authors
|
||||
"author": "Blender Foundation",
|
||||
"version": (3, 56),
|
||||
"blender": (4, 2, 0),
|
||||
"version": (4, 0, 0),
|
||||
"blender": (5, 0, 0),
|
||||
"location": "Node Editor Toolbar or Shift-W",
|
||||
"description": "Various tools to enhance and speed up node-based workflow",
|
||||
"warning": "",
|
||||
|
||||
@@ -440,11 +440,12 @@ def bgreset_menu_func(self, context):
|
||||
def save_viewer_menu_func(self, context):
|
||||
space = context.space_data
|
||||
if (space.type == 'NODE_EDITOR'
|
||||
and space.tree_type == 'CompositorNodeTree'
|
||||
and space.node_tree_sub_type == 'SCENE'
|
||||
and space.node_tree is not None
|
||||
and space.node_tree.library is None
|
||||
and space.tree_type == 'CompositorNodeTree'
|
||||
and context.scene.compositing_node_group.nodes.active
|
||||
and context.scene.compositing_node_group.nodes.active.type == "VIEWER"):
|
||||
and space.edit_tree.nodes.active
|
||||
and space.edit_tree.nodes.active.type == "VIEWER"):
|
||||
self.layout.operator(operators.NWSaveViewer.bl_idname, icon='FILE_IMAGE')
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ from .utils.paths import match_files_to_socket_names, split_into_components
|
||||
from .utils.nodes import (node_mid_pt, autolink, node_at_pos, get_nodes_links,
|
||||
force_update, nw_check,
|
||||
nw_check_not_empty, nw_check_selected, nw_check_active, nw_check_space_type,
|
||||
nw_check_node_type, nw_check_visible_outputs, nw_check_viewer_node, NWBase,
|
||||
nw_check_node_type, nw_check_visible_outputs, get_viewer_image, nw_check_viewer_node, NWBase,
|
||||
get_first_enabled_output, is_visible_socket)
|
||||
|
||||
|
||||
@@ -2191,7 +2191,7 @@ class NWAddSequence(Operator, NWBase, ImportHelper):
|
||||
class NWSaveViewer(bpy.types.Operator, ExportHelper):
|
||||
"""Save the current viewer node to an image file"""
|
||||
bl_idname = "node.nw_save_viewer"
|
||||
bl_label = "Save This Image"
|
||||
bl_label = "Save Viewer Image"
|
||||
filepath: StringProperty(subtype="FILE_PATH")
|
||||
filename_ext: EnumProperty(
|
||||
name="Format",
|
||||
@@ -2206,7 +2206,9 @@ class NWSaveViewer(bpy.types.Operator, ExportHelper):
|
||||
('.dpx', 'DPX', ""),
|
||||
('.exr', 'OPEN_EXR', ""),
|
||||
('.hdr', 'HDR', ""),
|
||||
('.tif', 'TIFF', "")),
|
||||
('.tif', 'TIFF', ""),
|
||||
('.webp', 'WEBP', ""),
|
||||
),
|
||||
default='.png',
|
||||
)
|
||||
|
||||
@@ -2218,36 +2220,39 @@ class NWSaveViewer(bpy.types.Operator, ExportHelper):
|
||||
|
||||
def execute(self, context):
|
||||
fp = self.filepath
|
||||
if fp:
|
||||
formats = {
|
||||
'.bmp': 'BMP',
|
||||
'.rgb': 'IRIS',
|
||||
'.png': 'PNG',
|
||||
'.jpg': 'JPEG',
|
||||
'.jpeg': 'JPEG',
|
||||
'.jp2': 'JPEG2000',
|
||||
'.tga': 'TARGA',
|
||||
'.cin': 'CINEON',
|
||||
'.dpx': 'DPX',
|
||||
'.exr': 'OPEN_EXR',
|
||||
'.hdr': 'HDR',
|
||||
'.tiff': 'TIFF',
|
||||
'.tif': 'TIFF'}
|
||||
basename, ext = path.splitext(fp)
|
||||
image_settings = context.scene.render.image_settings
|
||||
old_media_type = image_settings.media_type
|
||||
old_file_format = image_settings.file_format
|
||||
old_tree_type = context.space_data.tree_type
|
||||
image_settings.media_type = 'IMAGE'
|
||||
image_settings.file_format = formats[self.filename_ext]
|
||||
context.area.type = "IMAGE_EDITOR"
|
||||
context.area.spaces[0].image = bpy.data.images['Viewer Node']
|
||||
context.area.spaces[0].image.save_render(fp)
|
||||
context.area.type = "NODE_EDITOR"
|
||||
context.space_data.tree_type = old_tree_type
|
||||
image_settings.media_type = old_media_type
|
||||
image_settings.file_format = old_file_format
|
||||
return {'FINISHED'}
|
||||
if not fp:
|
||||
return {'CANCELLED'}
|
||||
|
||||
formats = {
|
||||
'.bmp': 'BMP',
|
||||
'.rgb': 'IRIS',
|
||||
'.png': 'PNG',
|
||||
'.jpg': 'JPEG',
|
||||
'.jpeg': 'JPEG',
|
||||
'.jp2': 'JPEG2000',
|
||||
'.tga': 'TARGA',
|
||||
'.cin': 'CINEON',
|
||||
'.dpx': 'DPX',
|
||||
'.exr': 'OPEN_EXR',
|
||||
'.hdr': 'HDR',
|
||||
'.tiff': 'TIFF',
|
||||
'.tif': 'TIFF',
|
||||
'.webp': 'WEBP',
|
||||
}
|
||||
image_settings = context.scene.render.image_settings
|
||||
old_media_type = image_settings.media_type
|
||||
old_file_format = image_settings.file_format
|
||||
image_settings.media_type = 'IMAGE'
|
||||
image_settings.file_format = formats[self.filename_ext]
|
||||
|
||||
try:
|
||||
get_viewer_image().save_render(fp)
|
||||
except RuntimeError as e:
|
||||
self.report({'ERROR'}, rpt_("Could not write image: {}").format(e))
|
||||
|
||||
image_settings.media_type = old_media_type
|
||||
image_settings.file_format = old_file_format
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class NWResetNodes(bpy.types.Operator):
|
||||
|
||||
@@ -183,6 +183,14 @@ def get_output_location(tree):
|
||||
return loc_x, loc_y
|
||||
|
||||
|
||||
def get_viewer_image():
|
||||
for img in bpy.data.images:
|
||||
if (img.source == 'VIEWER'
|
||||
and len(img.render_slots) == 0
|
||||
and sum(img.size) > 0):
|
||||
return img
|
||||
|
||||
|
||||
def nw_check(cls, context):
|
||||
space = context.space_data
|
||||
if space.type != 'NODE_EDITOR':
|
||||
@@ -258,14 +266,10 @@ def nw_check_visible_outputs(cls, context):
|
||||
|
||||
|
||||
def nw_check_viewer_node(cls):
|
||||
for img in bpy.data.images:
|
||||
# False if not connected or connected but no image
|
||||
if (img.source == 'VIEWER'
|
||||
and len(img.render_slots) == 0
|
||||
and sum(img.size) > 0):
|
||||
return True
|
||||
cls.poll_message_set("Viewer image not found.")
|
||||
return False
|
||||
if get_viewer_image() is None:
|
||||
cls.poll_message_set("Viewer image not found.")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_first_enabled_output(node):
|
||||
|
||||
Reference in New Issue
Block a user