node leaf sorting

This commit is contained in:
2026-03-26 19:15:02 -07:00
parent 673b2cae6b
commit 711d7995b3
10 changed files with 44 additions and 45 deletions

View File

@@ -86,14 +86,13 @@ for category, class_names in MENU_LAYOUT.items():
}
def get_menu_metadata(class_name: str, fallback_category: str = "uncategorized") -> dict[str, Any]:
def get_menu_metadata(class_name: str) -> dict[str, Any]:
metadata = _NODE_METADATA.get(class_name)
if metadata is not None:
return dict(metadata)
fallback_order = _CATEGORY_ORDER.get(fallback_category, len(_CATEGORY_ORDER))
return {
"category": fallback_category,
"category_order": fallback_order,
"category": "Unsorted",
"category_order": len(_CATEGORY_ORDER),
"menu_order": 10_000,
}

View File

@@ -39,7 +39,7 @@ def get_node_info(class_name: str) -> dict[str, Any]:
"""
cls = NODE_CLASS_MAPPINGS[class_name]
input_types: dict = cls.INPUT_TYPES()
menu_metadata = get_menu_metadata(class_name, getattr(cls, "CATEGORY", "uncategorized"))
menu_metadata = get_menu_metadata(class_name)
return {
"name": class_name,

View File

@@ -33,7 +33,7 @@ class Statistics:
RETURN_TYPES = ("MEASURE_TABLE",)
RETURN_NAMES = ("stats",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Compute basic surface statistics: min, max, mean, RMS roughness, median, "
"and skewness. Equivalent to gwy_data_field_get_min/max/avg/rms."
@@ -82,7 +82,7 @@ class Histogram:
RETURN_TYPES = ("MEASURE_TABLE", "COORDPAIR",)
RETURN_NAMES = ("measurements", "marker pair",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Compute the height distribution histogram (DH). "
"Use log scale to reveal small peaks next to a dominant background. "
@@ -185,7 +185,7 @@ class Cursors:
RETURN_TYPES = ("MEASURE_TABLE","COORDPAIR",)
RETURN_NAMES = ("measurement","coord pair",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Place two cursors on a line plot or 2D field. "
"On lines it reports x/y positions and dx/dy. "
@@ -353,7 +353,7 @@ class FFT2D:
RETURN_TYPES = ("DATA_FIELD", "DATA_FIELD", "DATA_FIELD", "DATA_FIELD")
RETURN_NAMES = ("log_magnitude", "magnitude", "phase", "psdf")
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Compute the 2D FFT with optional windowing and mean/plane subtraction. "
"Outputs log magnitude, magnitude, phase, and PSDF as separate channels. "
@@ -511,7 +511,7 @@ class InverseFFT2D:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("image",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Reconstruct a spatial-domain image from a 2D frequency spectrum. "
"For exact reconstruction, connect magnitude/phase (or log magnitude/phase, "
@@ -658,7 +658,7 @@ class CrossSection:
RETURN_TYPES = ("LINE", "COORDPAIR",)
RETURN_NAMES = ("profile", "marker pair",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Extract a cross-section profile along a line between two points. "
"Drag the markers on the image to set the line endpoints. "
@@ -1010,7 +1010,7 @@ class Stats:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "process"
CATEGORY = "analysis"
DESCRIPTION = (
"Compute a contextual scalar statistic from a LINE, record table, DATA_FIELD, or IMAGE. "
"The available operations adapt to the connected input type."

View File

@@ -348,7 +348,7 @@ class ColorMap:
RETURN_TYPES = ("COLORMAP",)
RETURN_NAMES = ("colormap",)
FUNCTION = "build"
CATEGORY = "display"
DESCRIPTION = (
"Build a reusable colormap. Choose a preset, or create a custom gradient with min/max colours "
"and any number of intermediate stops."
@@ -389,7 +389,7 @@ class Font:
RETURN_TYPES = ("FONT",)
RETURN_NAMES = ("font",)
FUNCTION = "build"
CATEGORY = "display"
DESCRIPTION = (
"Build a reusable font spec for annotation overlays. Choose a discovered system font, "
"use the default fallback stack, or point to a custom font file."
@@ -429,7 +429,7 @@ class Annotations:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("annotated",)
FUNCTION = "render"
CATEGORY = "display"
DESCRIPTION = (
"Attach optional publication-style annotations to a DATA_FIELD without flattening the raw data. "
"The preview shows a scale bar and/or side colour legend, while downstream field operations keep the underlying AFM values."
@@ -488,7 +488,7 @@ class Markup:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("annotated",)
FUNCTION = "process"
CATEGORY = "display"
DESCRIPTION = (
"Draw simple vector markup over a DATA_FIELD without flattening the underlying data. "
"Choose a shape mode, colour, and stroke width, then drag directly on the preview to place lines, rectangles, circles, or arrows."
@@ -549,7 +549,7 @@ class PreviewImage:
RETURN_TYPES = ()
FUNCTION = "preview"
CATEGORY = "display"
OUTPUT_NODE = True
DESCRIPTION = "Display an IMAGE or DATA_FIELD as a coloured thumbnail. Connect either input."
@@ -614,7 +614,7 @@ class View3D:
RETURN_TYPES = ()
FUNCTION = "render"
CATEGORY = "display"
OUTPUT_NODE = True
DESCRIPTION = (
"Interactive 3D surface view of a DATA_FIELD. "
@@ -691,7 +691,7 @@ class PrintTable:
RETURN_TYPES = ()
FUNCTION = "print_table"
CATEGORY = "display"
OUTPUT_NODE = True
DESCRIPTION = "Send a measurement or record table to the browser as a WebSocket message for display."
@@ -724,7 +724,7 @@ class ValueDisplay:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "display_value"
CATEGORY = "display"
DESCRIPTION = "Display a FLOAT, or a selected numeric row from a measurement table, and pass the value through unchanged."
_broadcast_value_fn = None

View File

@@ -34,7 +34,7 @@ class GaussianFilter:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("filtered",)
FUNCTION = "process"
CATEGORY = "filters"
DESCRIPTION = "Apply a Gaussian blur. Equivalent to gwy_data_field_filter_gaussian."
def process(self, field: DataField, sigma: float) -> tuple:
@@ -61,7 +61,7 @@ class MedianFilter:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("filtered",)
FUNCTION = "process"
CATEGORY = "filters"
DESCRIPTION = "Apply a median filter. Equivalent to gwy_data_field_filter_median."
def process(self, field: DataField, size: int) -> tuple:
@@ -90,7 +90,7 @@ class EdgeDetect:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("edges",)
FUNCTION = "process"
CATEGORY = "filters"
DESCRIPTION = (
"Detect edges using Sobel, Prewitt, Laplacian, or LoG operators. "
"Equivalent to gwy_data_field_filter_sobel / gwy_data_field_filter_laplacian."
@@ -229,7 +229,7 @@ class FFTFilter1D:
RETURN_TYPES = ("LINE",)
RETURN_NAMES = ("filtered",)
FUNCTION = "process"
CATEGORY = "filters"
DESCRIPTION = (
"Frequency-domain filtering of a 1-D line profile. "
"Supports lowpass, highpass, bandpass, and notch (band-reject) modes "
@@ -295,7 +295,7 @@ class FFTFilter2D:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("filtered",)
FUNCTION = "process"
CATEGORY = "filters"
DESCRIPTION = (
"Frequency-domain filtering of a 2-D data field. "
"Supports lowpass, highpass, bandpass, and notch (band-reject) modes "

View File

@@ -147,7 +147,7 @@ class Image:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("field",)
FUNCTION = "load"
CATEGORY = "io"
DESCRIPTION = (
"Load any supported file. "
"SPM formats (.gwy, .sxm, .ibw) provide calibrated dimensions; "
@@ -384,7 +384,7 @@ class ImageDemo:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("field",)
FUNCTION = "load"
CATEGORY = "io"
DESCRIPTION = "Load a bundled demo file so you can try the app without providing your own data."
def load(self, name: str = "", colormap: str = "viridis", colormap_map=None):
@@ -408,7 +408,7 @@ class Folder:
RETURN_TYPES = ("DIRECTORY",)
RETURN_NAMES = ("directory",)
FUNCTION = "list_files"
CATEGORY = "io"
DESCRIPTION = (
"Pick a folder and output its directory path plus one file socket per compatible image, array, or SPM file inside it. "
"Supported files include common images, .npy/.npz arrays, and .gwy/.sxm/.ibw scans."
@@ -441,7 +441,7 @@ class Coordinate:
RETURN_TYPES = ("COORD",)
RETURN_NAMES = ("point",)
FUNCTION = "process"
CATEGORY = "io"
DESCRIPTION = "Output a fractional (x, y) coordinate pair in [0, 1]."
def process(self, x: float, y: float) -> tuple:
@@ -464,7 +464,7 @@ class CoordinatePair:
RETURN_TYPES = ("COORDPAIR",)
RETURN_NAMES = ("coord pair",)
FUNCTION = "process"
CATEGORY = "io"
DESCRIPTION = "Output a pair of coordinates."
def process(self, a: tuple, b: tuple) -> tuple:
@@ -490,7 +490,7 @@ class Number:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "process"
CATEGORY = "io"
DESCRIPTION = (
"Output a fixed numeric value. "
"When connected to FLOAT inputs the exact value is used; "
@@ -528,7 +528,7 @@ class RangeSlider:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "process"
CATEGORY = "io"
DESCRIPTION = (
"Interactive float slider. Set min and max bounds, then drag the slider to output a FLOAT value."
)
@@ -584,7 +584,7 @@ class SaveImage:
RETURN_TYPES = ()
FUNCTION = "save"
CATEGORY = "io"
OUTPUT_NODE = True
MANUAL_TRIGGER = True
DESCRIPTION = (

View File

@@ -33,7 +33,7 @@ class PlaneLevelField:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("leveled",)
FUNCTION = "process"
CATEGORY = "level"
DESCRIPTION = (
"Fit and subtract a least-squares plane from the data. "
"Equivalent to gwy_data_field_fit_plane + gwy_data_field_plane_level."
@@ -83,7 +83,7 @@ class PolyLevelField:
RETURN_TYPES = ("DATA_FIELD", "DATA_FIELD")
RETURN_NAMES = ("leveled", "background")
FUNCTION = "process"
CATEGORY = "level"
DESCRIPTION = (
"Fit and subtract a polynomial background of given degree in x and y. "
"Equivalent to gwy_data_field_fit_polynom."
@@ -131,7 +131,7 @@ class FixZero:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("zeroed",)
FUNCTION = "process"
CATEGORY = "level"
DESCRIPTION = (
"Shift data so that the minimum (or mean/median) is zero. "
"Equivalent to fix_zero in Gwyddion's level.c."

View File

@@ -173,7 +173,7 @@ class DrawMask:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("mask",)
FUNCTION = "process"
CATEGORY = "mask"
DESCRIPTION = (
"Paint a binary mask directly over an image preview. "
"Pen size controls newly drawn strokes, the overlay lets you clear the mask, "
@@ -227,7 +227,7 @@ class ThresholdMask:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("mask",)
FUNCTION = "process"
CATEGORY = "mask"
DESCRIPTION = (
"Create a binary mask by thresholding data. "
"Otsu automatically finds the optimal threshold. "
@@ -295,7 +295,7 @@ class MaskMorphology:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("mask",)
FUNCTION = "process"
CATEGORY = "mask"
DESCRIPTION = (
"Apply morphological operations to a binary mask. "
"Dilate expands regions, erode shrinks them, "
@@ -358,7 +358,7 @@ class MaskInvert:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("mask",)
FUNCTION = "process"
CATEGORY = "mask"
DESCRIPTION = "Invert a binary mask — swap masked and unmasked regions."
_broadcast_fn = None
@@ -400,7 +400,7 @@ class MaskCombine:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("mask",)
FUNCTION = "process"
CATEGORY = "mask"
DESCRIPTION = (
"Combine two binary masks with a boolean operation. "
"AND keeps overlap, OR merges, XOR keeps non-overlapping regions, "

View File

@@ -30,7 +30,7 @@ class ColormapAdjust:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("field",)
FUNCTION = "process"
CATEGORY = "modify"
DESCRIPTION = (
"Adjust how a DATA_FIELD maps into its colormap without changing the underlying data. "
"offset and scale operate in normalized display coordinates; Auto resets to the full data range."
@@ -71,7 +71,7 @@ class CropResizeField:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("field",)
FUNCTION = "process"
CATEGORY = "modify"
DESCRIPTION = (
"Crop a DATA_FIELD with a draggable rectangle defined by two corners, then optionally resize it. "
"Incoming COORD inputs can lock either corner. Cropping updates physical extents and offsets; "
@@ -212,7 +212,7 @@ class RotateField:
RETURN_TYPES = ("DATA_FIELD",)
RETURN_NAMES = ("field",)
FUNCTION = "process"
CATEGORY = "modify"
DESCRIPTION = (
"Rotate a DATA_FIELD counterclockwise by an angle in degrees. "
"Optionally expand the canvas to keep the full rotated field while preserving the field center."

View File

@@ -30,7 +30,7 @@ class ParticleAnalysis:
RETURN_TYPES = ("RECORD_TABLE",)
RETURN_NAMES = ("particle_stats",)
FUNCTION = "process"
CATEGORY = "particles"
DESCRIPTION = (
"Label connected particle regions in a binary mask and compute per-particle "
"statistics: area, equivalent diameter, mean/max height, bounding box. "