1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import QByteArray, Qt
46from PyQt5.QtWidgets import (QApplication, QDialog, QDialogButtonBox, QLabel,
47        QMessageBox, QProgressBar, QPushButton, QVBoxLayout)
48from PyQt5.QtNetwork import QHostAddress, QTcpServer, QTcpSocket
49
50
51class Dialog(QDialog):
52    TotalBytes = 50 * 1024 * 1024
53    PayloadSize = 65536
54
55    def __init__(self, parent=None):
56        super(Dialog, self).__init__(parent)
57
58        self.tcpServer = QTcpServer()
59        self.tcpClient = QTcpSocket()
60        self.bytesToWrite = 0
61        self.bytesWritten = 0
62        self.bytesReceived = 0
63
64        self.clientProgressBar = QProgressBar()
65        self.clientStatusLabel = QLabel("Client ready")
66        self.serverProgressBar = QProgressBar()
67        self.serverStatusLabel = QLabel("Server ready")
68
69        self.startButton = QPushButton("&Start")
70        self.quitButton = QPushButton("&Quit")
71
72        buttonBox = QDialogButtonBox()
73        buttonBox.addButton(self.startButton, QDialogButtonBox.ActionRole)
74        buttonBox.addButton(self.quitButton, QDialogButtonBox.RejectRole)
75
76        self.startButton.clicked.connect(self.start)
77        self.quitButton.clicked.connect(self.close)
78        self.tcpServer.newConnection.connect(self.acceptConnection)
79        self.tcpClient.connected.connect(self.startTransfer)
80        self.tcpClient.bytesWritten.connect(self.updateClientProgress)
81        self.tcpClient.error.connect(self.displayError)
82
83        mainLayout = QVBoxLayout()
84        mainLayout.addWidget(self.clientProgressBar)
85        mainLayout.addWidget(self.clientStatusLabel)
86        mainLayout.addWidget(self.serverProgressBar)
87        mainLayout.addWidget(self.serverStatusLabel)
88        mainLayout.addStretch(1)
89        mainLayout.addSpacing(10)
90        mainLayout.addWidget(buttonBox)
91        self.setLayout(mainLayout)
92
93        self.setWindowTitle("Loopback")
94
95    def start(self):
96        self.startButton.setEnabled(False)
97
98        QApplication.setOverrideCursor(Qt.WaitCursor)
99
100        self.bytesWritten = 0
101        self.bytesReceived = 0
102
103        while not self.tcpServer.isListening() and not self.tcpServer.listen():
104            ret = QMessageBox.critical(self, "Loopback",
105                    "Unable to start the test: %s." % self.tcpServer.errorString(),
106                    QMessageBox.Retry | QMessageBox.Cancel)
107            if ret == QMessageBox.Cancel:
108                return
109
110        self.serverStatusLabel.setText("Listening")
111        self.clientStatusLabel.setText("Connecting")
112
113        self.tcpClient.connectToHost(QHostAddress(QHostAddress.LocalHost), self.tcpServer.serverPort())
114
115    def acceptConnection(self):
116        self.tcpServerConnection = self.tcpServer.nextPendingConnection()
117        self.tcpServerConnection.readyRead.connect(self.updateServerProgress)
118        self.tcpServerConnection.error.connect(self.displayError)
119
120        self.serverStatusLabel.setText("Accepted connection")
121        self.tcpServer.close()
122
123    def startTransfer(self):
124        self.bytesToWrite = Dialog.TotalBytes - self.tcpClient.write(QByteArray(Dialog.PayloadSize, '@'))
125        self.clientStatusLabel.setText("Connected")
126
127    def updateServerProgress(self):
128        self.bytesReceived += self.tcpServerConnection.bytesAvailable()
129        self.tcpServerConnection.readAll()
130
131        self.serverProgressBar.setMaximum(Dialog.TotalBytes)
132        self.serverProgressBar.setValue(self.bytesReceived)
133        self.serverStatusLabel.setText("Received %dMB" % (self.bytesReceived / (1024 * 1024)))
134
135        if self.bytesReceived == Dialog.TotalBytes:
136            self.tcpServerConnection.close()
137            self.startButton.setEnabled(True)
138            QApplication.restoreOverrideCursor()
139
140    def updateClientProgress(self, numBytes):
141        self.bytesWritten += numBytes
142        if self.bytesToWrite > 0:
143            self.bytesToWrite -= self.tcpClient.write(QByteArray(
144                                        min(self.bytesToWrite, Dialog.PayloadSize), '@'))
145
146        self.clientProgressBar.setMaximum(Dialog.TotalBytes)
147        self.clientProgressBar.setValue(self.bytesWritten)
148        self.clientStatusLabel.setText("Sent %dMB" % (self.bytesWritten / (1024 * 1024)))
149
150    def displayError(self, socketError):
151        if socketError == QTcpSocket.RemoteHostClosedError:
152            return
153
154        QMessageBox.information(self, "Network error",
155                "The following error occured: %s." % self.tcpClient.errorString())
156
157        self.tcpClient.close()
158        self.tcpServer.close()
159        self.clientProgressBar.reset()
160        self.serverProgressBar.reset()
161        self.clientStatusLabel.setText("Client ready")
162        self.serverStatusLabel.setText("Server ready")
163        self.startButton.setEnabled(True)
164        QApplication.restoreOverrideCursor()
165
166
167if __name__ == '__main__':
168
169    import sys
170
171    app = QApplication(sys.argv)
172    dialog = Dialog()
173    dialog.show()
174    sys.exit(dialog.exec_())
175