From 5a066b5bdc927f6460acf41e71e80ef591cca50a Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:38:02 +0200 Subject: [PATCH 1/8] Disallow opening the native context menu with right click or Menu It shows unavailable options, for example Show Sources. As for back and reload actions, the interface should already provide those actions. --- kiosk/kiosk_browser/browser_widget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kiosk/kiosk_browser/browser_widget.py b/kiosk/kiosk_browser/browser_widget.py index de06dae8..c1a04b95 100644 --- a/kiosk/kiosk_browser/browser_widget.py +++ b/kiosk/kiosk_browser/browser_widget.py @@ -54,6 +54,9 @@ def __init__(self, url, get_current_proxy, parent): # Allow sound playback without user gesture self._webview.page().settings().setAttribute(QtWebEngineWidgets.QWebEngineSettings.PlaybackRequiresUserGesture, False) + # Prevent opening context menu on right click or pressing menu + self._webview.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.NoContextMenu) + # Load url self._webview.setUrl(url) self._view(Status.LOADING) From afa7d25e01ad9a331ffbf448b0d57d943d42bdd2 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:41:58 +0200 Subject: [PATCH 2/8] Fix non fullscreen mode Window was not shown in this context. And we have to provide a size, otherwise on my WM (labwc), the window is very small. --- kiosk/kiosk_browser/__init__.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/kiosk/kiosk_browser/__init__.py b/kiosk/kiosk_browser/__init__.py index 2a9a0fa8..4876b2c8 100644 --- a/kiosk/kiosk_browser/__init__.py +++ b/kiosk/kiosk_browser/__init__.py @@ -1,6 +1,6 @@ import sys import logging -from PyQt5.QtCore import Qt, QUrl +from PyQt5.QtCore import Qt, QUrl, QSize from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QApplication @@ -20,8 +20,16 @@ def start(kiosk_url, settings_url, toggle_settings_key, fullscreen = True): mainWidget.setContextMenuPolicy(Qt.NoContextMenu) + screen_size = app.primaryScreen().size() + if fullscreen: - set_fullscreen(app, mainWidget) + # Without a Window Manager, showFullScreen does not work under X, + # so set the window size to the primary screen size. + mainWidget.resize(screen_size) + mainWidget.showFullScreen() + else: + mainWidget.resize(QSize(round(screen_size.width() / 2), round(screen_size.height() / 2))) + mainWidget.show() app.exec_() @@ -31,10 +39,3 @@ def parseUrl(url): raise InvalidUrl('Failed to parse URL "%s"' % url) from Exception else: return parsed_url - -def set_fullscreen(app, mainWidget): - # Without a Window Manager, showFullScreen does not work under X, - # so set the window size to the primary screen size. - screen_size = app.primaryScreen().size() - mainWidget.resize(screen_size) - mainWidget.showFullScreen() From 034b36b7c5fe789e3bca61de81efb2565daff740 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:43:23 +0200 Subject: [PATCH 3/8] Shorten usage of shortcuts in browser widget --- kiosk/kiosk_browser/browser_widget.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kiosk/kiosk_browser/browser_widget.py b/kiosk/kiosk_browser/browser_widget.py index c1a04b95..22f13d39 100644 --- a/kiosk/kiosk_browser/browser_widget.py +++ b/kiosk/kiosk_browser/browser_widget.py @@ -63,11 +63,9 @@ def __init__(self, url, get_current_proxy, parent): self._webview.loadFinished.connect(self._load_finished) # Shortcut to manually reload - self._reload_shortcut = QtWidgets.QShortcut('CTRL+R', self) - self._reload_shortcut.activated.connect(self.reload) + QtWidgets.QShortcut('CTRL+R', self).activated.connect(self.reload) # Shortcut to perform a hard refresh - self._hard_refresh_shortcut = QtWidgets.QShortcut('CTRL+SHIFT+R', self) - self._hard_refresh_shortcut.activated.connect(self._hard_refresh) + QtWidgets.QShortcut('CTRL+SHIFT+R', self).activated.connect(self._hard_refresh) # Prepare reload timer self._reload_timer = QtCore.QTimer(self) From 4d3b36dff1e5c57f8a7166fa4b5768a8b40b5ada Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:43:51 +0200 Subject: [PATCH 4/8] Open settings when long pressing the Menu key --- kiosk/kiosk_browser/main_widget.py | 35 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/kiosk/kiosk_browser/main_widget.py b/kiosk/kiosk_browser/main_widget.py index c3bb8f75..15f430e9 100644 --- a/kiosk/kiosk_browser/main_widget.py +++ b/kiosk/kiosk_browser/main_widget.py @@ -1,12 +1,13 @@ from PyQt5 import QtWidgets, QtCore +import time from kiosk_browser import browser_widget, captive_portal, dialogable_widget, proxy as proxy_module class MainWidget(QtWidgets.QWidget): - """ Show website at kiosk_url. + """ Show website from kiosk_url. - - Show settings in dialog using shortcut. - - Show message when captive portal is detected, allowing to show in dialog. + - Show settings in a dialog using a shortcut or long pressing Menu. + - Show toolbar message when captive portal is detected, opening it in a dialog. - Use proxy configured in Connman. """ @@ -17,14 +18,18 @@ def __init__(self, kiosk_url: str, settings_url: str, toggle_settings_key: str): proxy = proxy_module.Proxy() proxy.start_monitoring_daemon() + # Menu press + self._menu_press_since = None + self._menu_press_delay_seconds = 1.5 + # Browser widget self._kiosk_url = kiosk_url self._settings_url = settings_url self._dialogable_browser = dialogable_widget.DialogableWidget( parent = self, inner_widget = browser_widget.BrowserWidget( - url = kiosk_url, - get_current_proxy = proxy.get_current, + url = kiosk_url, + get_current_proxy = proxy.get_current, parent = self), on_close = self._close_dialog) @@ -43,9 +48,12 @@ def __init__(self, kiosk_url: str, settings_url: str, toggle_settings_key: str): self._layout.addWidget(self._dialogable_browser) self.setLayout(self._layout) - # Shortcuts + # Shortcut to toggle settings QtWidgets.QShortcut(toggle_settings_key, self).activated.connect(self._toggle_settings) + # Look at events with the eventFilter function + self.installEventFilter(self) + # Private def _toggle_settings(self): @@ -73,3 +81,18 @@ def _close_dialog(self): self._dialogable_browser.inner_widget().load(self._kiosk_url) if self._is_captive_portal_open: self._is_captive_portal_open = False + + def eventFilter(self, source, event): + # Toggle settings with a long press on the Menu key + if event.type() == QtCore.QEvent.ShortcutOverride: + if event.key() == QtCore.Qt.Key_Menu: + if self._menu_press_since is None: + self._menu_press_since = time.time() + elif time.time() - self._menu_press_since > self._menu_press_delay_seconds: + self._menu_press_since = None + self._toggle_settings() + elif event.type() == QtCore.QEvent.KeyRelease: + if event.key() == QtCore.Qt.Key_Menu: + self._menu_press_since = None + + return super(MainWidget, self).eventFilter(source, event) From a017c4016f8f9ae0087b8d723ed9ecb65f00fa3e Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:46:26 +0200 Subject: [PATCH 5/8] Add changelog --- controller/Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/controller/Changelog.md b/controller/Changelog.md index 4f3d03ba..077bf21d 100644 --- a/controller/Changelog.md +++ b/controller/Changelog.md @@ -2,6 +2,7 @@ ## Added +- kiosk: Open settings with a long press on the Menu key - controller: Enable spatial navigation using the arrow keys ## Removed From 3a7dba2e61814b4c876d8d497e3a7611fdcb405e Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 10:52:14 +0200 Subject: [PATCH 6/8] Update user manual --- docs/user-manual/Readme.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-manual/Readme.org b/docs/user-manual/Readme.org index 4b5c824d..167a3811 100644 --- a/docs/user-manual/Readme.org +++ b/docs/user-manual/Readme.org @@ -33,7 +33,7 @@ When a remote control with a power button is connected, the system may be shut d * Administration -<>A menu for system administration may be accessed with the key combination ~Ctrl-Shift-F12~. +<>A menu for system administration may be accessed with the key combination ~Ctrl-Shift-F12~, or with a long press on the Menu key. The administration interface opens to a page displaying basic system information. From 891c1d5df5020dd9b4582947ac481ff90658c734 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 14:33:07 +0200 Subject: [PATCH 7/8] Prevent re-firing settings toggle on very long press Use auto repeat to prevent re-triggering toggling the settings more than once when very long pressing the Menu key. --- kiosk/kiosk_browser/main_widget.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kiosk/kiosk_browser/main_widget.py b/kiosk/kiosk_browser/main_widget.py index 15f430e9..1d7e4af9 100644 --- a/kiosk/kiosk_browser/main_widget.py +++ b/kiosk/kiosk_browser/main_widget.py @@ -86,13 +86,13 @@ def eventFilter(self, source, event): # Toggle settings with a long press on the Menu key if event.type() == QtCore.QEvent.ShortcutOverride: if event.key() == QtCore.Qt.Key_Menu: - if self._menu_press_since is None: + if not event.isAutoRepeat(): self._menu_press_since = time.time() - elif time.time() - self._menu_press_since > self._menu_press_delay_seconds: + elif self._menu_press_since is not None and time.time() - self._menu_press_since > self._menu_press_delay_seconds: self._menu_press_since = None self._toggle_settings() elif event.type() == QtCore.QEvent.KeyRelease: - if event.key() == QtCore.Qt.Key_Menu: + if event.key() == QtCore.Qt.Key_Menu and not event.isAutoRepeat(): self._menu_press_since = None return super(MainWidget, self).eventFilter(source, event) From a8a355310f1801f2216abd456bcf6c2ccd5e7cbb Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 24 Sep 2024 18:06:16 +0200 Subject: [PATCH 8/8] Cleanly stop app on SIGINT --- kiosk/kiosk_browser/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kiosk/kiosk_browser/__init__.py b/kiosk/kiosk_browser/__init__.py index 4876b2c8..c1025913 100644 --- a/kiosk/kiosk_browser/__init__.py +++ b/kiosk/kiosk_browser/__init__.py @@ -1,5 +1,6 @@ import sys import logging +import signal from PyQt5.QtCore import Qt, QUrl, QSize from PyQt5.QtGui import QKeySequence from PyQt5.QtWidgets import QApplication @@ -31,6 +32,14 @@ def start(kiosk_url, settings_url, toggle_settings_key, fullscreen = True): mainWidget.resize(QSize(round(screen_size.width() / 2), round(screen_size.height() / 2))) mainWidget.show() + # Quit application when receiving SIGINT + def on_SIGINT(signum, frame): + print('Exiting…') + app.quit() + sys.exit(130) + signal.signal(signal.SIGINT, on_SIGINT) + + # Start application app.exec_() def parseUrl(url):