diff --git a/docs/plan-gui-extraction.md b/docs/plan-gui-extraction.md new file mode 100644 index 0000000..d3f0179 --- /dev/null +++ b/docs/plan-gui-extraction.md @@ -0,0 +1,139 @@ +--- +kind: reference +--- + +# Plan: move the GUI out of the SDK into `orisys-studio` + +Status: **planned, then postponed (2026-09-30).** Nothing here is in progress. This document +records what would move, what would stay, and the one coupling that makes the move non-trivial, so +that the work can be picked up without re-deriving it. It is a plan, not a description of current +behaviour — everything described below is still in the SDK exactly as listed. + +## Why + +`examples/gui/` is not an example. It is a full Qt + open3d + pyqtgraph application — 30 modules, +4,815 lines — that ships inside the SDK wheel. The GUI belongs to the `orisys-studio` application, +which already exists for that purpose. Leaving it in the SDK means the SDK ships, versions and +installs an application it is not responsible for. + +## Current state + +| Path | Modules | Lines | What it is | +|---|---|---|---| +| `examples/gui/` | 30 | 4,815 | The application: `viewer2d/`, `viewer3d/`, `widgets/`, `app.py`, `log_config.py` | +| `examples/viz/` | 11 | 2,298 | **Shared** drawing/rendering helpers — see the coupling below | +| `examples/pipeline/` | 6 | 577 | Sensor service, CSV recorder, profiling. **SDK support code, not GUI** | + +### Good news: the SDK core does not depend on the GUI + +Verified by AST, not by grep: **no module under `src/orisys/` imports `gui`, `viz`, `pipeline` or +`examples`.** The only match is `src/orisys/__init__.py` importing `orisys.pipeline`, which is the +SDK's own module. The dependency runs one way, so the GUI can leave without touching the library. + +Two apparent hits are prose, not code: `src/orisys/configs.py` mentions `examples/` in a docstring +about run-root layout, and `export_sensors.py` has an `Examples::` docstring section. The +`configs.py` anchor is a real behavioural dependency to preserve — the run-root heuristic keys off +a sibling `examples/` directory, so that directory must keep existing in the packaged layout even +after `gui/` leaves it. + +### The coupling: `viz/` is shared, so it cannot simply follow the GUI + +``` +examples/gui/ ──uses──▶ examples/viz/ ──uses──▶ orisys (SDK) +examples/pipeline/service.py ──uses──▶ examples/viz/ +tests/test_contact_arrow_scale.py ──uses──▶ examples/viz/ +``` + +`examples/pipeline/service.py` and a test depend on `viz`, and `viz` is a mix: `mesh_camera.py`, +`contact_arrow.py`, `flow_arrow.py` and `offscreen_mesh.py` are open3d rendering helpers, while +`render_2d.py`, `recording.py`, `signal_filters.py` and `force_charts.py` are toolkit-independent. +**Moving `viz/` wholesale would break `examples/pipeline/`.** It has to be split, not moved. + +### The real coupling is in the tests, and it is inverted + +`tests/test_qt_viewer_backend.py` imports `examples/gui` directly, and `tests/conftest.py` inserts +`EXAMPLES_ROOT` into `sys.path` with a guard whose stated reason is that **`examples/gui` must win +over `build/gui`, because both are packages named `gui`**. So the SDK's test suite currently +depends on the GUI application being present and on a name-resolution guard existing for it. + +This is the inversion to fix: the SDK must not depend on the GUI. + +Two related facts: + +- `examples/gui/` contains committed `__pycache__` directories (`adapter.cpython-310.pyc`, + `cpython-313.pyc`, …) — build artefacts tracked alongside source. +- `tests/test_wheel_contents.py` and `tests/test_pyproject.py` assert on what the wheel bundles, so + they will need updating when the `gui/**/*.py` bundle pattern goes. + +## What moves, what stays + +**Moves to `orisys-studio`:** + +- `examples/gui/**` — the whole application, as the single home for GUI code. +- `examples/finger_pipeline_3dview.py` — a 91-line launcher for the dashboard; its only purpose is + to start the GUI, so it belongs with the GUI. +- The open3d rendering half of `examples/viz/`: `contact_arrow.py`, `flow_arrow.py`, + `mesh_camera.py`, `offscreen_mesh.py`, plus the open3d-dependent `__init__` re-exports. +- `tests/test_qt_viewer_backend.py` — it tests the GUI, so it moves with the GUI. + +**Stays in the SDK:** + +- `examples/pipeline/` — SDK support code (sensor service, CSV recording, profiling). +- `examples/viz/` toolkit-independent half: `render_2d.py`, `recording.py`, `signal_filters.py`, + `force_charts.py` — needed by `examples/pipeline/service.py`. +- `examples/plot_data.py` — uses pyqtgraph for force charts only; 96 lines. Decide separately + whether it follows the GUI; it is the one remaining example with a Qt dependency after the move. +- `src/orisys/finger/viewer.py` (`UVMeshViewer`) — **see the deprecation note below.** + +## The public-API problem, handled separately + +`UVMeshViewer`, `init_uv_mesh_viewer` and `update_uv_mesh_viewer` are exported in +`src/orisys/finger/__init__.py`'s `__all__`. **Removing them is a breaking change to a released +API**, so it must not be bundled into a code move: + +1. Release with them present and documented `DeprecationWarning`s emitted on construction. +2. Remove them at the next major version, with the release notes naming the replacement. + +The audit (`docs/api-audit.md`) already lists `UVMeshViewer` among the surface that is used by +examples and absent from the reference. + +## Sequence, lowest risk first + +| # | Step | Risk | Notes | +|---|---|---|---| +| 1 | Add `docs/plan-gui-extraction.md` (this file) | none | Done | +| 2 | Delete the committed `__pycache__` under `examples/gui/` and add it to `.gitignore` | none | Pure hygiene; ship independently | +| 3 | Tag the SDK at the commit whose `examples/gui` is known-good, so the studio move has a pinned source | none | A tag, not a submodule or subtree | +| 4 | In `orisys-studio`: import `examples/gui` + the open3d half of `viz` as its own provenance-stated change, reconciling against its existing viewer code | **medium** | The studio repo already has viewer code; this is a merge decision, not a copy | +| 5 | In the SDK: move `tests/test_qt_viewer_backend.py` out, and simplify the `conftest.py` guard | **medium** | The guard exists for `gui`; with `gui` gone, `EXAMPLES_ROOT` may still be needed for `viz`/`pipeline`. Re-derive, do not guess | +| 6 | In the SDK: retire `examples/gui/`, `finger_pipeline_3dview.py`, and the moved `viz` modules | low once 4 and 5 land | | +| 7 | Update the bundle patterns in `pyproject.toml` (drop `gui/**/*.py`, adjust `viz/**`) and the wheel tests | low | `test_wheel_contents.py`, `test_pyproject.py` | +| 8 | Update `examples/README.md` and the SDK reference to stop pointing at the dashboard | low | The README already says the GUI is moving | +| 9 | Deprecate `UVMeshViewer` in a release; remove at the next major | low | Separate release cycle | + +Steps 2 and 3 are safe to do immediately and independently. Steps 4–5 are the actual move and need +both repositories. Nothing here requires a library behaviour change, which is what makes the move +tractable. + +## Open questions for the studio side + +- **Merge or adopt?** `orisys-studio` already has its own viewer. If its code is the descendant of + this one, take the SDK copy only where it is ahead; otherwise choose one and delete the other. + This must be decided before step 4, because the answer changes what gets imported. +- **Do the SDK's 2D and 3D viewers need to keep working headless for CI?** `examples/viz` contains + offscreen rendering used to produce evidence images. If any SDK or sensor-design workflow depends + on that, the offscreen half may need to stay reachable from the SDK even after the app moves. +- **Does `plot_data.py` follow the GUI?** It is an example, not an application, and it is the last + Qt-dependent file in `examples/`. + +## Acceptance criteria + +- [ ] `src/orisys/` still imports no `gui`/`viz`/`examples` module (re-run the AST check). +- [ ] The SDK test suite passes with `examples/gui/` absent. +- [ ] No module named `gui` ships in the wheel; `test_wheel_contents.py` asserts it. +- [ ] `examples/pipeline/service.py` and `tests/test_contact_arrow_scale.py` still import their + `viz` dependencies. +- [ ] `UVMeshViewer` is either deprecated-but-present or removed by an explicit major-version + decision — never dropped silently in a move. +- [ ] The `examples/` sibling directory still exists in the packaged layout, because + `configs.py`'s run-root anchor depends on it. diff --git a/examples/README.md b/examples/README.md index 96a159f..1f9b74d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -79,9 +79,12 @@ a header explaining the difference. They are kept so that code copied from them Qt / PyQtGraph examples additionally need `pip install -e ".[gui]"`. -## The GUI is moving out +## The GUI lives here for now `examples/gui/` - the 2D viewer, the 3D dashboard and their widgets - is a full Qt + open3d -application, not an example. It is being moved to the **orisys-studio** application, which is -where the GUI belongs. Until that lands, `finger_pipeline_3dview.py` and `examples/gui/` still -work as before; new work should not build on them. +application rather than an example, and its proper home is the **orisys-studio** application. +That move is **planned but not started**; `finger_pipeline_3dview.py` and `examples/gui/` work +exactly as before. The plan, including the coupling that makes it non-trivial, is in +`docs/plan-gui-extraction.md`. + +The practical guidance is only that new SDK work should not build on `examples/gui/`.