fix value display node
This commit is contained in:
@@ -17,7 +17,6 @@ MENU_LAYOUT: dict[str, list[str]] = {
|
|||||||
"Note",
|
"Note",
|
||||||
"ImageDemo",
|
"ImageDemo",
|
||||||
"Folder",
|
"Folder",
|
||||||
"Number",
|
|
||||||
"RangeSlider",
|
"RangeSlider",
|
||||||
"Coordinate",
|
"Coordinate",
|
||||||
"CoordinatePair",
|
"CoordinatePair",
|
||||||
@@ -27,7 +26,7 @@ MENU_LAYOUT: dict[str, list[str]] = {
|
|||||||
"Font",
|
"Font",
|
||||||
"ColormapAdjust",
|
"ColormapAdjust",
|
||||||
"PreviewImage",
|
"PreviewImage",
|
||||||
"ValueDisplay",
|
"ValueIO",
|
||||||
"View3D",
|
"View3D",
|
||||||
"Save",
|
"Save",
|
||||||
"SaveImage",
|
"SaveImage",
|
||||||
|
|||||||
@@ -43,10 +43,9 @@ from backend.nodes import (
|
|||||||
markup,
|
markup,
|
||||||
preview_image,
|
preview_image,
|
||||||
statistics,
|
statistics,
|
||||||
|
value_io,
|
||||||
view_3d,
|
view_3d,
|
||||||
print_table,
|
print_table,
|
||||||
value_display,
|
|
||||||
# Analysis
|
|
||||||
curvature,
|
curvature,
|
||||||
fractal_dimension,
|
fractal_dimension,
|
||||||
histogram,
|
histogram,
|
||||||
|
|||||||
@@ -154,13 +154,12 @@ def _compute_curvature_results(
|
|||||||
+ coeffs[5] * y_norm * y_norm
|
+ coeffs[5] * y_norm * y_norm
|
||||||
)
|
)
|
||||||
|
|
||||||
r1 = float("inf") if abs(kappa1) <= 1e-14 else float(1.0 / (q * q * kappa1))
|
#todo: fix inf case
|
||||||
r2 = float("inf") if abs(kappa2) <= 1e-14 else float(1.0 / (q * q * kappa2))
|
r1 = float(np.inf) if abs(kappa1) <= 1e-14 else float(1.0 / (q * q * kappa1))
|
||||||
|
r2 = float(np.inf) if abs(kappa2) <= 1e-14 else float(1.0 / (q * q * kappa2))
|
||||||
x0 = float(xc / q + 0.5 * xreal + field.xoff)
|
x0 = float(xc / q + 0.5 * xreal + field.xoff)
|
||||||
y0 = float(yc / q + 0.5 * yreal + field.yoff)
|
y0 = float(yc / q + 0.5 * yreal + field.yoff)
|
||||||
|
|
||||||
print(f"debug: {x0}, {y0}, {r1}, {r2}")
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"degree": float(degree),
|
"degree": float(degree),
|
||||||
"x0": x0,
|
"x0": x0,
|
||||||
@@ -292,8 +291,8 @@ class Curvature:
|
|||||||
OUTPUTS = (
|
OUTPUTS = (
|
||||||
('ANNOTATION_SOURCE', 'output'),
|
('ANNOTATION_SOURCE', 'output'),
|
||||||
('RECORD_TABLE', 'measurements'),
|
('RECORD_TABLE', 'measurements'),
|
||||||
('LINE', 'profile_x'),
|
('LINE', 'profile_a'),
|
||||||
('LINE', 'profile_y'),
|
('LINE', 'profile_b'),
|
||||||
)
|
)
|
||||||
FUNCTION = "process"
|
FUNCTION = "process"
|
||||||
|
|
||||||
@@ -340,7 +339,7 @@ class Curvature:
|
|||||||
|
|
||||||
profiles = []
|
profiles = []
|
||||||
for pair in intersections[:2]:
|
for pair in intersections[:2]:
|
||||||
profiles.append(_profile_from_intersections(field, pair[1], pair[0]))
|
profiles.append(_profile_from_intersections(field, pair[0], pair[1]))
|
||||||
while len(profiles) < 2:
|
while len(profiles) < 2:
|
||||||
profiles.append(_empty_profile(field.si_unit_xy, field.si_unit_z))
|
profiles.append(_empty_profile(field.si_unit_xy, field.si_unit_z))
|
||||||
|
|
||||||
@@ -360,7 +359,7 @@ class Curvature:
|
|||||||
preview_base = render_datafield_preview(field, field.colormap)
|
preview_base = render_datafield_preview(field, field.colormap)
|
||||||
panels = []
|
panels = []
|
||||||
|
|
||||||
for p, title in zip(profiles, ["X Principal Axis", "Y Principal Axis"]):
|
for p, title in zip(profiles, ["Principal Axis A", "Principal Axis B"]):
|
||||||
if len(p.data) > 0:
|
if len(p.data) > 0:
|
||||||
panels.append({
|
panels.append({
|
||||||
"title": title,
|
"title": title,
|
||||||
@@ -376,7 +375,7 @@ class Curvature:
|
|||||||
})
|
})
|
||||||
|
|
||||||
emit_preview({"kind": "panels", "panels": panels})
|
emit_preview({"kind": "panels", "panels": panels})
|
||||||
# emit_table(table)
|
emit_table(table)
|
||||||
|
|
||||||
if warnings:
|
if warnings:
|
||||||
emit_warning(warnings[0])
|
emit_warning(warnings[0])
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ Shared helper functions for argonode nodes.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
@@ -16,6 +17,62 @@ from backend.runtime_paths import demo_dir, input_dir, output_dir
|
|||||||
# Scalar payload helpers (from display.py)
|
# Scalar payload helpers (from display.py)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_SI_PREFIXES: dict[str, float] = {
|
||||||
|
'Y': 1e24, 'Z': 1e21, 'E': 1e18, 'P': 1e15, 'T': 1e12,
|
||||||
|
'G': 1e9, 'M': 1e6, 'k': 1e3,
|
||||||
|
'm': 1e-3, 'u': 1e-6, 'µ': 1e-6, 'n': 1e-9, 'p': 1e-12,
|
||||||
|
'f': 1e-15, 'a': 1e-18, 'z': 1e-21, 'y': 1e-24,
|
||||||
|
}
|
||||||
|
|
||||||
|
_PREFIXABLE_UNITS: frozenset[str] = frozenset({
|
||||||
|
'm', 's', 'A', 'V', 'W', 'Hz', 'F', 'C', 'J', 'N', 'Pa',
|
||||||
|
'T', 'H', 'S', 'g', 'K', 'Ohm', 'ohm', 'Ω',
|
||||||
|
})
|
||||||
|
|
||||||
|
_NUMBER_RE = re.compile(
|
||||||
|
r'^([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)\s*(.*)?$'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_number_with_unit(text: str) -> tuple[float, str]:
|
||||||
|
"""Parse a string like '1.5 nm' into (1.5e-9, 'm').
|
||||||
|
|
||||||
|
The numeric part may use scientific notation. The unit is stripped of any
|
||||||
|
recognised SI prefix and the raw value is scaled accordingly, so the
|
||||||
|
returned float is always in the base SI unit. Units that are not
|
||||||
|
prefixable are returned unchanged alongside the unscaled value.
|
||||||
|
|
||||||
|
Examples::
|
||||||
|
|
||||||
|
parse_number_with_unit("1 um") → (1e-6, "m")
|
||||||
|
parse_number_with_unit("500 nm") → (5e-7, "m")
|
||||||
|
parse_number_with_unit("3.14") → (3.14, "")
|
||||||
|
parse_number_with_unit("2 kHz") → (2000.0, "Hz")
|
||||||
|
"""
|
||||||
|
text = text.strip()
|
||||||
|
if not text:
|
||||||
|
return 0.0, ""
|
||||||
|
|
||||||
|
m = _NUMBER_RE.match(text)
|
||||||
|
if not m:
|
||||||
|
raise ValueError(f"Cannot parse number: {text!r}")
|
||||||
|
|
||||||
|
numeric = float(m.group(1))
|
||||||
|
unit_str = (m.group(2) or "").strip()
|
||||||
|
|
||||||
|
if not unit_str:
|
||||||
|
return numeric, ""
|
||||||
|
|
||||||
|
# Try prefix + base-unit split (handle multi-byte µ as a prefix)
|
||||||
|
if len(unit_str) >= 2:
|
||||||
|
prefix_char = unit_str[0]
|
||||||
|
rest = unit_str[1:]
|
||||||
|
if prefix_char in _SI_PREFIXES and rest in _PREFIXABLE_UNITS:
|
||||||
|
return numeric * _SI_PREFIXES[prefix_char], rest
|
||||||
|
|
||||||
|
return numeric, unit_str
|
||||||
|
|
||||||
|
|
||||||
def _scalar_payload(value: float, unit: str = "") -> dict:
|
def _scalar_payload(value: float, unit: str = "") -> dict:
|
||||||
payload = {"value": float(value)}
|
payload = {"value": float(value)}
|
||||||
if isinstance(unit, str) and unit.strip():
|
if isinstance(unit, str) and unit.strip():
|
||||||
@@ -24,7 +81,7 @@ def _scalar_payload(value: float, unit: str = "") -> dict:
|
|||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Measurement helpers (from display.py — used by ValueDisplay)
|
# Measurement helpers (from display.py — used by ValueIO)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
def _measurement_names(table: list) -> list[str]:
|
def _measurement_names(table: list) -> list[str]:
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ from __future__ import annotations
|
|||||||
from backend.node_registry import register_node
|
from backend.node_registry import register_node
|
||||||
from backend.execution_context import emit_table, emit_value
|
from backend.execution_context import emit_table, emit_value
|
||||||
from backend.data_types import RecordTable
|
from backend.data_types import RecordTable
|
||||||
from backend.nodes.helpers import _measurement_entry, _measurement_value, _scalar_payload
|
from backend.nodes.helpers import _measurement_entry, _measurement_value, _scalar_payload, parse_number_with_unit
|
||||||
|
|
||||||
|
|
||||||
@register_node(display_name="Value Display")
|
@register_node(display_name="Value Display")
|
||||||
class ValueDisplay:
|
class ValueIO:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"value": ("FLOAT", {
|
"number_input": ("STRING", {
|
||||||
"accepted_types": ["RECORD_TABLE"],
|
"text_input": True,
|
||||||
"socket_only": True,
|
"default": "0",
|
||||||
|
"placeholder": "e.g. 1.5 nm",
|
||||||
|
"hide_when_input_connected": "value",
|
||||||
|
"hide_label": True,
|
||||||
}),
|
}),
|
||||||
"measurement": ("STRING", {
|
"measurement": ("STRING", {
|
||||||
"default": "",
|
"default": "",
|
||||||
@@ -22,7 +25,13 @@ class ValueDisplay:
|
|||||||
"value": ["RECORD_TABLE"],
|
"value": ["RECORD_TABLE"],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}
|
},
|
||||||
|
"optional": {
|
||||||
|
"value": ("FLOAT", {
|
||||||
|
"accepted_types": ["RECORD_TABLE"],
|
||||||
|
"socket_only": True,
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
OUTPUTS = (
|
OUTPUTS = (
|
||||||
@@ -35,14 +44,16 @@ class ValueDisplay:
|
|||||||
_broadcast_value_fn = None
|
_broadcast_value_fn = None
|
||||||
_current_node_id: str = ""
|
_current_node_id: str = ""
|
||||||
|
|
||||||
def display_value(self, value, measurement: str = "") -> tuple:
|
def display_value(self, number_input: str = "0", value=None, measurement: str = "") -> tuple:
|
||||||
unit = ""
|
unit = ""
|
||||||
if isinstance(value, RecordTable):
|
if isinstance(value, RecordTable):
|
||||||
emit_table(value)
|
emit_table(value)
|
||||||
row = _measurement_entry(value, measurement)
|
row = _measurement_entry(value, measurement)
|
||||||
numeric = _measurement_value(value, measurement)
|
numeric = _measurement_value(value, measurement)
|
||||||
unit = row.get("unit", "") if isinstance(row.get("unit"), str) else ""
|
unit = row.get("unit", "") if isinstance(row.get("unit"), str) else ""
|
||||||
else:
|
elif value is not None:
|
||||||
numeric = float(value)
|
numeric = float(value)
|
||||||
|
else:
|
||||||
|
numeric, unit = parse_number_with_unit(str(number_input))
|
||||||
emit_value(_scalar_payload(numeric, unit))
|
emit_value(_scalar_payload(numeric, unit))
|
||||||
return (numeric,)
|
return (numeric,)
|
||||||
@@ -31,6 +31,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@@ -205,6 +206,10 @@ def create_app(
|
|||||||
value = payload
|
value = payload
|
||||||
unit = ""
|
unit = ""
|
||||||
|
|
||||||
|
# JSON cannot encode non-finite floats; convert to string representations.
|
||||||
|
if isinstance(value, float) and not math.isfinite(value):
|
||||||
|
value = "∞" if value > 0 else ("-∞" if math.isinf(value) else "NaN")
|
||||||
|
|
||||||
data = {"node_id": node_id, "value": value}
|
data = {"node_id": node_id, "value": value}
|
||||||
if isinstance(unit, str) and unit.strip():
|
if isinstance(unit, str) and unit.strip():
|
||||||
data["unit"] = unit.strip()
|
data["unit"] = unit.strip()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import { getGroupMinimumSize } from './groupSizing.js';
|
import { getGroupMinimumSize } from './groupSizing.js';
|
||||||
import { buildCombinedInputNameByWidgetName, formatUiLabel } from './nodeWidgetLayout.js';
|
import { buildCombinedInputNameByWidgetName, formatUiLabel } from './nodeWidgetLayout.js';
|
||||||
import { applySIPrefix, formatNumericCell, formatTableRowCell, getTableColumns } from './valueFormatting.js';
|
import { applySIPrefix, formatNumericCell, formatTableRowCell, getTableColumns, parseNumberWithUnit } from './valueFormatting.js';
|
||||||
|
|
||||||
// ── Context (provided by App) ─────────────────────────────────────────
|
// ── Context (provided by App) ─────────────────────────────────────────
|
||||||
|
|
||||||
@@ -443,7 +443,11 @@ function getScalarPayload(scalarValue) {
|
|||||||
return Number.isFinite(scalarValue) ? { value: scalarValue, unit: '' } : null;
|
return Number.isFinite(scalarValue) ? { value: scalarValue, unit: '' } : null;
|
||||||
}
|
}
|
||||||
if (!scalarValue || typeof scalarValue !== 'object') return null;
|
if (!scalarValue || typeof scalarValue !== 'object') return null;
|
||||||
const numeric = Number(scalarValue.value);
|
const raw = scalarValue.value;
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
return { valueText: raw, unitText: typeof scalarValue.unit === 'string' ? scalarValue.unit : '' };
|
||||||
|
}
|
||||||
|
const numeric = Number(raw);
|
||||||
if (!Number.isFinite(numeric)) return null;
|
if (!Number.isFinite(numeric)) return null;
|
||||||
return {
|
return {
|
||||||
value: numeric,
|
value: numeric,
|
||||||
@@ -454,6 +458,7 @@ function getScalarPayload(scalarValue) {
|
|||||||
function formatScalarDisplay(scalarValue) {
|
function formatScalarDisplay(scalarValue) {
|
||||||
const payload = getScalarPayload(scalarValue);
|
const payload = getScalarPayload(scalarValue);
|
||||||
if (!payload) return null;
|
if (!payload) return null;
|
||||||
|
if ('valueText' in payload) return payload;
|
||||||
|
|
||||||
if (payload.unit) {
|
if (payload.unit) {
|
||||||
const prefixed = applySIPrefix(payload.value, payload.unit);
|
const prefixed = applySIPrefix(payload.value, payload.unit);
|
||||||
@@ -1471,6 +1476,55 @@ function CustomNode({ id, data }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Editable value-box for text_input FLOAT widgets ──────────────────
|
||||||
|
|
||||||
|
function TextInputValueBox({ val, placeholder, nodeId, name, label, hideLabel, onChange }) {
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const parsed = parseNumberWithUnit(val);
|
||||||
|
const display = parsed ? formatScalarDisplay({ value: parsed.numeric, unit: parsed.unit }) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!hideLabel && <label>{label}</label>}
|
||||||
|
<div
|
||||||
|
className="node-value-box nodrag"
|
||||||
|
style={{ cursor: editing ? 'text' : 'pointer' }}
|
||||||
|
onClick={() => !editing && setEditing(true)}
|
||||||
|
>
|
||||||
|
{editing ? (
|
||||||
|
<input
|
||||||
|
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||||
|
autoFocus
|
||||||
|
className="nodrag"
|
||||||
|
type="text"
|
||||||
|
value={val}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={(e) => onChange(nodeId, name, e.target.value)}
|
||||||
|
onBlur={() => setEditing(false)}
|
||||||
|
style={{
|
||||||
|
background: 'transparent',
|
||||||
|
border: 'none',
|
||||||
|
outline: 'none',
|
||||||
|
color: 'inherit',
|
||||||
|
font: 'inherit',
|
||||||
|
textAlign: 'center',
|
||||||
|
width: '100%',
|
||||||
|
padding: 0,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : display ? (
|
||||||
|
<>
|
||||||
|
<span className="node-value-box-number">{display.valueText}</span>
|
||||||
|
{display.unitText && <span className="node-value-box-unit">{display.unitText}</span>}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span className="node-value-box-number" style={{ opacity: 0.4 }}>{placeholder || '0'}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Widget renderer ───────────────────────────────────────────────────
|
// ── Widget renderer ───────────────────────────────────────────────────
|
||||||
|
|
||||||
function WidgetControl({ widget, nodeId, value, widgetValues, onChange, openFileBrowser, hideLabel = false, measurementChoices }) {
|
function WidgetControl({ widget, nodeId, value, widgetValues, onChange, openFileBrowser, hideLabel = false, measurementChoices }) {
|
||||||
@@ -1701,6 +1755,20 @@ function WidgetControl({ widget, nodeId, value, widgetValues, onChange, openFile
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts?.text_input) {
|
||||||
|
return (
|
||||||
|
<TextInputValueBox
|
||||||
|
val={val}
|
||||||
|
placeholder={placeholder || opts?.placeholder || ''}
|
||||||
|
nodeId={nodeId}
|
||||||
|
name={name}
|
||||||
|
label={label}
|
||||||
|
hideLabel={hideLabel || !!opts.hide_label}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'FLOAT') {
|
if (type === 'FLOAT') {
|
||||||
if (opts?.slider) {
|
if (opts?.slider) {
|
||||||
const rawMin = opts?.min_widget ? widgetValues?.[opts.min_widget] : opts?.min;
|
const rawMin = opts?.min_widget ? widgetValues?.[opts.min_widget] : opts?.min;
|
||||||
|
|||||||
@@ -1,3 +1,41 @@
|
|||||||
|
const SI_PREFIX_MULTIPLIERS = {
|
||||||
|
Y: 1e24, Z: 1e21, E: 1e18, P: 1e15, T: 1e12,
|
||||||
|
G: 1e9, M: 1e6, k: 1e3,
|
||||||
|
m: 1e-3, u: 1e-6, µ: 1e-6, n: 1e-9, p: 1e-12,
|
||||||
|
f: 1e-15, a: 1e-18, z: 1e-21, y: 1e-24,
|
||||||
|
};
|
||||||
|
|
||||||
|
const NUMBER_WITH_UNIT_RE = /^([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)\s*(.*)?$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a string like "1.5 nm" into { numeric: 1.5e-9, unit: "m" }.
|
||||||
|
* Returns null if the string does not start with a valid number.
|
||||||
|
* The numeric value is scaled to the base SI unit via the prefix.
|
||||||
|
*/
|
||||||
|
export function parseNumberWithUnit(text) {
|
||||||
|
const s = String(text ?? '').trim();
|
||||||
|
if (!s) return { numeric: 0, unit: '' };
|
||||||
|
|
||||||
|
const m = s.match(NUMBER_WITH_UNIT_RE);
|
||||||
|
if (!m) return null;
|
||||||
|
|
||||||
|
const numeric = parseFloat(m[1]);
|
||||||
|
const unitStr = (m[2] ?? '').trim();
|
||||||
|
|
||||||
|
if (!unitStr) return { numeric, unit: '' };
|
||||||
|
|
||||||
|
if (unitStr.length >= 2) {
|
||||||
|
const prefix = unitStr[0];
|
||||||
|
const rest = unitStr.slice(1);
|
||||||
|
const multiplier = SI_PREFIX_MULTIPLIERS[prefix];
|
||||||
|
if (multiplier !== undefined && PREFIXABLE_UNITS.has(rest)) {
|
||||||
|
return { numeric: numeric * multiplier, unit: rest };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { numeric, unit: unitStr };
|
||||||
|
}
|
||||||
|
|
||||||
const SI_PREFIXES = [
|
const SI_PREFIXES = [
|
||||||
{ exp: -24, prefix: 'y' },
|
{ exp: -24, prefix: 'y' },
|
||||||
{ exp: -21, prefix: 'z' },
|
{ exp: -21, prefix: 'z' },
|
||||||
|
|||||||
@@ -2017,17 +2017,17 @@ def test_print_table():
|
|||||||
|
|
||||||
|
|
||||||
def test_value_display():
|
def test_value_display():
|
||||||
print("=== Test: ValueDisplay ===")
|
print("=== Test: ValueIO ===")
|
||||||
from backend.nodes.value_display import ValueDisplay
|
from backend.nodes.value_io import ValueIO
|
||||||
|
|
||||||
node = ValueDisplay()
|
node = ValueIO()
|
||||||
value_spec = ValueDisplay.INPUT_TYPES()["required"]["value"]
|
value_spec = ValueIO.INPUT_TYPES()["required"]["value"]
|
||||||
assert value_spec[0] == "FLOAT"
|
assert value_spec[0] == "FLOAT"
|
||||||
assert value_spec[1]["accepted_types"] == ["RECORD_TABLE"]
|
assert value_spec[1]["accepted_types"] == ["RECORD_TABLE"]
|
||||||
|
|
||||||
captured = []
|
captured = []
|
||||||
ValueDisplay._broadcast_value_fn = lambda node_id, payload: captured.append((node_id, payload))
|
ValueIO._broadcast_value_fn = lambda node_id, payload: captured.append((node_id, payload))
|
||||||
ValueDisplay._current_node_id = "test"
|
ValueIO._current_node_id = "test"
|
||||||
|
|
||||||
result = node.display_value(3.25)
|
result = node.display_value(3.25)
|
||||||
assert result == (3.25,)
|
assert result == (3.25,)
|
||||||
@@ -2041,7 +2041,7 @@ def test_value_display():
|
|||||||
assert result == (1.7e-7,)
|
assert result == (1.7e-7,)
|
||||||
assert captured[-1] == ("test", {"value": 1.7e-7, "unit": "m"})
|
assert captured[-1] == ("test", {"value": 1.7e-7, "unit": "m"})
|
||||||
|
|
||||||
ValueDisplay._broadcast_value_fn = None
|
ValueIO._broadcast_value_fn = None
|
||||||
print(" PASS\n")
|
print(" PASS\n")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user