Implement recursive Interface in vue
This commit is contained in:
parent
a11f9b7f62
commit
9f2cde2275
6 changed files with 167 additions and 57 deletions
|
|
@ -4,8 +4,6 @@ This module contains the base ``ScanWorkflow`` class that all workflows should s
|
|||
as well as specific workflows.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import (
|
||||
Generic,
|
||||
|
|
@ -41,7 +39,12 @@ from openflexure_microscope_server.things.background_detect import (
|
|||
from openflexure_microscope_server.things.camera import BaseCamera, CaptureParams
|
||||
from openflexure_microscope_server.things.camera_stage_mapping import CameraStageMapper
|
||||
from openflexure_microscope_server.things.stage import BaseStage
|
||||
from openflexure_microscope_server.ui import PropertyControl, property_control_for
|
||||
from openflexure_microscope_server.ui import (
|
||||
UI_ELEMENT_RESPONSE,
|
||||
Accordion,
|
||||
UIElementList,
|
||||
property_control_for,
|
||||
)
|
||||
|
||||
SettingModelType = TypeVar("SettingModelType", bound=BaseModel)
|
||||
|
||||
|
|
@ -160,9 +163,9 @@ class ScanWorkflow(Generic[SettingModelType], lt.Thing):
|
|||
|
||||
return True, focus_height
|
||||
|
||||
@lt.property
|
||||
def settings_ui(self) -> list[PropertyControl]:
|
||||
"""A list of PropertyControl objects to create the settings in the scan tab."""
|
||||
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||
def settings_ui(self) -> UIElementList:
|
||||
"""Return the UI for the workflow's settings in the scan tab."""
|
||||
raise NotImplementedError(
|
||||
"Each scan workflow must implement a settings_ui method."
|
||||
)
|
||||
|
|
@ -556,35 +559,43 @@ class HistoScanWorkflow(RectGridWorkflow[HistoScanSettingsModel]):
|
|||
|
||||
return imaged, focus_height
|
||||
|
||||
@lt.property
|
||||
def settings_ui(self) -> list[PropertyControl]:
|
||||
"""A list of PropertyControl objects to create the settings in the scan tab."""
|
||||
return [
|
||||
property_control_for(
|
||||
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
||||
),
|
||||
property_control_for(
|
||||
self, "stack_images_to_save", label="Images in Stack to Save"
|
||||
),
|
||||
property_control_for(
|
||||
self,
|
||||
"stack_min_images_to_test",
|
||||
label="Minimum number of images to test for focus",
|
||||
),
|
||||
property_control_for(self, "stack_dz", label="Stack dz (steps)", step=5),
|
||||
property_control_for(
|
||||
self, "autofocus_dz", label="Autofocus Range (steps)", step=200
|
||||
),
|
||||
property_control_for(
|
||||
self, "max_range", label="Maximum Distance (steps)", step=1000
|
||||
),
|
||||
property_control_for(
|
||||
self, "skip_background", label="Detect and Skip Empty Fields"
|
||||
),
|
||||
property_control_for(
|
||||
self, "equal_distances", label="Set Equal x and y Distances"
|
||||
),
|
||||
]
|
||||
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||
def settings_ui(self) -> UIElementList:
|
||||
"""Return the UI for the workflow's settings in the scan tab."""
|
||||
return UIElementList(
|
||||
[
|
||||
Accordion(
|
||||
title="Background Detect",
|
||||
children=self._background_detector.settings_ui,
|
||||
),
|
||||
property_control_for(
|
||||
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
||||
),
|
||||
property_control_for(
|
||||
self, "stack_images_to_save", label="Images in Stack to Save"
|
||||
),
|
||||
property_control_for(
|
||||
self,
|
||||
"stack_min_images_to_test",
|
||||
label="Minimum number of images to test for focus",
|
||||
),
|
||||
property_control_for(
|
||||
self, "stack_dz", label="Stack dz (steps)", step=5
|
||||
).model_dump(),
|
||||
property_control_for(
|
||||
self, "autofocus_dz", label="Autofocus Range (steps)", step=200
|
||||
),
|
||||
property_control_for(
|
||||
self, "max_range", label="Maximum Distance (steps)", step=1000
|
||||
),
|
||||
property_control_for(
|
||||
self, "skip_background", label="Detect and Skip Empty Fields"
|
||||
),
|
||||
property_control_for(
|
||||
self, "equal_distances", label="Set Equal x and y Distances"
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class RegularGridSettingsModel(RectGridSettingsModel):
|
||||
|
|
@ -668,17 +679,21 @@ class RegularGridWorkflow(RectGridWorkflow[RegularGridSettingsModel]):
|
|||
save_resolution=settings.capture_params.save_resolution,
|
||||
)
|
||||
|
||||
@lt.property
|
||||
def settings_ui(self) -> list[PropertyControl]:
|
||||
"""A list of PropertyControl objects to create the settings in the scan tab."""
|
||||
return [
|
||||
property_control_for(
|
||||
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
||||
),
|
||||
property_control_for(self, "x_count", label="Number of columns"),
|
||||
property_control_for(self, "y_count", label="Number of rows"),
|
||||
property_control_for(self, "autofocus_dz", label="Autofocus Range (steps)"),
|
||||
]
|
||||
@lt.endpoint("get", "settings_ui", responses=UI_ELEMENT_RESPONSE)
|
||||
def settings_ui(self) -> UIElementList:
|
||||
"""Return the UI for the workflow's settings in the scan tab."""
|
||||
return UIElementList(
|
||||
[
|
||||
property_control_for(
|
||||
self, "overlap", label="Image Overlap (0.1-0.7)", step=0.05
|
||||
),
|
||||
property_control_for(self, "x_count", label="Number of columns"),
|
||||
property_control_for(self, "y_count", label="Number of rows"),
|
||||
property_control_for(
|
||||
self, "autofocus_dz", label="Autofocus Range (steps)"
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class SnakeWorkflow(RegularGridWorkflow):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from html import escape
|
|||
from html.parser import HTMLParser
|
||||
from typing import Annotated, Any, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, BeforeValidator
|
||||
from pydantic import BaseModel, BeforeValidator, RootModel
|
||||
|
||||
import labthings_fastapi as lt
|
||||
|
||||
|
|
@ -268,6 +268,28 @@ class Container(BaseModel):
|
|||
UIElementModels = HTMLBlock | PropertyControl | ActionButton | Accordion | Container
|
||||
|
||||
|
||||
class UIElementList(RootModel[list[UIElementModels]]):
|
||||
"""A list of user interface elements.
|
||||
|
||||
Note! Two important things to consider:
|
||||
|
||||
* This cannot be the return of a lt.property. To fully describe the UI recursion
|
||||
is used. Web of Things DataSchema doesn't support rectursion. Use a
|
||||
lt.endpoint instead
|
||||
* If the module using this class imoports future annotations then this will cause
|
||||
havoc with pydantics forward references.
|
||||
"""
|
||||
|
||||
|
||||
# Resolve forward references so Pydantic can build a sensible recursive schema
|
||||
Accordion.model_rebuild()
|
||||
Container.model_rebuild()
|
||||
UIElementList.model_rebuild()
|
||||
|
||||
# This constant can be used as the value for the `responses` argument of lt.endpoint.
|
||||
UI_ELEMENT_RESPONSE = {
|
||||
200: {
|
||||
"description": "A list of UI element specifications.",
|
||||
"model": UIElementList,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue