Kiosk PyQt6 WebEngine : alternative navigateur, process unique, auto-reload
This commit is contained in:
34
scripts/install-kiosk-qt.sh
Normal file
34
scripts/install-kiosk-qt.sh
Normal 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
91
scripts/kiosk-qt.py
Normal 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()
|
||||||
Reference in New Issue
Block a user