tidy up old code

This commit is contained in:
2026-04-02 00:40:09 -07:00
parent 6bfa295d25
commit 7de9bddec7
39 changed files with 219 additions and 533 deletions

View File

@@ -2,7 +2,6 @@ from __future__ import annotations
from contextlib import contextmanager
from contextvars import ContextVar
import inspect
from typing import Any, Callable
Callback = Callable[[str, Any], None]
@@ -13,16 +12,6 @@ _callbacks_var: ContextVar[dict[str, Callback | None]] = ContextVar(
)
_node_id_var: ContextVar[str | None] = ContextVar("tono_execution_node_id", default=None)
_LEGACY_CALLBACK_ATTRS = {
"preview": "_broadcast_fn",
"table": "_broadcast_table_fn",
"mesh": "_broadcast_mesh_fn",
"overlay": "_broadcast_overlay_fn",
"value": "_broadcast_value_fn",
"warning": "_broadcast_warning_fn",
"file_download": "_broadcast_file_download_fn",
}
@contextmanager
def execution_callbacks(
@@ -63,42 +52,12 @@ def current_node_id() -> str | None:
return _node_id_var.get()
def _legacy_emit(kind: str, payload: Any) -> bool:
callback_attr = _LEGACY_CALLBACK_ATTRS.get(kind)
if not callback_attr:
return False
frame = inspect.currentframe()
try:
frame = frame.f_back
while frame is not None:
for owner_name in ("self", "cls"):
owner = frame.f_locals.get(owner_name)
if owner is None:
continue
candidate = owner if isinstance(owner, type) else owner.__class__
callback = getattr(candidate, callback_attr, None)
node_id = getattr(candidate, "_current_node_id", None)
if callback is not None and node_id:
callback(str(node_id), payload)
return True
frame = frame.f_back
finally:
del frame
return False
def _emit(kind: str, payload: Any) -> None:
callbacks = _callbacks_var.get()
callback = callbacks.get(kind)
node_id = current_node_id()
if callback is not None and node_id:
callback(node_id, payload)
return
_legacy_emit(kind, payload)
def emit_preview(payload: Any) -> None:

View File

@@ -16,9 +16,6 @@ from backend.data_types import (
@register_node(display_name="Annotations")
class Annotations:
_broadcast_warning_fn = None
_current_node_id: str = ""
@classmethod
def INPUT_TYPES(cls):
return {

View File

@@ -37,9 +37,6 @@ class CropResizeField:
"resizing preserves the cropped physical size."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self,
field: DataField,

View File

@@ -39,9 +39,6 @@ class CrossSection:
"Equivalent to gwy_data_field_get_profile."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self, field: DataField,
x1: float, y1: float, x2: float, y2: float,

View File

@@ -39,9 +39,6 @@ class Cursors:
"On fields it reports x/y/z at both markers plus dx/dy/dz."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self, line, x1: float, y1: float, x2: float, y2: float,
coord_pair=None,

View File

@@ -30,9 +30,6 @@ class FFT1D:
"Returns the FFT spectrum of the line, and identifies peaks."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self, profile,
) -> tuple:

View File

@@ -122,84 +122,6 @@ def _measurement_value(table: list, selection: str) -> float:
raise ValueError(f"Measurement '{row.get('quantity', selection)}' does not have a numeric value.")
# ---------------------------------------------------------------------------
# SI formatting helpers (from display.py — used by Annotations)
# ---------------------------------------------------------------------------
_SI_PREFIXES = [
(1e24, "Y"), (1e21, "Z"), (1e18, "E"), (1e15, "P"), (1e12, "T"),
(1e9, "G"), (1e6, "M"), (1e3, "k"), (1.0, ""), (1e-3, "m"),
(1e-6, "u"), (1e-9, "n"), (1e-12, "p"), (1e-15, "f"),
(1e-18, "a"), (1e-21, "z"), (1e-24, "y"),
]
_PREFIXABLE_UNITS = {"m", "s", "A", "V", "W", "Hz", "F", "C", "J", "N", "Pa", "T", "H", "S", "g", "K", "Ohm", "ohm", "\u03a9"}
def _format_numeric(value: float) -> str:
if not np.isfinite(value):
return str(value)
abs_value = abs(value)
if abs_value == 0:
return "0"
if abs_value >= 1e4 or abs_value < 1e-3:
return f"{value:.3e}"
return f"{value:.4g}"
def _format_with_unit(value: float, unit: str) -> str:
unit = (unit or "").strip()
if not unit:
return _format_numeric(value)
if unit in _PREFIXABLE_UNITS and np.isfinite(value) and value != 0:
abs_value = abs(value)
for scale, prefix in _SI_PREFIXES:
scaled = abs_value / scale
if 1 <= scaled < 1000:
signed = value / scale
return f"{_format_numeric(signed)} {prefix}{unit}"
return f"{_format_numeric(value)} {unit}"
def _nice_length(target: float) -> float:
if not np.isfinite(target) or target <= 0:
return 0.0
exponent = np.floor(np.log10(target))
base = 10.0 ** exponent
for step in (5.0, 2.0, 1.0):
candidate = step * base
if candidate <= target:
return candidate
return base
def _display_value_range(field) -> tuple[float, float, float]:
data = np.asarray(field.data, dtype=np.float64)
dmin = float(data.min())
dmax = float(data.max())
if not np.isfinite(dmin) or not np.isfinite(dmax) or dmax <= dmin:
return dmin, dmin, dmin
offset = float(field.display_offset)
scale = float(field.display_scale)
if not np.isfinite(offset):
offset = 0.0
if not np.isfinite(scale) or scale <= 0.0:
scale = 1.0
low_norm = float(np.clip(offset, 0.0, 1.0))
high_norm = float(np.clip(offset + scale, 0.0, 1.0))
if high_norm < low_norm:
low_norm, high_norm = high_norm, low_norm
mid_norm = 0.5 * (low_norm + high_norm)
span = dmax - dmin
return (
dmin + low_norm * span,
dmin + mid_norm * span,
dmin + high_norm * span,
)
def _render_annotation_text(text: str, size_px: int, color: tuple[int, int, int]):
from PIL import Image, ImageDraw, ImageFont

View File

@@ -34,9 +34,6 @@ class Histogram:
"Equivalent to gwy_data_field_dh."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self,
field: DataField,

View File

@@ -36,9 +36,6 @@ class Image:
"Images (.png, .tiff, .jpg) and arrays (.npy, .npz) are loaded as uncalibrated fields."
)
_broadcast_warning_fn = None
_current_node_id = None
def load(self, filename: str = "", colormap: str = "viridis", colormap_map=None, path: str | None = None):
selected_path = str(path).strip() if path is not None else str(filename).strip()
if not selected_path:

View File

@@ -26,9 +26,6 @@ class ImageDemo:
DESCRIPTION = "Load a bundled demo file so you can try the app without providing your own data."
_broadcast_warning_fn = None
_current_node_id = None
def load(self, name: str = "", colormap: str = "viridis", colormap_map=None):
from backend.nodes.image import Image
loader = Image()

View File

@@ -43,9 +43,6 @@ class Markup:
"or rasterize markup directly onto an IMAGE."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(
self,
input,

View File

@@ -33,9 +33,6 @@ class DrawMask:
"and invert flips the final binary output."
)
_broadcast_overlay_fn = None
_current_node_id: str = ""
def process(self, field: DataField, pen_size: int, invert: bool, mask_paths: str) -> tuple:
strokes = _parse_mask_strokes(mask_paths)
mask = _rasterize_mask(field.xres, field.yres, strokes, pen_size)

View File

@@ -28,9 +28,6 @@ class MaskInvert:
DESCRIPTION = "Invert a binary mask — swap masked and unmasked regions."
_broadcast_fn = None
_current_node_id: str = ""
def process(self, mask: np.ndarray, field: DataField | None = None) -> tuple:
out = np.where(mask > 127, np.uint8(0), np.uint8(255))

View File

@@ -41,9 +41,6 @@ class MaskMorphology:
"Equivalent to Gwyddion mask_morph."
)
_broadcast_fn = None
_current_node_id: str = ""
def process(self, mask: np.ndarray, operation: str, radius: int, shape: str,
field: DataField | None = None) -> tuple:
from scipy.ndimage import binary_closing, binary_dilation, binary_erosion, binary_opening

View File

@@ -33,9 +33,6 @@ class ThresholdMask:
"Equivalent to Gwyddion's threshold and otsu_threshold modules."
)
_broadcast_fn = None
_current_node_id: str = ""
def process(self, field: DataField, method: str, threshold: float, direction: str) -> tuple:
data = field.data

View File

@@ -36,9 +36,6 @@ class PreviewImage:
OUTPUT_NODE = True
DESCRIPTION = "Display an IMAGE or DATA_FIELD as a coloured thumbnail."
_broadcast_fn = None
_current_node_id: str = ""
def preview(
self,
colormap: str,

View File

@@ -21,9 +21,6 @@ class PrintTable:
OUTPUT_NODE = True
DESCRIPTION = "Send a measurement or record table to the browser as a WebSocket message for display."
_broadcast_table_fn = None
_current_node_id: str = ""
def print_table(self, table: list) -> tuple:
emit_table(table)
return ()

View File

@@ -28,9 +28,6 @@ class RotateField:
"Optionally expand the canvas to keep the full rotated field while preserving the field center."
)
_broadcast_warning_fn = None
_current_node_id: str = ""
def process(
self,
field: DataField,

View File

@@ -74,9 +74,6 @@ class Save:
"Save a single graph value to disk. Supports fields, images, lines, tables, scalars, and 3D meshes."
)
_broadcast_warning_fn = None
_current_node_id = None
def save(
self,
filename: str,

View File

@@ -63,9 +63,6 @@ class SaveImage:
"Click Save to write (does not auto-run)."
)
_broadcast_warning_fn = None
_current_node_id = None
def save(
self,
filename: str,
@@ -187,5 +184,3 @@ class SaveImage:
def _send_warning(self, message: str):
emit_warning(message)
return ()

View File

@@ -19,9 +19,6 @@ from backend.nodes.helpers import (
class Stats:
"""Polymorphic scalar stats node for LINE, DATA_TABLE, DATA_FIELD, or IMAGE inputs."""
_broadcast_value_fn = None
_current_node_id: str = ""
@classmethod
def INPUT_TYPES(cls):
return {

View File

@@ -41,9 +41,6 @@ class ValueIO:
DESCRIPTION = "Display a FLOAT, or a selected numeric row from a measurement table, and pass the value through unchanged."
_broadcast_value_fn = None
_current_node_id: str = ""
def display_value(self, number_input: str = "0", value=None, measurement: str = "") -> tuple:
unit = ""
if isinstance(value, RecordTable):

View File

@@ -148,9 +148,6 @@ class View3D:
"Drag to rotate, middle-drag to pan, and right-drag or scroll to zoom. z_scale exaggerates height."
)
_broadcast_mesh_fn = None
_current_node_id: str = ""
def render(
self, field: DataField,
colormap: str, z_scale: float, resolution: int, make_solid: bool = False,