tidy up old code
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user