Files
orisys-sdk/docs/plan-gui-extraction.md
T
ykcheungab f700102067 docs: record the GUI extraction plan, postponed
examples/gui/ is a full Qt + open3d application (30 modules, 4,815 lines) rather
than an example, and its proper home is the orisys-studio application. The move
is planned but not started, so this records the plan rather than performing any
of it.

What the plan establishes, so the work does not have to be re-derived:

- src/orisys/ imports no gui/viz/examples module, verified by AST rather than
  grep. The dependency is one-way, so the GUI can leave without a library
  change. The single apparent hit is `from .pipeline import ...` in
  src/orisys/__init__.py, which is the SDK's own module.
- examples/viz/ is shared and cannot follow the GUI wholesale: it mixes open3d
  renderers with toolkit-independent helpers that examples/pipeline/service.py
  and tests/test_contact_arrow_scale.py depend on. It has to be split.
- The real coupling is inverted and lives in the tests: tests/test_qt_viewer_backend.py
  imports examples/gui, and tests/conftest.py deliberately puts examples/ ahead
  of build/ because both define a package named `gui`. Removing the GUI means
  re-deriving that guard, not deleting it blindly.
- UVMeshViewer and friends are in src/orisys/finger/__init__.__all__, so removing
  them is a breaking change to a released API and needs a deprecation release
  first. It must not be folded into a code move.
- The `examples/` directory must keep existing in the packaged layout even after
  gui/ leaves it: src/orisys/configs.py anchors its run-root detection on a
  sibling examples/ folder.

Also updates examples/README.md to say "lives here for now" instead of "is moving
out", so nothing reads as work in progress. The GUI itself is untouched, and the
only guidance offered is that new SDK work should not build on it.
2026-09-30 17:10:17 +08:00

8.1 KiB
Raw Blame History

kind
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 DeprecationWarnings 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.