NiceWidgets widget API notes¶
Per-widget narrative guides and auto-generated class docs live under Widgets. This page keeps cross-cutting contracts consumers rely on (update policy, context menus, hosting rules).
For empty plots / collapsed grids, start with Layout and sizing.
Purpose¶
NiceWidgets provides reusable NiceGUI widgets for scientific and desktop applications. The widgets should be framework-level building blocks, not application-specific orchestration.
CloudScope is one known consumer. This document records the public widget contracts consumers rely on.
Design rule:
nicewidgets owns reusable UI behavior.
applications own orchestration.
domain packages own domain interpretation.
PlotlyRasterViewer¶
Responsibilities:
- display 2D raster/image data
- handle Plotly/NiceGUI integration
- provide efficient image updates
- expose public ROI overlay APIs
- expose future axis range callbacks for linked views
External callers should call public methods only. They should not mutate internal Plotly dictionaries directly except through the widget API.
Plotly update policy¶
Full plotly.update() is expensive because it can push the full plot data/image payload. Use it only for full figure rebuilds.
For ROI-only changes, use browser-side incremental updates such as:
Plotly.relayout(..., {shapes: [...]})
The widget should own this implementation detail.
External callers should simply call:
viewer.set_rois(...)
viewer.select_roi(...)
viewer.add_roi(...)
viewer.delete_roi(...)
Plotly raster viewer context menu¶
The Plotly raster viewer owns its right-click menu and exposes display toggles through public widget APIs. Callers may provide initial display defaults with:
PlotlyRasterViewer(
display_options=PlotlyRasterViewerDisplayOptions(
show_plotly_toolbar=False,
show_rois=True,
show_trace_overlays=True,
show_axis_labels=False,
theme='light',
)
)
The context menu currently provides:
- ROIs
- Traces
- Axis Labels
- Plotly Toolbar
- Copy To Clipboard
Checked items are prefixed with ✓. Visibility toggles must not delete source widget state. ROI visibility is applied through Plotly shape visible; trace overlay visibility is applied through Plotly trace visible; axis display is updated through Plotly.relayout and includes title text, tick labels, tick marks, axis line, zero line, and grid lines; toolbar visibility is updated through Plotly config. Plotly light/dark theme changes are also applied through Plotly.relayout without rebuilding the raster image.
Public display API:
set_roi_overlays_visible(visible: bool) -> None
set_trace_overlays_visible(visible: bool) -> None
set_axis_labels_visible(visible: bool) -> None
set_plotly_toolbar_visible(visible: bool) -> None
set_theme(theme: Literal['light', 'dark']) -> None
set_dark_mode(enabled: bool) -> None
copy_plot_to_clipboard() -> None
ROI overlay API¶
Current/desired public API:
set_rois(rois: Sequence[RectRoiOverlay]) -> None
select_roi(roi_id: int | None) -> None
add_roi(roi: RectRoiOverlay) -> None
delete_roi(roi_id: int) -> None
ROI shapes should be stored in Plotly layout shapes and identified with:
name = "roi:<roi_id>"
ROI style should be configured centrally, not scattered:
RectRoiStyleConfig(
line_width=..., line_color=..., fill_color=...,
selected_line_width=..., selected_line_color=..., selected_fill_color=...,
)
ROI overlay support currently targets rectangular ROIs only. Line-segment ROI display can be added later with separate shape/model APIs.
ROI overlay performance rule¶
ROI-only updates should:
- mutate layout.shapes in the widget's local plotly dict
- call Plotly.relayout for shapes only
- avoid NiceGUI plotly.update()
This rule prevents expensive redraws and avoids resending large image payloads.
Future ROI editing API¶
Planned editing flow:
application enters ROI edit mode
PlotlyRasterViewer enables selected shape editing
user drags/resizes shape
widget emits preview bounds
application controller stores pending bounds
toolbar OK commits
cancel restores model shapes
nicewidgets should expose clean callbacks/events such as:
on_roi_bounds_preview(roi_id, bounds)
set_roi_editing(enabled, roi_id)
get_current_roi_bounds(roi_id)
External callers should not parse raw Plotly relayout payloads if nicewidgets can hide that complexity.
Axis range linking¶
Applications may need a primary image x-axis and an analysis plot x-axis linked.
nicewidgets responsibilities:
PlotlyRasterViewer:
emit x-axis range changes from Plotly relayout
accept programmatic x-axis range updates
PlotlyPlotWidget:
emit x-axis range changes from Plotly relayout / zoom
accept programmatic x-axis range updates
Application responsibility:
translate widget callbacks into app-level axis range events
avoid feedback loops by tagging event source view_id
Suggested application event:
XAxisRangeChanged(source_view_id, x_min, x_max)
This should be implemented after the widgets expose stable callback APIs.
ImageToolbarWidget¶
Responsibilities:
- channel selection UI
- ROI selection UI
- ROI CRUD/edit lifecycle buttons
- emit widget-level toolbar intents
Applications translate nicewidgets toolbar intents into application intents.
Example:
ImageToolbarRoiAddRequestIntent -> AddRoiIntent
ImageToolbarSelectRoiIntent -> SelectRoiIntent
The widget should not mutate application or domain state directly.
Widget state APIs¶
Reusable widgets should provide external setter methods that update internal/UI state without emitting user-intent callbacks. This prevents feedback loops.
Examples:
set_file_ext(...)
set_roi_options_and_selection_ext(...)
set_enabled_ext(...)
Pattern:
user action -> emits intent callback
programmatic state sync -> no intent callback
Stress points and improvement areas¶
Plotly DOM id handling is fragile¶
Any browser-side JS must use the established widget DOM-id pattern. Do not guess new selectors. Tests should validate generated JS strings where practical, but real browser behavior still needs manual testing.
Avoid async public APIs unless required¶
Simple widget state updates should be synchronous where possible. Async widget APIs caused test/runtime friction in ROI overlay work.
Axis linking is not finished¶
Plotly widgets need stable range callback APIs before applications should implement full axis linking.
Keep widgets application-agnostic¶
If a widget imports application events, controllers, or state, that is an architecture smell.
Test widgets at option/model boundary¶
Most widget tests should assert generated option dictionaries, styles, and JS payloads rather than requiring a browser.