33a3d8c9c2
Studio had a colour contract with a mechanical audit but no layout equivalent,
which is where the recurring visual defects live: window flashing, the label
under the 3D view, the platform-not-moving report, and the 2D/3D sync work all
passed a 3,408-assertion tool suite because that suite asserts widget state, not
the visible result.
apps/studio/docs/ui-layout.md states the contract: ViewLayoutController owns the
top-level arrangement and each panel owns its internal layout; every path that
changes the view must go through apply_preset(); each view's pane priority and
degradation order; and which numeric geometry arguments are legitimate (a 1px
separator is not the same defect as a magic width on a flexible panel).
It also records the one transition that is broken today.
on_cal_panel_toggled(False) sets view_preset = "live" directly instead of
calling apply_preset("live"), so the replay -> calibration -> calibration-off
sequence leaves the rail on the replay source, the run button in playback role,
and the previous view's splitter sizes. The live branch does call apply_preset,
which is what makes the asymmetry easy to miss.
Three claimed enforcement gaps are stated so the contract is not mistaken for
something already enforced: audit_palette reads theme JSON and never scans
source for the hex literals ui-palette.md forbids; the containment test compares
width and height without x/y, so a shifted or overlapping child passes; and the
GL pixel suite skips invisibly without a real context.
Verified: doc_lint default and --all both exit 0; the new document is registered
in the workspace inventory (63 collected, 63 inventoried, 0 missing) and linked
from README.md beside the palette reference. No source file changed.
128 lines
5.5 KiB
Markdown
128 lines
5.5 KiB
Markdown
# orisys-app
|
|
|
|
Desktop applications that consume the Orisys SDK.
|
|
|
|
| App | Status | Qt binding |
|
|
|-----|--------|-----------|
|
|
| `apps/studio` | The customer-facing shell (theme / locale / prefs / logging, acquisition, 3D) | **PySide6** |
|
|
| `apps/autocal` | Internal 5-axis calibration platform control | **PySide6** |
|
|
| `apps/internal` | Stub, superseded by `apps/autocal` | — |
|
|
|
|
Both shipped apps are **PySide6** — Qt's **LGPLv3** binding. PyQt5's free option is
|
|
**GPLv3-or-commercial**, which is why the studio was migrated off it: a closed-source
|
|
product would otherwise need a licence purchase. The rule is enforced, not just
|
|
documented: neither app may import PyQt5/PyQt6/PySide2, and `shared/` (used by both) may
|
|
import PySide6 and nothing else. See `apps/studio/tests/test_binding_purity.py` and
|
|
`apps/autocal/tests/test_binding_purity.py`.
|
|
|
|
One practical consequence: `pyqtgraph` binds to whichever toolkit it imports first, so a
|
|
development environment that still has PyQt5 installed can build plots from the wrong
|
|
binding. `orisys_studio.main` sets `PYQTGRAPH_QT_LIB=PySide6` before anything imports it,
|
|
and the purity test asserts that importing the app pulls in no other binding.
|
|
|
|
## Layout
|
|
|
|
- `shared/` — common paths, prefs, theme, i18n, logging, and the ZMotion client
|
|
- `shared/resources/zmotion/` — vendored ZMotion runtime DLLs (internal use only)
|
|
- `apps/studio/config/` — locales, default prefs, editable `sensors/` copies
|
|
- `apps/studio/outputs/` — generated logs and calibration exports (gitignored)
|
|
- `apps/autocal/` — internal platform control; see `apps/autocal/README.md`
|
|
- `build/` — Windows PyInstaller packaging (`build/studio/build.ps1`)
|
|
|
|
### Selecting which app's paths resolve
|
|
|
|
`orisys_app_shared.paths` reads `ORISYS_APP_ID` (an `apps/` directory name) to
|
|
decide which product's `config/`, `preference.json` and `outputs/` a process
|
|
owns. Each app's launcher sets it before importing the shared package; when it
|
|
is unset the studio is assumed, which keeps existing callers working unchanged.
|
|
A named app whose `config/` is missing raises rather than silently resolving
|
|
another product's tree.
|
|
|
|
## Run (studio shell)
|
|
|
|
```powershell
|
|
cd D:\repo\orisys-app
|
|
python -m pip install -e .\shared -e .\apps\studio
|
|
python .\apps\studio\run.py
|
|
```
|
|
|
|
Or without install:
|
|
|
|
```powershell
|
|
$env:PYTHONPATH = "D:\repo\orisys-app\shared\src;D:\repo\orisys-app\apps\studio\src"
|
|
python D:\repo\orisys-app\apps\studio\run.py
|
|
```
|
|
|
|
### Local `orisys` SDK
|
|
|
|
`orisys_app_shared.force` imports `orisys.calibration` at module scope, so the
|
|
studio app cannot start against an SDK build that predates that module
|
|
(the published 0.5.2 wheel does not include it). When working from a repo
|
|
checkout, install the SDK from source instead of from an index:
|
|
|
|
```powershell
|
|
python -m pip install .\..\orisys-sdk # same checkout that built this app
|
|
```
|
|
|
|
If a pip build is blocked by the environment, the equivalent manual install is
|
|
to copy `orisys-sdk/src/orisys/**/*.py` over `site-packages/orisys/` and delete
|
|
the stale `__init__.cp310-win_amd64.pyd` (an extension module shadows the
|
|
package `__init__.py`, which hides sibling modules such as `calibration.py`).
|
|
|
|
### Verifying UI changes
|
|
|
|
Both tools render the real main window with the offscreen Qt platform — no
|
|
display, camera or sensor hardware required:
|
|
|
|
```powershell
|
|
python .\apps\studio\tools\ui_check.py # layout/theme assertions
|
|
python .\apps\studio\tools\ui_snapshot.py --theme light --demo `
|
|
--sensor finger --panels cal,3d --size 1600x900 --out shot.png
|
|
```
|
|
|
|
`ui_check.py` needs only the runtime dependencies. The `pytest` suite in
|
|
`shared/tests` and `apps/studio/tests` covers the same ground plus locale and
|
|
preference-schema parity:
|
|
|
|
```powershell
|
|
pip install pytest
|
|
python -m pytest
|
|
```
|
|
|
|
On Windows the offscreen platform finds no fonts unless `QT_QPA_FONTDIR` points
|
|
at `C:\Windows\Fonts`; both tools set it automatically.
|
|
|
|
`ui_check.py` also runs the palette rules (WCAG contrast, colour separation,
|
|
ramp monotonicity) that guard the brand tokens; see
|
|
[`apps/studio/docs/ui-palette.md`](apps/studio/docs/ui-palette.md) for the
|
|
colour system, its relationship to the UI design spec, and every deliberate
|
|
deviation from it.
|
|
|
|
Layout follows [`apps/studio/docs/ui-layout.md`](apps/studio/docs/ui-layout.md):
|
|
which controller owns the arrangement, the fixed effect set every view change
|
|
must apply, each view's pane priority and degradation order, when a numeric
|
|
geometry argument is legitimate, and which of these rules are not yet
|
|
mechanically enforced.
|
|
|
|
`ui_check.py` includes a `record channel` section as well; see
|
|
[`apps/studio/docs/record-channel.md`](apps/studio/docs/record-channel.md) for
|
|
why recording and calibration sampling no longer hang off the UI refresh timer,
|
|
and for the data-reliability fixes that came with it.
|
|
|
|
## Branding
|
|
|
|
Window / EXE icon: `shared/resources/branding/logo.png`
|
|
Top bar / About: `shared/resources/branding/logo_topbar.png`
|
|
|
|
Dark mode is the default. Logos currently sit on a black tile; a transparent (or light) variant may be needed for polished light mode later.
|
|
|
|
## Versioning
|
|
|
|
- **Studio app version** (`orisys_studio.__version__`) — shown in Help → About and used for releases.
|
|
- **Shared package version** (`orisys_app_shared.__version__`) — internal library version.
|
|
- **Shared API** (`orisys_app_shared.API_VERSION`) — compatibility tag. Studio declares `REQUIRES_SHARED_API`; startup logs a warning on mismatch.
|
|
|
|
## Deferred
|
|
|
|
Run/Stop pipeline, arrow/force plots, units, calibration capture, Open3D finger mesh, sync-from-wheel for `config/sensors`, PyInstaller packaging, and the internal app.
|