Files
orisys-app/README.md
T
ykcheungab a0c13c1dc6 加快门禁:删除测试窗口、释放桩线程并并行运行三个阶段
关闭后未删除的测试窗口让每次主题切换重排全部残留控件,ui_check 每次关窗还要等桩线程 5 秒。Linux 上结论不变,test_ui_smoke 75→64 s、ui_check 52→16 s;Windows 门禁结果见 .scratch/gate-speed/。
2026-10-19 09:34:29 +08:00

165 lines
7.2 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_studio_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.
### The merge gate
One command runs everything a change must not break, and it is the only check that
is meant to be unskippable:
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File tools\gate.ps1
```
It runs `tools/lint_gate.py` over the changed files, then the full studio suite, the full
shared suite, and `ui_check.py` at the same time (`-Serial` runs those three one after
another with live output, for reading a failing stage); writes each one's output to
`.scratch/gate/gate-<run-id>/`; and holds the run to three contracts — an **empty**
`tools/known_failures.txt`, the skip ceilings and collection floors in
`tools/skip_budget.txt`, and zero findings anywhere. It exits 0 only when all of
that holds.
The lint step compares against a commit range, not only the dirty tree: `-LintBase <ref>`
defaults to `origin/main`, so a pre-push run checks every commit about to be pushed.
A ref that does not resolve is a configuration error (exit 2); the gate never quietly
falls back to linting uncommitted files only.
The **gate platform is Windows**: the suites render real Qt widgets offscreen, and two
checks are platform-sensitive ([`docs/test-repair-plan.zh.md`](docs/test-repair-plan.zh.md)
§3 #11 and #13), so a green Linux run is not the same statement.
An optional `pre-push` hook calls it, so a failing gate blocks the push. It is
deliberately not installed for you:
```powershell
git config core.hooksPath .githooks # enable; `git config --unset core.hooksPath` disables
```
Day-to-day development still uses targeted test runs
([`docs/test-execution-plan.zh.md`](docs/test-execution-plan.zh.md)); the gate is what
runs before a change is committed or handed over.
## 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.