fix table math column picker

This commit is contained in:
2026-03-25 00:01:24 -07:00
parent 44de72d31b
commit a65b7c5642
9 changed files with 174 additions and 9 deletions

View File

@@ -50,6 +50,7 @@ class ExecutionEngine:
on_table: Callable[[str, list], None] | None = None,
on_mesh: Callable[[str, dict], None] | None = None,
on_overlay: Callable[[str, str], None] | None = None,
on_value: Callable[[str, float], None] | None = None,
on_warning: Callable[[str, str], None] | None = None,
) -> dict[str, tuple]:
"""
@@ -73,7 +74,7 @@ class ExecutionEngine:
node_outputs: dict[str, tuple] = {}
# Inject display callbacks before execution
self._inject_display_callbacks(on_preview, on_table, on_mesh, on_overlay, on_warning)
self._inject_display_callbacks(on_preview, on_table, on_mesh, on_overlay, on_value, on_warning)
for node_id in order:
node_def = prompt[node_id]
@@ -176,11 +177,12 @@ class ExecutionEngine:
on_table: Callable | None,
on_mesh: Callable | None = None,
on_overlay: Callable | None = None,
on_value: Callable | None = None,
on_warning: Callable | None = None,
) -> None:
"""Wire up broadcast callbacks on display node classes."""
from backend.nodes.display import PreviewImage, PrintTable, View3D
from backend.nodes.analysis import CrossSection, LineCursors
from backend.nodes.display import PreviewImage, PrintTable, View3D, ValueDisplay
from backend.nodes.analysis import CrossSection, LineCursors, TableMath
from backend.nodes.modify import CropResizeField
from backend.nodes.mask import ThresholdMask, MaskMorphology, MaskInvert, MaskCombine
from backend.nodes.io import SaveImage, LoadFile
@@ -192,6 +194,8 @@ class ExecutionEngine:
MaskCombine._broadcast_fn = on_preview
View3D._broadcast_mesh_fn = on_mesh
PrintTable._broadcast_table_fn = on_table
ValueDisplay._broadcast_value_fn = on_value
TableMath._broadcast_value_fn = on_value
CrossSection._broadcast_overlay_fn = on_overlay
LineCursors._broadcast_overlay_fn = on_overlay
CropResizeField._broadcast_overlay_fn = on_overlay
@@ -200,12 +204,12 @@ class ExecutionEngine:
def _set_node_id_on_display(self, cls: type, node_id: str) -> None:
"""Inform display nodes of their current node_id for WS tagging."""
from backend.nodes.display import PreviewImage, PrintTable, View3D
from backend.nodes.analysis import CrossSection, LineCursors
from backend.nodes.display import PreviewImage, PrintTable, View3D, ValueDisplay
from backend.nodes.analysis import CrossSection, LineCursors, TableMath
from backend.nodes.modify import CropResizeField
from backend.nodes.mask import ThresholdMask, MaskMorphology, MaskInvert, MaskCombine
from backend.nodes.io import LoadFile, SaveImage
if cls in (PreviewImage, PrintTable, View3D, CrossSection, LineCursors, CropResizeField,
if cls in (PreviewImage, PrintTable, View3D, ValueDisplay, TableMath, CrossSection, LineCursors, CropResizeField,
ThresholdMask, MaskMorphology, MaskInvert, MaskCombine,
LoadFile, SaveImage):
cls._current_node_id = node_id

View File

@@ -587,12 +587,18 @@ TABLE_OPS: dict[str, Callable[[np.ndarray], float]] = {
class TableMath:
"""Compute a scalar reduction over one numeric column in a TABLE."""
_broadcast_value_fn = None
_current_node_id: str = ""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"table": ("TABLE",),
"column": ("STRING", {"default": "value"}),
"column": ("STRING", {
"default": "value",
"choices_from_table_input": "table",
}),
"operation": (list(TABLE_OPS.keys()),),
}
}
@@ -618,7 +624,11 @@ class TableMath:
op = TABLE_OPS.get(operation)
if op is None:
raise ValueError(f"Unsupported table operation: {operation}")
return (op(np.asarray(values, dtype=np.float64)),)
result = op(np.asarray(values, dtype=np.float64))
if TableMath._broadcast_value_fn is not None:
TableMath._broadcast_value_fn(TableMath._current_node_id, result)
return (result,)
def _resolve_column_name(self, table: list, column: str) -> str:
requested = str(column or "").strip()

View File

@@ -173,3 +173,29 @@ class PrintTable:
if PrintTable._broadcast_table_fn is not None:
PrintTable._broadcast_table_fn(PrintTable._current_node_id, table)
return ()
@register_node(display_name="Value Display")
class ValueDisplay:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"value": ("FLOAT",),
}
}
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "display_value"
CATEGORY = "display"
DESCRIPTION = "Display a FLOAT in the graph and pass the same value through unchanged."
_broadcast_value_fn = None
_current_node_id: str = ""
def display_value(self, value: float) -> tuple:
numeric = float(value)
if ValueDisplay._broadcast_value_fn is not None:
ValueDisplay._broadcast_value_fn(ValueDisplay._current_node_id, numeric)
return (numeric,)

View File

@@ -16,6 +16,7 @@ WebSocket message types sent to clients
{"type": "executing", "data": {"node": "...", "prompt_id": "..."}}
{"type": "preview", "data": {"node_id": "...", "image": "data:..."}}
{"type": "table", "data": {"node_id": "...", "rows": [...]}}
{"type": "scalar", "data": {"node_id": "...", "value": 1.23}}
{"type": "execution_error", "data": {"node_id": "...", "message": "..."}}
{"type": "execution_complete", "data": {"prompt_id": "..."}}
"""
@@ -114,6 +115,9 @@ def create_app(loop: asyncio.AbstractEventLoop) -> web.Application:
def on_overlay(node_id: str, overlay_data) -> None:
broadcast({"type": "overlay", "data": {"node_id": node_id, "overlay": overlay_data}})
def on_value(node_id: str, value: float) -> None:
broadcast({"type": "scalar", "data": {"node_id": node_id, "value": value}})
def on_warning(node_id: str, message: str) -> None:
broadcast({"type": "node_warning", "data": {"node_id": node_id, "message": message}})
@@ -260,6 +264,7 @@ def create_app(loop: asyncio.AbstractEventLoop) -> web.Application:
on_table=on_table,
on_mesh=on_mesh,
on_overlay=on_overlay,
on_value=on_value,
on_warning=on_warning,
),
)