rework menu system
This commit is contained in:
@@ -12,18 +12,19 @@ from typing import Any
|
||||
|
||||
|
||||
MENU_LAYOUT: dict[str, list[str]] = {
|
||||
"Add": [
|
||||
"Input": [
|
||||
"Image",
|
||||
"ImageDemo",
|
||||
"Folder",
|
||||
"ColorMap",
|
||||
"Number",
|
||||
"RangeSlider",
|
||||
"Coordinate",
|
||||
"CoordinatePair",
|
||||
"Font",
|
||||
],
|
||||
"Output": [
|
||||
"Display": [
|
||||
"ColorMap",
|
||||
"Font",
|
||||
"ColormapAdjust",
|
||||
"PreviewImage",
|
||||
"ValueDisplay",
|
||||
"View3D",
|
||||
@@ -36,11 +37,10 @@ MENU_LAYOUT: dict[str, list[str]] = {
|
||||
"Annotations",
|
||||
"AngleMeasure",
|
||||
],
|
||||
"Modify": [
|
||||
"Geometry": [
|
||||
"CropResizeField",
|
||||
"RotateField",
|
||||
"FlipField",
|
||||
"ColormapAdjust",
|
||||
],
|
||||
"Filter": [
|
||||
"GaussianFilter",
|
||||
@@ -50,25 +50,31 @@ MENU_LAYOUT: dict[str, list[str]] = {
|
||||
"FFTFilter2D",
|
||||
"ScarRemoval",
|
||||
],
|
||||
"Frequency": [
|
||||
"PSDF",
|
||||
"Spectral": [
|
||||
"FFT2D",
|
||||
"InverseFFT2D",
|
||||
"FFTFilter1D",
|
||||
"FFTFilter2D",
|
||||
"ACF",
|
||||
"PSDF",
|
||||
],
|
||||
"Flatten": [
|
||||
"Level & Correct": [
|
||||
"FixZero",
|
||||
"LineCorrection",
|
||||
"PlaneLevelField",
|
||||
"PolyLevelField",
|
||||
"FacetLevelField",
|
||||
"LineCorrection",
|
||||
"ScarRemoval",
|
||||
],
|
||||
"Measure": [
|
||||
"AngleMeasure",
|
||||
"CrossSection",
|
||||
"Histogram",
|
||||
"Cursors",
|
||||
"Curvature",
|
||||
"FractalDimension",
|
||||
"ACF",
|
||||
"PSDF",
|
||||
"Statistics",
|
||||
"Stats",
|
||||
],
|
||||
@@ -78,12 +84,14 @@ MENU_LAYOUT: dict[str, list[str]] = {
|
||||
"MaskMorphology",
|
||||
"MaskInvert",
|
||||
"MaskOperations",
|
||||
],
|
||||
"Grains": [
|
||||
"GrainAnalysis",
|
||||
"GrainDistanceTransform",
|
||||
"WatershedSegmentation",
|
||||
],
|
||||
"Grains": [
|
||||
"GrainDistanceTransform",
|
||||
"WatershedSegmentation",
|
||||
"GrainAnalysis",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -91,11 +99,17 @@ _CATEGORY_ORDER = {category: index for index, category in enumerate(MENU_LAYOUT)
|
||||
_NODE_METADATA: dict[str, dict[str, Any]] = {}
|
||||
for category, class_names in MENU_LAYOUT.items():
|
||||
for node_order, class_name in enumerate(class_names):
|
||||
_NODE_METADATA[class_name] = {
|
||||
metadata = _NODE_METADATA.setdefault(class_name, {
|
||||
"category": category,
|
||||
"category_order": _CATEGORY_ORDER[category],
|
||||
"menu_order": node_order,
|
||||
}
|
||||
"menu_categories": [],
|
||||
})
|
||||
metadata["menu_categories"].append({
|
||||
"category": category,
|
||||
"category_order": _CATEGORY_ORDER[category],
|
||||
"menu_order": node_order,
|
||||
})
|
||||
|
||||
|
||||
def get_menu_metadata(class_name: str) -> dict[str, Any]:
|
||||
@@ -107,4 +121,9 @@ def get_menu_metadata(class_name: str) -> dict[str, Any]:
|
||||
"category": "Unsorted",
|
||||
"category_order": len(_CATEGORY_ORDER),
|
||||
"menu_order": 10_000,
|
||||
"menu_categories": [{
|
||||
"category": "Unsorted",
|
||||
"category_order": len(_CATEGORY_ORDER),
|
||||
"menu_order": 10_000,
|
||||
}],
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ def get_node_info(class_name: str) -> dict[str, Any]:
|
||||
"category": menu_metadata["category"],
|
||||
"category_order": menu_metadata["category_order"],
|
||||
"menu_order": menu_metadata["menu_order"],
|
||||
"menu_categories": list(menu_metadata.get("menu_categories", [])),
|
||||
"input": input_types,
|
||||
"input_order": {k: list(v.keys()) for k, v in input_types.items()},
|
||||
"output": list(cls.RETURN_TYPES),
|
||||
|
||||
@@ -404,8 +404,16 @@ function canStartCanvasRightDragZoom(target) {
|
||||
}
|
||||
|
||||
function compareMenuNodes(a, b) {
|
||||
const orderA = Number.isFinite(a?.def?.menu_order) ? a.def.menu_order : Number.MAX_SAFE_INTEGER;
|
||||
const orderB = Number.isFinite(b?.def?.menu_order) ? b.def.menu_order : Number.MAX_SAFE_INTEGER;
|
||||
const orderA = Number.isFinite(a?.menu_order)
|
||||
? a.menu_order
|
||||
: Number.isFinite(a?.def?.menu_order)
|
||||
? a.def.menu_order
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
const orderB = Number.isFinite(b?.menu_order)
|
||||
? b.menu_order
|
||||
: Number.isFinite(b?.def?.menu_order)
|
||||
? b.def.menu_order
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
if (orderA !== orderB) return orderA - orderB;
|
||||
|
||||
const nameA = (a?.def?.display_name || a?.className || '').toLowerCase();
|
||||
@@ -615,19 +623,37 @@ function ContextMenu({ x, y, nodeDefs, onAdd, onClose, filterType, filterDirecti
|
||||
if (!hasMatch) continue;
|
||||
}
|
||||
}
|
||||
const cat = def.category || 'uncategorized';
|
||||
if (!cats[cat]) {
|
||||
cats[cat] = {
|
||||
name: cat,
|
||||
order: Number.isFinite(def.category_order) ? def.category_order : Number.MAX_SAFE_INTEGER,
|
||||
items: [],
|
||||
};
|
||||
const menuCategories = Array.isArray(def.menu_categories) && def.menu_categories.length > 0
|
||||
? def.menu_categories
|
||||
: [{
|
||||
category: def.category || 'uncategorized',
|
||||
category_order: def.category_order,
|
||||
menu_order: def.menu_order,
|
||||
}];
|
||||
|
||||
for (const menuCategory of menuCategories) {
|
||||
const cat = menuCategory?.category || def.category || 'uncategorized';
|
||||
if (!cats[cat]) {
|
||||
cats[cat] = {
|
||||
name: cat,
|
||||
order: Number.isFinite(menuCategory?.category_order)
|
||||
? menuCategory.category_order
|
||||
: Number.MAX_SAFE_INTEGER,
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
cats[cat].order = Math.min(
|
||||
cats[cat].order,
|
||||
Number.isFinite(menuCategory?.category_order)
|
||||
? menuCategory.category_order
|
||||
: Number.MAX_SAFE_INTEGER,
|
||||
);
|
||||
cats[cat].items.push({
|
||||
className,
|
||||
def,
|
||||
menu_order: Number.isFinite(menuCategory?.menu_order) ? menuCategory.menu_order : def.menu_order,
|
||||
});
|
||||
}
|
||||
cats[cat].order = Math.min(
|
||||
cats[cat].order,
|
||||
Number.isFinite(def.category_order) ? def.category_order : Number.MAX_SAFE_INTEGER,
|
||||
);
|
||||
cats[cat].items.push({ className, def });
|
||||
}
|
||||
return Object.values(cats)
|
||||
.map((category) => ({
|
||||
@@ -642,10 +668,15 @@ function ContextMenu({ x, y, nodeDefs, onAdd, onClose, filterType, filterDirecti
|
||||
if (!search.trim()) return null;
|
||||
const q = search.toLowerCase();
|
||||
const results = [];
|
||||
const seen = new Set();
|
||||
for (const category of categories) {
|
||||
for (const { className, def } of category.items) {
|
||||
if (seen.has(className)) continue;
|
||||
const name = (def.display_name || className).toLowerCase();
|
||||
if (name.includes(q)) results.push({ className, def });
|
||||
if (name.includes(q)) {
|
||||
results.push({ className, def });
|
||||
seen.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
||||
@@ -33,13 +33,16 @@ export const TYPE_COLORS = {
|
||||
};
|
||||
|
||||
export const CAT_COLORS = {
|
||||
io: '#37474f',
|
||||
filters: '#1a237e',
|
||||
modify: '#0f766e',
|
||||
level: '#1b5e20',
|
||||
analysis: '#4a148c',
|
||||
Input: '#37474f',
|
||||
Display: '#212121',
|
||||
Overlay: '#0f766e',
|
||||
Geometry: '#0d9488',
|
||||
Filter: '#1a237e',
|
||||
Spectral: '#4c1d95',
|
||||
'Level & Correct': '#1b5e20',
|
||||
Measure: '#4a148c',
|
||||
Mask: '#7c2d12',
|
||||
Grains: '#bf360c',
|
||||
display: '#212121',
|
||||
};
|
||||
|
||||
export const SOCKET_COMPATIBILITY = {
|
||||
|
||||
@@ -287,7 +287,7 @@ def test_flip_field():
|
||||
overlays=[markup_overlay, annotation_overlay],
|
||||
)
|
||||
|
||||
assert get_node_info("FlipField")["category"] == "Modify"
|
||||
assert get_node_info("FlipField")["category"] == "Geometry"
|
||||
|
||||
flipped_x, = node.process(field, axis="x")
|
||||
assert np.array_equal(flipped_x.data, np.flipud(data))
|
||||
@@ -569,7 +569,7 @@ def test_facet_level():
|
||||
|
||||
node = FacetLevelField()
|
||||
plane_node = PlaneLevelField()
|
||||
assert get_node_info("FacetLevelField")["category"] == "Flatten"
|
||||
assert get_node_info("FacetLevelField")["category"] == "Level & Correct"
|
||||
|
||||
N = 96
|
||||
yy, xx = np.mgrid[0:N, 0:N]
|
||||
@@ -749,7 +749,7 @@ def test_line_correction():
|
||||
from backend.nodes.line_correction import LineCorrection
|
||||
|
||||
node = LineCorrection()
|
||||
assert get_node_info("LineCorrection")["category"] == "Flatten"
|
||||
assert get_node_info("LineCorrection")["category"] == "Level & Correct"
|
||||
|
||||
rows = 96
|
||||
cols = 128
|
||||
@@ -815,7 +815,9 @@ def test_scar_removal():
|
||||
from backend.nodes.scar_removal import ScarRemoval
|
||||
|
||||
node = ScarRemoval()
|
||||
assert get_node_info("ScarRemoval")["category"] == "Filter"
|
||||
info = get_node_info("ScarRemoval")
|
||||
assert info["category"] == "Filter"
|
||||
assert {entry["category"] for entry in info["menu_categories"]} == {"Filter", "Level & Correct"}
|
||||
|
||||
rows = 96
|
||||
cols = 128
|
||||
@@ -874,7 +876,9 @@ def test_angle_measure():
|
||||
from backend.data_types import ImageData
|
||||
|
||||
node = AngleMeasure()
|
||||
assert get_node_info("AngleMeasure")["category"] == "Overlay"
|
||||
info = get_node_info("AngleMeasure")
|
||||
assert info["category"] == "Overlay"
|
||||
assert {entry["category"] for entry in info["menu_categories"]} == {"Overlay", "Measure"}
|
||||
required_inputs = AngleMeasure.INPUT_TYPES()["required"]
|
||||
optional_inputs = AngleMeasure.INPUT_TYPES().get("optional", {})
|
||||
assert required_inputs["color"][1]["default"] == "#ff9800"
|
||||
|
||||
Reference in New Issue
Block a user