Kiosk PyQt6 WebEngine : alternative navigateur, process unique, auto-reload

This commit is contained in:
2026-04-09 07:46:39 +02:00
parent b4ec4dbf3f
commit 0d24c8af26
2 changed files with 125 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Installe PyQt6 WebEngine et configure le service kiosk Qt
set -e
echo "=== Installation PyQt6 WebEngine ==="
pip3 install PyQt6 PyQt6-WebEngine --break-system-packages
echo "=== Creation service systemd ==="
cat > /etc/systemd/system/photobooth-kiosk-qt.service << EOF
[Unit]
Description=Photobooth Kiosk Qt
After=graphical-session.target photobooth.service
Wants=photobooth.service
[Service]
Type=simple
User=$(logname)
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/$(logname)/.Xauthority
ExecStart=/usr/bin/python3 /home/$(logname)/photobooth/scripts/kiosk-qt.py
Restart=always
RestartSec=3
[Install]
WantedBy=graphical-session.target
EOF
systemctl daemon-reload
systemctl enable photobooth-kiosk-qt
echo "=== Pour basculer sur le kiosk Qt ==="
echo " sudo systemctl stop photobooth-kiosk"
echo " sudo systemctl disable photobooth-kiosk"
echo " sudo systemctl start photobooth-kiosk-qt"

91
scripts/kiosk-qt.py Normal file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""
Kiosk PyQt6 WebEngine pour photobooth.
Alternative au browser : process unique, pas de "already running", restart propre.
Usage : python3 scripts/kiosk-qt.py
"""
import sys
import time
import subprocess
from PyQt6.QtCore import Qt, QUrl, QTimer
from PyQt6.QtWidgets import QApplication
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineSettings
BACKEND_URL = "http://localhost:8080"
RETRY_INTERVAL_MS = 3000
def wait_backend(url: str, timeout: int = 30) -> bool:
"""Attend que le backend soit dispo avant d'afficher."""
import urllib.request
for _ in range(timeout):
try:
urllib.request.urlopen(url, timeout=1)
return True
except Exception:
time.sleep(1)
return False
class KioskWindow(QWebEngineView):
def __init__(self):
super().__init__()
# Plein ecran sans decoration
self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
self.showFullScreen()
# Parametres WebEngine
settings = self.settings()
settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True)
settings.setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True)
settings.setAttribute(QWebEngineSettings.WebAttribute.TouchIconsEnabled, True)
settings.setAttribute(QWebEngineSettings.WebAttribute.ScrollAnimatorEnabled, True)
# Cacher le curseur
self.setCursor(Qt.CursorShape.BlankCursor)
self.setUrl(QUrl(BACKEND_URL))
# Watchdog : recharge si la page ne repond plus (WebSocket coupe)
self._watchdog = QTimer(self)
self._watchdog.setInterval(10000)
self._watchdog.timeout.connect(self._check_backend)
self._watchdog.start()
def _check_backend(self):
import urllib.request
try:
urllib.request.urlopen(BACKEND_URL, timeout=2)
except Exception:
self.reload()
def keyPressEvent(self, event):
# F5 = reload, F11 = fullscreen toggle, Echap ignoré
if event.key() == Qt.Key.Key_F5:
self.reload()
elif event.key() == Qt.Key.Key_F11:
if self.isFullScreen():
self.showNormal()
else:
self.showFullScreen()
def main():
app = QApplication(sys.argv)
app.setApplicationName("Photobooth Kiosk")
# Attendre que le backend soit pret
if not wait_backend(BACKEND_URL):
print("Backend non disponible apres 30s", file=sys.stderr)
sys.exit(1)
window = KioskWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()