Shotbot is a PySide6 desktop tool for matchmove workflow execution in a VFX studio pipeline. It provides shot browsing and one-click application launching with workspace context.
Shotbot is designed around this pipeline:
3DEqualizer -> Maya -> Nuke -> Publish
Primary use cases:
- Browse active shots from
ws -sg - Launch tools in shot context (
3de,maya,nuke,rv,publish) - Browse other artists' 3DE scenes
- Resume prior work via Previous Shots
- Python
3.11+ - Linux shell environment for launcher execution
- Studio workspace command availability (
ws) - Rez command availability for production DCC launches unless
REZ_MODEis explicitly disabled - A valid SGTK bootstrap source in the launched DCC environment
(
Rezpackage startup or site wrapper). Shotbot's own hooks are a post-launch context layer, not the primary bootstrap source.
# install dependencies
uv sync
# run in production/VFX mode
uv run python shotbot.py# run with mock data (no ws, no facility filesystem)
uv run python shotbot.py --mock
# or
SHOTBOT_MOCK=1 uv run python shotbot.pyPlain --mock uses the in-memory MockWorkspacePool, so it does not exercise
the real bash/process-pool startup path that the remote host uses.
# local fake /shows tree + fake ws shell function + real ProcessPoolManager
uv run python dev-tools/run_local_remote_sim.py
# emulate a cache-backed startup like the remote
uv run python dev-tools/run_local_remote_sim.py --cache-profile warm
# emulate remote runs where active shots cache exists but is expired
uv run python dev-tools/run_local_remote_sim.py --cache-profile stale-shots
# if you want to force headless/offscreen locally
QT_QPA_PLATFORM=offscreen uv run python dev-tools/run_local_remote_sim.pyThis mode is the closest local reproduction path for remote startup issues:
- keeps mock data and a local fake VFX filesystem
- uses a temporary
HOMEwith a minimalwsshell function - keeps
SHOTBOT_MOCK=1 - forces the real
ProcessPoolManagerinstead ofMockWorkspacePool - can start from
clean,warm, orstale-shotscache profiles
That means startup still goes through the real /bin/bash -i/-l -c ... path.
# headless CI-style launch (uses mock mode automatically)
uv run python shotbot.py --headless --mock
# auto-capture a screenshot after N seconds
# current implementation writes to /mnt/c/temp/shotbot_auto.png
uv run python shotbot.py --mock --screenshot 10# lint + format
uv run ruff check .
uv run ruff format .
# type checking
uv run basedpyright
# primary test suite (deterministic correctness gate; stable full-suite shutdown)
./run_tests.sh
# or: uv run pytest tests/ --force-clean-exit
# parallel isolation check (secondary diagnostic run; not the current GitHub Actions gate)
uv run pytest tests/ -n auto --force-clean-exit
# randomized-order stress check (shared-state / teardown bugs; mirrors secondary CI job)
uv run pytest tests/ -n auto --force-clean-exit -o addopts='-ra -m "not quarantine" --durations=20 --tb=short -p no:rerunfailures -p no:xvfb --dist=loadgroup' --randomly-seed=12345
# dead code detection (generate trace first, then run the normal 80-confidence pass)
uv run python scripts/generate_skylos_trace.py
uv run skylos . --exclude-folder tests --exclude-folder archive --confidence 80
# optional one-time broader pass after the 80-confidence queue is clear
uv run skylos . --exclude-folder tests --exclude-folder archive --confidence 60
# after that, switch to ranked debt hotspots rather than lower-confidence sweeps
uv run python dev-tools/run_skylos_debt.py
# raw full-repo debt audit (slower and noisier)
uv run python dev-tools/run_skylos_debt.py --full --json -o /tmp/skylos_debt_full.jsonFor the full Skylos workflow, including broader trace coverage (--markexpr),
partial-trace handling, whitelist guidance, the current skylos-fast
workaround, and the repo policy of 80 as the default pass with 60 as the
stop point, see docs/SKYLOS_DEAD_CODE_DETECTION.md.
Test policy: the deterministic serial run is still the main correctness gate, and CI now
also runs a randomized-order parallel stress job with a rotating seed. Full-suite Qt runs
should use --force-clean-exit so PySide shutdown crashes do not mask the real pytest result.
Parallel is retained as a secondary isolation check for shared-state and teardown bugs,
not because it is dramatically faster.
For full test policy and command guidance, see tests/README.md. Use
tests/fixtures/README.md for fixture inventory and fixture-specific
troubleshooting.
See docs/README.md for the active documentation index and
docs/CONTRIBUTOR_SETUP.md for contributor bootstrap.
- User settings persist via Qt
QSettings(~/.config/ShotBot/ShotBot.confon Linux). - App launch mappings are configured in
config.py. SHOTBOT_SCRIPTS_DIRshould point at the deployed sharedscripts/directory when Nuke or helper utilities need bundled startup files.- 3DE does not use
SHOTBOT_SCRIPTS_DIRas its final startup-hook path. File launches go throughwrapper/3de-shotbot, which injects the dedicatedscripts/3de/directory after the facility wrapper resolves. - Shotbot manages workspace and Rez shell setup, but it does not currently
perform a full SGTK bootstrap on its own. See
docs/LAUNCHER_AND_VFX_ENVIRONMENT.mdfor the production launcher contract.