aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShyamnath Premnadh <Shyamnath.Premnadh@qt.io>2022-05-09 00:42:57 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-05-18 15:42:58 +0000
commit9fe9c110c5a951f4440015f2a36ac9bc949cd6cc (patch)
tree8bb0b6c4b0b4914921310be872c4fe4a8c82f6bf
parent06dd32d94cfc452b2d199d525455af6397f77f3a (diff)
webenginecore - QtWebEngineProfile setNotificationPresenter
- added setNotificationPresenter(...) - added QtWebEngineWidgets Notifications example which uses setNotificationPresenter Task-number: PYSIDE-1912 Change-Id: Ib8fdf7879d6e54bcae47dcc89be6bd5a861a8846 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> (cherry picked from commit cd394249fc77063a2bd6ba7a7633fdbc10621475) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--examples/webenginewidgets/notifications/doc/notifications.pngbin0 -> 21374 bytes
-rw-r--r--examples/webenginewidgets/notifications/doc/notifications.rst8
-rw-r--r--examples/webenginewidgets/notifications/main.py94
-rw-r--r--examples/webenginewidgets/notifications/notificationpopup.py105
-rw-r--r--examples/webenginewidgets/notifications/notifications.pyproject3
-rw-r--r--examples/webenginewidgets/notifications/resources/icon.pngbin0 -> 2252 bytes
-rw-r--r--examples/webenginewidgets/notifications/resources/index.html91
-rw-r--r--sources/pyside6/PySide6/QtWebEngineCore/typesystem_webenginecore.xml3
-rw-r--r--sources/pyside6/PySide6/glue/qtwebenginecore.cpp17
9 files changed, 321 insertions, 0 deletions
diff --git a/examples/webenginewidgets/notifications/doc/notifications.png b/examples/webenginewidgets/notifications/doc/notifications.png
new file mode 100644
index 000000000..3540be8d1
--- /dev/null
+++ b/examples/webenginewidgets/notifications/doc/notifications.png
Binary files 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 <https://doc.qt.io/qt-6/qtwebengine-webenginewidgets-notifications-example.html>`_
+
+.. 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("<b>" + self.notification.title() + "</b>")
+ 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
--- /dev/null
+++ b/examples/webenginewidgets/notifications/resources/icon.png
Binary files 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 @@
+<!doctype html>
+<html>
+<head>
+<title>Web Notifications Example</title>
+<script>
+ var notificationsCreated = 0
+
+ function getPermission() { return document.Notification }
+ function resetPermission(permission = 'default') {
+ document.Notification = permission
+ document.getElementById('state').value = getPermission()
+ }
+
+ function createNotification() {
+ let title = 'Notification #' + ++notificationsCreated
+ let options = { body: 'Visit doc.qt.io for more info!', icon: 'icon.png', }
+
+ let notification = new Notification(title, options)
+ document.notification = notification
+
+ notification.onerror = function(error) {
+ document.getElementById('act').value += ' with error'
+ document.notification = null
+ }
+ notification.onshow = function() {
+ document.getElementById('act').value += ', shown'
+ document.getElementById('close').style.display = 'inline'
+ }
+ notification.onclick = function() {
+ document.getElementById('act').value += ', clicked'
+ }
+ notification.onclose = function() {
+ if (document.notification && notification == document.notification) {
+ document.getElementById('act').value += ' and closed'
+ document.getElementById('close').style.display = 'none'
+ document.notification = null
+ }
+ }
+
+ console.log('...notification created [Title: ' + title + ']')
+ document.getElementById('act').value = 'Notification was created'
+ }
+
+ function onMakeNotification() {
+ if (getPermission() == 'granted') {
+ createNotification()
+ } else if (getPermission() == 'denied') {
+ setTimeout(function() {
+ if (window.confirm('Notifications are disabled!\n' +
+ 'Permission needs to be granted by user. Reset?'))
+ resetPermission()
+ }, 1)
+ } else {
+ Notification.requestPermission().then(function (permission) {
+ console.info('notifications request: ' + permission)
+ resetPermission(permission)
+ if (permission == 'granted')
+ createNotification()
+ })
+ }
+ }
+
+ function closeNotification() { if (document.notification) document.notification.close() }
+
+ document.addEventListener('DOMContentLoaded', function() {
+ resetPermission(Notification.permission) })
+</script>
+</head>
+<body style='text-align:center;'>
+ <h3>Click the button to send a notification</h3>
+
+ <button onclick='onMakeNotification()'>Notify!</button>
+
+ <p>
+ <output id='act'></output>
+ <button id='close' style='display: none;' onclick='closeNotification()'>Close</button>
+ </p><br>
+
+ <p>
+ <label for='state'>Permission:</label>
+ <output id='state'></output>
+ <button onclick='resetPermission()'>Reset</button>
+ </p><br>
+
+ <h4>More info can be found on:</h4>
+ <ul style='list-style-type: none;'>
+ <li>W3 <a href='https://www.w3.org/TR/notifications'>Web Notifications</a> standard</li>
+ <li>Documentation for <a href='https://doc.qt.io'>Qt WebEngine</a> module</li>
+ </ul>
+</body>
+</html>
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 @@
<object-type name="QWebEngineProfile">
<enum-type name="HttpCacheType"/>
<enum-type name="PersistentCookiesPolicy"/>
+ <add-function signature="setNotificationPresenter(PyCallable* @notificationPresenter@)">
+ <inject-code class="target" position="beginning" file="../glue/qtwebenginecore.cpp" snippet="qwebengineprofile-setnotificationpresenter"/>
+ </add-function>
</object-type>
<object-type name="QWebEngineNewWindowRequest">
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<QWebEngineNotification> 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