From 9fe9c110c5a951f4440015f2a36ac9bc949cd6cc Mon Sep 17 00:00:00 2001 From: Shyamnath Premnadh Date: Mon, 9 May 2022 00:42:57 +0200 Subject: webenginecore - QtWebEngineProfile setNotificationPresenter - added setNotificationPresenter(...) - added QtWebEngineWidgets Notifications example which uses setNotificationPresenter Task-number: PYSIDE-1912 Change-Id: Ib8fdf7879d6e54bcae47dcc89be6bd5a861a8846 Reviewed-by: Friedemann Kleint (cherry picked from commit cd394249fc77063a2bd6ba7a7633fdbc10621475) Reviewed-by: Qt Cherry-pick Bot --- .../notifications/doc/notifications.png | Bin 0 -> 21374 bytes .../notifications/doc/notifications.rst | 8 ++ examples/webenginewidgets/notifications/main.py | 94 ++++++++++++++++++ .../notifications/notificationpopup.py | 105 +++++++++++++++++++++ .../notifications/notifications.pyproject | 3 + .../notifications/resources/icon.png | Bin 0 -> 2252 bytes .../notifications/resources/index.html | 91 ++++++++++++++++++ .../QtWebEngineCore/typesystem_webenginecore.xml | 3 + sources/pyside6/PySide6/glue/qtwebenginecore.cpp | 17 ++++ 9 files changed, 321 insertions(+) create mode 100644 examples/webenginewidgets/notifications/doc/notifications.png create mode 100644 examples/webenginewidgets/notifications/doc/notifications.rst create mode 100644 examples/webenginewidgets/notifications/main.py create mode 100644 examples/webenginewidgets/notifications/notificationpopup.py create mode 100644 examples/webenginewidgets/notifications/notifications.pyproject create mode 100644 examples/webenginewidgets/notifications/resources/icon.png create mode 100644 examples/webenginewidgets/notifications/resources/index.html diff --git a/examples/webenginewidgets/notifications/doc/notifications.png b/examples/webenginewidgets/notifications/doc/notifications.png new file mode 100644 index 000000000..3540be8d1 Binary files /dev/null and b/examples/webenginewidgets/notifications/doc/notifications.png differ diff --git a/examples/webenginewidgets/notifications/doc/notifications.rst b/examples/webenginewidgets/notifications/doc/notifications.rst new file mode 100644 index 000000000..a06ebfbc5 --- /dev/null +++ b/examples/webenginewidgets/notifications/doc/notifications.rst @@ -0,0 +1,8 @@ +WebEngine Notifications Example +=============================== + +Python port of C++ `WebEngine Notifications `_ + +.. image:: notifications.png + :width: 400 + :alt: Notifications Example Screenshot diff --git a/examples/webenginewidgets/notifications/main.py b/examples/webenginewidgets/notifications/main.py new file mode 100644 index 000000000..defc79477 --- /dev/null +++ b/examples/webenginewidgets/notifications/main.py @@ -0,0 +1,94 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +"""PySide6 WebEngineWidgets Notifications Example""" + +import sys +from pathlib import Path + +from PySide6.QtCore import QUrl, QCoreApplication +from PySide6.QtWidgets import QApplication +from PySide6.QtWebEngineCore import QWebEnginePage +from PySide6.QtWebEngineWidgets import QWebEngineView +from PySide6.QtGui import QDesktopServices + +from notificationpopup import NotificationPopup + + +class WebEnginePage(QWebEnginePage): + def __init__(self, parent): + super().__init__(parent) + + def acceptNavigationRequest(self, url: QUrl, *_): + if url.scheme != "https": + return True + QDesktopServices.openUrl(url) + return False + + +if __name__ == '__main__': + + src_dir = Path(__file__).resolve().parent + QCoreApplication.setOrganizationName("QtProject") + app = QApplication(sys.argv) + view = QWebEngineView() + + # set custom page to open all page's links for https scheme in system browser + view.setPage(WebEnginePage(view)) + + def set_feature_permission(origin: QUrl, feature: QWebEnginePage.Feature): + if feature != QWebEnginePage.Notifications: + return + + view.page().setFeaturePermission(origin, feature, QWebEnginePage.PermissionGrantedByUser) + + view.page().featurePermissionRequested.connect(set_feature_permission) + profile = view.page().profile() + popup = NotificationPopup(view) + + def presentNotification(notification): + popup.present(notification) + + profile.setNotificationPresenter(presentNotification) + view.resize(640, 480) + view.show() + view.setUrl(QUrl.fromLocalFile(src_dir / "resources" / "index.html")) + + sys.exit(app.exec()) diff --git a/examples/webenginewidgets/notifications/notificationpopup.py b/examples/webenginewidgets/notifications/notificationpopup.py new file mode 100644 index 000000000..e06c3a330 --- /dev/null +++ b/examples/webenginewidgets/notifications/notificationpopup.py @@ -0,0 +1,105 @@ +############################################################################# +## +## Copyright (C) 2022 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Qt for Python examples of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:BSD$ +## You may use this file under the terms of the BSD license as follows: +## +## "Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions are +## met: +## * Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## * Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in +## the documentation and/or other materials provided with the +## distribution. +## * Neither the name of The Qt Company Ltd nor the names of its +## contributors may be used to endorse or promote products derived +## from this software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +## +## $QT_END_LICENSE$ +## +############################################################################# + +from PySide6.QtCore import Qt, QTimer, QPoint, Slot +from PySide6.QtWidgets import (QWidget, QHBoxLayout, QLabel, QVBoxLayout, QSpacerItem, QSizePolicy, + QPushButton) +from PySide6.QtWebEngineCore import QWebEngineNotification +from PySide6.QtGui import QPixmap, QMouseEvent + + +class NotificationPopup(QWidget): + def __init__(self, parent) -> None: + super().__init__(parent) + self.notification = None + self.m_icon, self.m_title, self.m_message = QLabel(), QLabel(), QLabel() + self.setWindowFlags(Qt.ToolTip) + + rootLayout = QHBoxLayout(self) + rootLayout.addWidget(self.m_icon) + + bodyLayout = QVBoxLayout() + rootLayout.addLayout(bodyLayout) + + titleLayout = QHBoxLayout() + bodyLayout.addLayout(titleLayout) + + titleLayout.addWidget(self.m_title) + titleLayout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding)) + + close = QPushButton("Close") + titleLayout.addWidget(close) + close.clicked.connect(self.onClosed) + + bodyLayout.addWidget(self.m_message) + self.adjustSize() + + def present(self, newNotification: QWebEngineNotification): + if self.notification: + self.notification.close() + + self.notification = newNotification + + self.m_title.setText("" + self.notification.title() + "") + self.m_message.setText(self.notification.message()) + self.m_icon.setPixmap(QPixmap.fromImage(self.notification.icon()) + .scaledToHeight(self.m_icon.height())) + + self.show() + self.notification.show() + + self.notification.closed.connect(self.onClosed) + QTimer.singleShot(10000, lambda: self.onClosed()) + + self.move(self.parentWidget().mapToGlobal(self.parentWidget().rect().bottomRight() - + QPoint(self.width() + 10, self.height() + 10))) + + @Slot() + def onClosed(self): + self.hide() + if self.notification: + self.notification.close() + self.notification = None + + def mouseReleaseEvent(self, event: QMouseEvent) -> None: + QWidget.mouseReleaseEvent(event) + if self.notification and event.button() == Qt.LeftButton: + self.notification.click() + self.onClosed() diff --git a/examples/webenginewidgets/notifications/notifications.pyproject b/examples/webenginewidgets/notifications/notifications.pyproject new file mode 100644 index 000000000..0a3d3c4c5 --- /dev/null +++ b/examples/webenginewidgets/notifications/notifications.pyproject @@ -0,0 +1,3 @@ +{ + "files": ["main.py", "notificationpopup.py"] +} diff --git a/examples/webenginewidgets/notifications/resources/icon.png b/examples/webenginewidgets/notifications/resources/icon.png new file mode 100644 index 000000000..4c3870c06 Binary files /dev/null and b/examples/webenginewidgets/notifications/resources/icon.png differ diff --git a/examples/webenginewidgets/notifications/resources/index.html b/examples/webenginewidgets/notifications/resources/index.html new file mode 100644 index 000000000..99dbac683 --- /dev/null +++ b/examples/webenginewidgets/notifications/resources/index.html @@ -0,0 +1,91 @@ + + + +Web Notifications Example + + + +

Click the button to send a notification

+ + + +

+ + +


+ +

+ + + +


+ +

More info can be found on:

+ + + diff --git a/sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml b/sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml index 478621736..9514100e4 100644 --- a/sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml +++ b/sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml @@ -111,6 +111,9 @@ + + + diff --git a/sources/pyside6/PySide6/glue/qtwebenginecore.cpp b/sources/pyside6/PySide6/glue/qtwebenginecore.cpp index 41fe99440..a0a08af40 100644 --- a/sources/pyside6/PySide6/glue/qtwebenginecore.cpp +++ b/sources/pyside6/PySide6/glue/qtwebenginecore.cpp @@ -53,3 +53,20 @@ auto callback = [callable](const QWebEngineCookieStore::FilterRequest& filterReq }; %CPPSELF.%FUNCTION_NAME(callback); // @snippet qwebenginecookiestore-setcookiefilter + +// @snippet qwebengineprofile-setnotificationpresenter +auto callable = %PYARG_1; +auto callback = [callable](std::unique_ptr webEngineNotification) -> void +{ + Shiboken::GilState state; + Shiboken::AutoDecRef arglist(PyTuple_New(1)); + PyTuple_SET_ITEM(arglist.object(), 0, + Shiboken::Conversions::pointerToPython( + SbkPySide6_QtWebEngineCoreTypes[SBK_QWEBENGINENOTIFICATION_IDX], + webEngineNotification.release())); + Py_INCREF(callable); + PyObject_CallObject(callable, arglist); + Py_DECREF(callable); +}; +%CPPSELF.%FUNCTION_NAME(callback); +// @snippet qwebengineprofile-setnotificationpresenter -- cgit v1.2.3