3.7 KiB
macOS — Camera on macOS with PySide6
Problem
Aplikacja uruchomiona w interpreted mode (python -m duck_preview) na macOS z kamerą Elgato nie wyświetla obrazu.
Przyczyna
Oficjalny przykład PySide6 (camera.qml / examples/multimedia) zawiera kod:
if sys.platform == "darwin":
is_nuitka = "__compiled__" in globals()
if not is_nuitka and sys.platform == "darwin":
print("This example does not work on macOS when Python is run "
"in interpreted mode. For this example to work on macOS, "
"package the example using pyside6-deploy")
sys.exit(0)
macOS → AVFoundation → wymaga spakowania przez Nuitka/pyside6-deploy.
QCamera na macOS potrzebuje properly bundled app structure (Info.plist, entitlements, code signing). W interpreted mode Python nie ma tego contextu.
Rozwiązanie
Pakować aplikację przez pyside6-deploy (Nuitka) na macOS. Na Windows działa bez pakowania.
Qt Permission API (QCameraPermission)
Od Qt 6.5+ dostępne jest QCameraPermission — nowoczesne API do proszenia o zgodę kamery.
API
from PySide6.QtCore import QCameraPermission, Qt
from PySide6.QtWidgets import QApplication
perm = QCameraPermission()
match QApplication.checkPermission(perm):
case Qt.PermissionStatus.Undetermined:
# Prosimy o zgodę → callback wywoła init ponownie
QApplication.requestPermission(perm, parent, callback)
return
case Qt.PermissionStatus.Denied:
# overlay: "Camera permission denied"
case Qt.PermissionStatus.Granted:
# uruchom kamerę
Permission flow
User → wybiera kamerę w menu
→ MainWindow._on_camera_selected(device)
→ checkPermission(QCameraPermission)
├─ Undetermined → requestPermission(camera, parent, callback)
│ └─ callback → _on_camera_selected ponownie
├─ Denied → overlay: "Camera permission denied. Grant access in System Settings > Privacy & Security > Camera"
└─ Granted → CameraService.start(device)
QVideoWidget + QVideoSink — dual output
PySide6 6.11 ma osobne metody w QMediaCaptureSession:
| Metoda | Przeznaczenie |
|---|---|
setVideoOutput(QVideoWidget*) |
Natywne renderowanie (GPU) |
setVideoSink(QVideoSink*) |
Dostęp do klatek (telemetria, AI) |
Oba działają równolegle — nie ma konfliktu.
QMediaCaptureSession
├─ setVideoOutput(QVideoWidget) → natywne renderowanie
└─ setVideoSink(QVideoSink) → frame access
└─ videoFrameChanged → FrameDispatcher
QVideoSink dostarcza sygnał videoFrameChanged(QVideoFrame) — na to podpina się FrameDispatcher, a ten rozsyła do TelemetryCollector i przyszłych AI subscriberów.
macOS — packaging
pyside6-deploy
pip install nuitka
pyside6-deploy duck_preview/__main__.py --name "Duck Preview"
pyside6-deploy.toml
[app]
script = "duck_preview/__main__.py"
name = "Duck Preview"
bundle_identifier = "com.bartool.duck-preview"
categories = "public.app-category.photography"
platforms = ["macos"]
Po spakowaniu powstaje Duck Preview.app — standalone bundle z dostępem do AVFoundation.
Windows
Na Windows działa bez pakowania — python -m duck_preview w venv.
Font fallback
Consolas nie istnieje na macOS. Używać "monospace" (generic font family — Qt mapuje na Menlo na macOS, Consolas na Windows).
Error state w Overlay
Overlay wspiera wyświetlanie błędów (np. brak permisji):
- Normalny stan → zielone metryki (FPS, frame times)
- Error state → czerwony komunikat
- Przełączanie przez
overlay.set_metrics({"error": "message"})