diff options
author | BogDan Vatra <bogdan@kde.org> | 2019-06-11 16:33:57 +0300 |
---|---|---|
committer | BogDan Vatra <bogdan@kde.org> | 2019-06-12 16:11:46 +0300 |
commit | b8a71125f038b5a52d034b804d32ecddd9e19286 (patch) | |
tree | dc4803701dc042baa827a97e89ce72c2734c5cf2 /examples | |
parent | 0e0b5ca1c2075e1a31cecccc82e8deef1f0eb992 (diff) |
Add WebSockets example
Change-Id: I5626b8a835c3b2d717e30b482678b95e20f26d38
Reviewed-by: Brett Stottlemyer <bstottle@ford.com>
Diffstat (limited to 'examples')
9 files changed, 489 insertions, 1 deletions
diff --git a/examples/remoteobjects/remoteobjects.pro b/examples/remoteobjects/remoteobjects.pro index e4dd62a..9b22d21 100644 --- a/examples/remoteobjects/remoteobjects.pro +++ b/examples/remoteobjects/remoteobjects.pro @@ -3,7 +3,8 @@ CONFIG += debug_and_release ordered SUBDIRS = \ server \ cppclient \ - simpleswitch + simpleswitch \ + websockets qtHaveModule(widgets) { SUBDIRS += \ diff --git a/examples/remoteobjects/websockets/common/common.pri b/examples/remoteobjects/websockets/common/common.pri new file mode 100644 index 0000000..48389af --- /dev/null +++ b/examples/remoteobjects/websockets/common/common.pri @@ -0,0 +1,9 @@ +CONFIG -= app_bundle + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/websocketiodevice.h + +SOURCES += \ + $$PWD/websocketiodevice.cpp diff --git a/examples/remoteobjects/websockets/common/websocketiodevice.cpp b/examples/remoteobjects/websockets/common/websocketiodevice.cpp new file mode 100644 index 0000000..4c3e917 --- /dev/null +++ b/examples/remoteobjects/websockets/common/websocketiodevice.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ford Motor Company +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#include "websocketiodevice.h" + +#include <QWebSocket> + +WebSocketIoDevice::WebSocketIoDevice(QWebSocket *webSocket, QObject *parent) + : QIODevice(parent) + , m_socket(webSocket) +{ + open(QIODevice::ReadWrite); + connect(webSocket, &QWebSocket::disconnected, this, &WebSocketIoDevice::disconnected); + connect(webSocket, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray &message){ + m_buffer.append(message); + emit readyRead(); + }); + connect(webSocket, &QWebSocket::bytesWritten, this, &WebSocketIoDevice::bytesWritten); +} + +qint64 WebSocketIoDevice::bytesAvailable() const +{ + return QIODevice::bytesAvailable() + m_buffer.size(); +} + +bool WebSocketIoDevice::isSequential() const +{ + return true; +} + +void WebSocketIoDevice::close() +{ + if (m_socket) + m_socket->close(); +} + +qint64 WebSocketIoDevice::readData(char *data, qint64 maxlen) +{ + auto sz = std::min(int(maxlen), m_buffer.size()); + if (sz <= 0) + return sz; + memcpy(data, m_buffer.constData(), size_t(sz)); + m_buffer.remove(0, sz); + return sz; +} + +qint64 WebSocketIoDevice::writeData(const char *data, qint64 len) +{ + if (m_socket) + return m_socket->sendBinaryMessage(QByteArray{data, int(len)}); + return -1; +} diff --git a/examples/remoteobjects/websockets/common/websocketiodevice.h b/examples/remoteobjects/websockets/common/websocketiodevice.h new file mode 100644 index 0000000..0e5609c --- /dev/null +++ b/examples/remoteobjects/websockets/common/websocketiodevice.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ford Motor Company +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#ifndef WEBSOCKETIODEVICE_H +#define WEBSOCKETIODEVICE_H + +#include <QBuffer> +#include <QIODevice> +#include <QPointer> + +class QWebSocket; + +class WebSocketIoDevice : public QIODevice +{ + Q_OBJECT +public: + WebSocketIoDevice(QWebSocket *webSocket, QObject *parent = nullptr); + +signals: + void disconnected(); + + // QIODevice interface +public: + qint64 bytesAvailable() const override; + bool isSequential() const override; + void close() override; + +protected: + qint64 readData(char *data, qint64 maxlen) override; + qint64 writeData(const char *data, qint64 len) override; + +private: + QPointer<QWebSocket> m_socket; + QByteArray m_buffer; +}; + +#endif // WEBSOCKETIODEVICE_H diff --git a/examples/remoteobjects/websockets/websockets.pro b/examples/remoteobjects/websockets/websockets.pro new file mode 100644 index 0000000..bb2685a --- /dev/null +++ b/examples/remoteobjects/websockets/websockets.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +qtHaveModule(widgets): qtHaveModule(websockets) { + SUBDIRS += \ + wsclient \ + wsserver +} diff --git a/examples/remoteobjects/websockets/wsclient/main.cpp b/examples/remoteobjects/websockets/wsclient/main.cpp new file mode 100644 index 0000000..9cc64fb --- /dev/null +++ b/examples/remoteobjects/websockets/wsclient/main.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ford Motor Company +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ + +#include <QTreeView> +#include <QApplication> +#include <QRemoteObjectNode> +#include <QAbstractItemModelReplica> +#include <QWebSocket> + +#include "websocketiodevice.h" + +int main(int argc, char **argv) +{ + + QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n" + "qt.remoteobjects.warning=false\n" + "qt.remoteobjects.models.debug=false\n" + "qt.remoteobjects.models.debug=false"); + + QApplication app(argc, argv); + + + + QScopedPointer<QWebSocket> webSocket{new QWebSocket}; + WebSocketIoDevice socket(webSocket.data()); + QRemoteObjectNode node; + node.addClientSideConnection(&socket); + node.setHeartbeatInterval(1000); + webSocket->open(QStringLiteral("ws://localhost:8088")); + + QTreeView view; + view.setWindowTitle(QStringLiteral("RemoteView")); + view.resize(640,480); + QScopedPointer<QAbstractItemModelReplica> model(node.acquireModel(QStringLiteral("RemoteModel"))); + view.setModel(model.data()); + view.show(); + + return app.exec(); +} diff --git a/examples/remoteobjects/websockets/wsclient/wsclient.pro b/examples/remoteobjects/websockets/wsclient/wsclient.pro new file mode 100644 index 0000000..f329e4a --- /dev/null +++ b/examples/remoteobjects/websockets/wsclient/wsclient.pro @@ -0,0 +1,9 @@ +QT += widgets remoteobjects websockets +requires(qtConfig(treeview)) + +SOURCES += main.cpp + +include(../common/common.pri) + +target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/websockets/wsclient +INSTALLS += target diff --git a/examples/remoteobjects/websockets/wsserver/main.cpp b/examples/remoteobjects/websockets/wsserver/main.cpp new file mode 100644 index 0000000..8354fdd --- /dev/null +++ b/examples/remoteobjects/websockets/wsserver/main.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ford Motor Company +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtRemoteObjects module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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$ +** +****************************************************************************/ +#include <QTreeView> +#include <QApplication> +#include <QRemoteObjectNode> +#include <QTimer> +#include <QStandardItemModel> +#include <QStandardItem> +#include <QWebSocket> +#include <QWebSocketServer> + +#include "websocketiodevice.h" + +struct TimerHandler : public QObject +{ + Q_OBJECT +public: + QStandardItemModel *model; +public Q_SLOTS: + void changeData() { + Q_ASSERT(model); + Q_ASSERT(model->rowCount() > 50); + Q_ASSERT(model->columnCount() > 1); + for (int i = 10; i < 50; ++i) + model->setData(model->index(i, 1), QColor(Qt::blue), Qt::BackgroundRole); + } + void insertData() { + Q_ASSERT(model); + Q_ASSERT(model->rowCount() > 50); + Q_ASSERT(model->columnCount() > 1); + model->insertRows(2, 9); + for (int i = 2; i < 11; ++i) { + model->setData(model->index(i, 1), QColor(Qt::green), Qt::BackgroundRole); + model->setData(model->index(i, 1), QLatin1String("InsertedRow"), Qt::DisplayRole); + } + } + void removeData() { + model->removeRows(2, 4); + } + + void changeFlags() { + QStandardItem *item = model->item(0, 0); + item->setEnabled(false); + item = item->child(0, 0); + item->setFlags(item->flags() & Qt::ItemIsSelectable); + } + + void moveData() { + model->moveRows(QModelIndex(), 2, 4, QModelIndex(), 10); + } +}; + +QList<QStandardItem*> addChild(int numChildren, int nestingLevel) +{ + QList<QStandardItem*> result; + if (nestingLevel == 0) + return result; + for (int i = 0; i < numChildren; ++i) { + QStandardItem *child = new QStandardItem(QStringLiteral("Child num %1, nesting Level %2").arg(i+1).arg(nestingLevel)); + if (i == 0) + child->appendRow(addChild(numChildren, nestingLevel -1)); + result.push_back(child); + } + return result; +} + +int main(int argc, char *argv[]) +{ + QLoggingCategory::setFilterRules("qt.remoteobjects.debug=false\n" + "qt.remoteobjects.warning=false"); + QApplication app(argc, argv); + + const int modelSize = 100000; + QStringList list; + QStandardItemModel sourceModel; + QStringList hHeaderList; + hHeaderList << QStringLiteral("First Column with spacing") << QStringLiteral("Second Column with spacing"); + sourceModel.setHorizontalHeaderLabels(hHeaderList); + list.reserve(modelSize); + for (int i = 0; i < modelSize; ++i) { + QStandardItem *firstItem = new QStandardItem(QStringLiteral("FancyTextNumber %1").arg(i)); + if (i == 0) + firstItem->appendRow(addChild(2, 2)); + QStandardItem *secondItem = new QStandardItem(QStringLiteral("FancyRow2TextNumber %1").arg(i)); + if (i % 2 == 0) + firstItem->setBackground(Qt::red); + QList<QStandardItem*> row; + row << firstItem << secondItem; + sourceModel.invisibleRootItem()->appendRow(row); + //sourceModel.appendRow(row); + list << QStringLiteral("FancyTextNumber %1").arg(i); + } + + // Needed by QMLModelViewClient + QHash<int,QByteArray> roleNames = { + {Qt::DisplayRole, "_text"}, + {Qt::BackgroundRole, "_color"} + }; + sourceModel.setItemRoleNames(roleNames); + + QVector<int> roles; + roles << Qt::DisplayRole << Qt::BackgroundRole; + + QWebSocketServer webSockServer{QStringLiteral("WS QtRO"), QWebSocketServer::NonSecureMode}; + webSockServer.listen(QHostAddress::LocalHost, 8088); + + QRemoteObjectHost hostNode; + hostNode.setHostUrl(webSockServer.serverAddress().toString(), QRemoteObjectHost::AllowExternalRegistration); + + hostNode.enableRemoting(&sourceModel, QStringLiteral("RemoteModel"), roles); + + QObject::connect(&webSockServer, &QWebSocketServer::newConnection, &hostNode, [&hostNode, &webSockServer]{ + while (auto conn = webSockServer.nextPendingConnection()) { + QObject::connect(conn, &QWebSocket::disconnected, conn, &QWebSocket::deleteLater); + QObject::connect(conn, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), conn, &QWebSocket::deleteLater); + auto ioDevice = new WebSocketIoDevice(conn); + QObject::connect(conn, &QWebSocket::destroyed, ioDevice, &WebSocketIoDevice::deleteLater); + hostNode.addHostSideConnection(ioDevice); + } + }); + + QTreeView view; + view.setWindowTitle(QStringLiteral("SourceView")); + view.setModel(&sourceModel); + view.show(); + TimerHandler handler; + handler.model = &sourceModel; + QTimer::singleShot(5000, &handler, SLOT(changeData())); + QTimer::singleShot(10000, &handler, SLOT(insertData())); + QTimer::singleShot(11000, &handler, SLOT(changeFlags())); + QTimer::singleShot(12000, &handler, SLOT(removeData())); + QTimer::singleShot(13000, &handler, SLOT(moveData())); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/remoteobjects/websockets/wsserver/wsserver.pro b/examples/remoteobjects/websockets/wsserver/wsserver.pro new file mode 100644 index 0000000..e892091 --- /dev/null +++ b/examples/remoteobjects/websockets/wsserver/wsserver.pro @@ -0,0 +1,9 @@ +QT += widgets remoteobjects websockets +requires(qtConfig(treeview)) + +SOURCES += main.cpp + +include(../common/common.pri) + +target.path = $$[QT_INSTALL_EXAMPLES]/remoteobjects/websockets/wsserver +INSTALLS += target |