aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-02-04 15:54:11 +0100
committerCristián Maureira-Fredes <cristian.maureira-fredes@qt.io>2020-02-11 19:46:27 +0100
commitd4757ce27b3a675a54a6cc1d57c8b1987087299d (patch)
treeb1d71d4399aea182e52436aff8c425fe8d2d5aa6
parent8681f3a11c458ca9b092e96bbd24e51603342d6c (diff)
Add the webchannel standalone example
Task-number: PYSIDE-1199 Change-Id: Icc138844b0cb5e7ccb502cbe840fc578fad8ca8f Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r--examples/webchannel/standalone/core.py62
-rw-r--r--examples/webchannel/standalone/dialog.py68
-rw-r--r--examples/webchannel/standalone/dialog.ui48
-rw-r--r--examples/webchannel/standalone/index.html79
-rw-r--r--examples/webchannel/standalone/main.py99
-rw-r--r--examples/webchannel/standalone/standalone.pyproject4
-rw-r--r--examples/webchannel/standalone/ui_dialog.py55
-rw-r--r--examples/webchannel/standalone/websocketclientwrapper.py72
-rw-r--r--examples/webchannel/standalone/websockettransport.py88
9 files changed, 575 insertions, 0 deletions
diff --git a/examples/webchannel/standalone/core.py b/examples/webchannel/standalone/core.py
new file mode 100644
index 000000000..9fb056496
--- /dev/null
+++ b/examples/webchannel/standalone/core.py
@@ -0,0 +1,62 @@
+#############################################################################
+##
+## Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://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 PySide2.QtCore import QObject, Signal, Slot
+
+
+class Core(QObject):
+ """An instance of this class gets published over the WebChannel and is then
+ accessible to HTML clients."""
+ sendText = Signal(str)
+
+ def __init__(self, dialog, parent=None):
+ super(Core, self).__init__(parent)
+ self._dialog = dialog
+ self._dialog.sendText.connect(self._emit_send_text)
+
+ @Slot(str)
+ def _emit_send_text(self, text):
+ self.sendText.emit(text)
+
+ @Slot(str)
+ def receiveText(self, text):
+ self._dialog.displayMessage("Received message: {}".format(text))
diff --git a/examples/webchannel/standalone/dialog.py b/examples/webchannel/standalone/dialog.py
new file mode 100644
index 000000000..45951deb9
--- /dev/null
+++ b/examples/webchannel/standalone/dialog.py
@@ -0,0 +1,68 @@
+#############################################################################
+##
+## Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://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 PySide2.QtCore import Signal, Slot
+from PySide2.QtWidgets import QDialog
+from ui_dialog import Ui_Dialog
+
+
+class Dialog(QDialog):
+ sendText = Signal(str)
+
+ def __init__(self, parent=None):
+ super(Dialog, self).__init__(parent)
+ self._ui = Ui_Dialog()
+ self._ui.setupUi(self)
+ self._ui.send.clicked.connect(self.clicked)
+
+ @Slot(str)
+ def displayMessage(self, message):
+ self._ui.output.appendPlainText(message)
+
+ @Slot()
+ def clicked(self):
+ text = self._ui.input.text()
+ if not text:
+ return
+ self.sendText.emit(text)
+ self.displayMessage("Sent message: {}".format(text))
+ self._ui.input.clear()
diff --git a/examples/webchannel/standalone/dialog.ui b/examples/webchannel/standalone/dialog.ui
new file mode 100644
index 000000000..056a3f587
--- /dev/null
+++ b/examples/webchannel/standalone/dialog.ui
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QLineEdit" name="input">
+ <property name="placeholderText">
+ <string>Message Contents</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="send">
+ <property name="text">
+ <string>Send</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2">
+ <widget class="QPlainTextEdit" name="output">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="plainText">
+ <string notr="true">Initializing WebChannel...</string>
+ </property>
+ <property name="backgroundVisible">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/webchannel/standalone/index.html b/examples/webchannel/standalone/index.html
new file mode 100644
index 000000000..7c042cd0c
--- /dev/null
+++ b/examples/webchannel/standalone/index.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <script type="text/javascript" src="./qwebchannel.js"></script>
+ <script type="text/javascript">
+ //BEGIN SETUP
+ function output(message) {
+ var output = document.getElementById("output");
+ output.innerHTML = output.innerHTML + message + "\n";
+ }
+ window.onload = function() {
+ if (location.search != "")
+ var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]);
+ else
+ var baseUrl = "ws://localhost:12345";
+
+ output("Connecting to WebSocket server at " + baseUrl + ".");
+ var socket = new WebSocket(baseUrl);
+
+ socket.onclose = function() {
+ console.error("web channel closed");
+ };
+ socket.onerror = function(error) {
+ console.error("web channel error: " + error);
+ };
+ socket.onopen = function() {
+ output("WebSocket connected, setting up QWebChannel.");
+ new QWebChannel(socket, function(channel) {
+ // make core object accessible globally
+ window.core = channel.objects.core;
+
+ document.getElementById("send").onclick = function() {
+ var input = document.getElementById("input");
+ var text = input.value;
+ if (!text) {
+ return;
+ }
+
+ output("Sent message: " + text);
+ input.value = "";
+ core.receiveText(text);
+ }
+
+ core.sendText.connect(function(message) {
+ output("Received message: " + message);
+ });
+
+ core.receiveText("Client connected, ready to send/receive messages!");
+ output("Connected to WebChannel, ready to send/receive messages!");
+ });
+ }
+ }
+ //END SETUP
+ </script>
+ <style type="text/css">
+ html {
+ height: 100%;
+ width: 100%;
+ }
+ #input {
+ width: 400px;
+ margin: 0 10px 0 0;
+ }
+ #send {
+ width: 90px;
+ margin: 0;
+ }
+ #output {
+ width: 500px;
+ height: 300px;
+ }
+ </style>
+ </head>
+ <body>
+ <textarea id="output"></textarea><br />
+ <input id="input" /><input type="submit" id="send" value="Send" onclick="javascript:click();" />
+ </body>
+</html>
diff --git a/examples/webchannel/standalone/main.py b/examples/webchannel/standalone/main.py
new file mode 100644
index 000000000..d3119141f
--- /dev/null
+++ b/examples/webchannel/standalone/main.py
@@ -0,0 +1,99 @@
+#############################################################################
+##
+## Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://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$
+##
+#############################################################################
+
+
+import os
+import sys
+
+from PySide2.QtWidgets import QApplication
+from PySide2.QtGui import QDesktopServices
+from PySide2.QtNetwork import QHostAddress, QSslSocket
+from PySide2.QtCore import (QFile, QFileInfo, QUrl)
+from PySide2.QtWebChannel import QWebChannel
+from PySide2.QtWebSockets import QWebSocketServer
+
+from dialog import Dialog
+from core import Core
+from websocketclientwrapper import WebSocketClientWrapper
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ if not QSslSocket.supportsSsl():
+ print('The example requires SSL support.')
+ sys.exit(-1)
+ cur_dir = os.path.dirname(os.path.abspath(__file__))
+ jsFileInfo = QFileInfo(cur_dir + "/qwebchannel.js")
+ if not jsFileInfo.exists():
+ QFile.copy(":/qtwebchannel/qwebchannel.js",
+ jsFileInfo.absoluteFilePath())
+
+ # setup the QWebSocketServer
+ server = QWebSocketServer("QWebChannel Standalone Example Server",
+ QWebSocketServer.NonSecureMode)
+ if not server.listen(QHostAddress.LocalHost, 12345):
+ print("Failed to open web socket server.")
+ sys.exit(-1)
+
+ # wrap WebSocket clients in QWebChannelAbstractTransport objects
+ clientWrapper = WebSocketClientWrapper(server)
+
+ # setup the channel
+ channel = QWebChannel()
+ clientWrapper.clientConnected.connect(channel.connectTo)
+
+ # setup the UI
+ dialog = Dialog()
+
+ # setup the core and publish it to the QWebChannel
+ core = Core(dialog)
+ channel.registerObject("core", core)
+
+ # open a browser window with the client HTML page
+ url = QUrl.fromLocalFile(cur_dir + "/index.html")
+ QDesktopServices.openUrl(url)
+
+ message = "Initialization complete, opening browser at {}.".format(
+ url.toDisplayString())
+ dialog.displayMessage(message)
+ dialog.show()
+
+ sys.exit(app.exec_())
diff --git a/examples/webchannel/standalone/standalone.pyproject b/examples/webchannel/standalone/standalone.pyproject
new file mode 100644
index 000000000..b4fcdfa8e
--- /dev/null
+++ b/examples/webchannel/standalone/standalone.pyproject
@@ -0,0 +1,4 @@
+{
+ "files": ["main.py", "core.py", "dialog.py", "websocketclientwrapper.py",
+ "websockettransport.py", "dialog.ui", "index.html"]
+}
diff --git a/examples/webchannel/standalone/ui_dialog.py b/examples/webchannel/standalone/ui_dialog.py
new file mode 100644
index 000000000..873edba10
--- /dev/null
+++ b/examples/webchannel/standalone/ui_dialog.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+################################################################################
+## Form generated from reading UI file 'dialog.ui'
+##
+## Created by: Qt User Interface Compiler version 5.14.1
+##
+## WARNING! All changes made in this file will be lost when recompiling UI file!
+################################################################################
+
+from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint,
+ QRect, QSize, QUrl, Qt)
+from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
+ QFontDatabase, QIcon, QLinearGradient, QPalette, QPainter, QPixmap,
+ QRadialGradient)
+from PySide2.QtWidgets import *
+
+
+class Ui_Dialog(object):
+ def setupUi(self, Dialog):
+ if not Dialog.objectName():
+ Dialog.setObjectName(u"Dialog")
+ Dialog.resize(400, 300)
+ self.gridLayout = QGridLayout(Dialog)
+ self.gridLayout.setObjectName(u"gridLayout")
+ self.input = QLineEdit(Dialog)
+ self.input.setObjectName(u"input")
+
+ self.gridLayout.addWidget(self.input, 1, 0, 1, 1)
+
+ self.send = QPushButton(Dialog)
+ self.send.setObjectName(u"send")
+
+ self.gridLayout.addWidget(self.send, 1, 1, 1, 1)
+
+ self.output = QPlainTextEdit(Dialog)
+ self.output.setObjectName(u"output")
+ self.output.setReadOnly(True)
+ self.output.setPlainText(u"Initializing WebChannel...")
+ self.output.setBackgroundVisible(False)
+
+ self.gridLayout.addWidget(self.output, 0, 0, 1, 2)
+
+
+ self.retranslateUi(Dialog)
+
+ QMetaObject.connectSlotsByName(Dialog)
+ # setupUi
+
+ def retranslateUi(self, Dialog):
+ Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
+ self.input.setPlaceholderText(QCoreApplication.translate("Dialog", u"Message Contents", None))
+ self.send.setText(QCoreApplication.translate("Dialog", u"Send", None))
+ # retranslateUi
+
diff --git a/examples/webchannel/standalone/websocketclientwrapper.py b/examples/webchannel/standalone/websocketclientwrapper.py
new file mode 100644
index 000000000..24505b03b
--- /dev/null
+++ b/examples/webchannel/standalone/websocketclientwrapper.py
@@ -0,0 +1,72 @@
+#############################################################################
+##
+## Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://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 PySide2.QtCore import QObject, Signal, Slot
+
+from websockettransport import WebSocketTransport
+
+
+class WebSocketClientWrapper(QObject):
+ """Wraps connected QWebSockets clients in WebSocketTransport objects.
+
+ This code is all that is required to connect incoming WebSockets to
+ the WebChannel. Any kind of remote JavaScript client that supports
+ WebSockets can thus receive messages and access the published objects.
+ """
+ clientConnected = Signal(WebSocketTransport)
+
+ def __init__(self, server, parent=None):
+ """Construct the client wrapper with the given parent. All clients
+ connecting to the QWebSocketServer will be automatically wrapped
+ in WebSocketTransport objects."""
+ super(WebSocketClientWrapper, self).__init__(parent)
+ self._server = server
+ self._server.newConnection.connect(self.handleNewConnection)
+ self._transports = []
+
+ @Slot()
+ def handleNewConnection(self):
+ """Wrap an incoming WebSocket connection in a WebSocketTransport
+ object."""
+ socket = self._server.nextPendingConnection()
+ transport = WebSocketTransport(socket)
+ self._transports.append(transport)
+ self.clientConnected.emit(transport)
diff --git a/examples/webchannel/standalone/websockettransport.py b/examples/webchannel/standalone/websockettransport.py
new file mode 100644
index 000000000..4e42e7674
--- /dev/null
+++ b/examples/webchannel/standalone/websockettransport.py
@@ -0,0 +1,88 @@
+#############################################################################
+##
+## Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: http://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 PySide2.QtWebChannel import QWebChannelAbstractTransport
+from PySide2.QtCore import QByteArray, QJsonDocument, Slot
+
+
+class WebSocketTransport(QWebChannelAbstractTransport):
+ """QWebChannelAbstractSocket implementation using a QWebSocket internally.
+
+ The transport delegates all messages received over the QWebSocket over
+ its textMessageReceived signal. Analogously, all calls to
+ sendTextMessage will be sent over the QWebSocket to the remote client.
+ """
+
+ def __init__(self, socket):
+ """Construct the transport object and wrap the given socket.
+ The socket is also set as the parent of the transport object."""
+ super(WebSocketTransport, self).__init__(socket)
+ self._socket = socket
+ self._socket.textMessageReceived.connect(self.textMessageReceived)
+ self._socket.disconnected.connect(self._disconnected)
+
+ def __del__(self):
+ """Destroys the WebSocketTransport."""
+ self._socket.deleteLater()
+
+ def _disconnected(self):
+ self.deleteLater()
+
+ def sendMessage(self, message):
+ """Serialize the JSON message and send it as a text message via the
+ WebSocket to the client."""
+ doc = QJsonDocument(message)
+ json_message = str(doc.toJson(QJsonDocument.Compact), "utf-8")
+ self._socket.sendTextMessage(json_message)
+
+ @Slot(str)
+ def textMessageReceived(self, message_data_in):
+ """Deserialize the stringified JSON messageData and emit
+ messageReceived."""
+ message_data = QByteArray(bytes(message_data_in, encoding='utf8'))
+ message = QJsonDocument.fromJson(message_data)
+ if message.isNull():
+ print("Failed to parse text message as JSON object:", message_data)
+ return
+ if not message.isObject():
+ print("Received JSON message that is not an object: ", message_data)
+ return
+ self.messageReceived.emit(message.object(), self)