1 2############################################################################# 3## 4## Copyright (C) 2013 Riverbank Computing Limited. 5## Copyright (C) 2016 The Qt Company Ltd. 6## Contact: http://www.qt.io/licensing/ 7## 8## This file is part of the Qt for Python examples of the Qt Toolkit. 9## 10## $QT_BEGIN_LICENSE:BSD$ 11## You may use this file under the terms of the BSD license as follows: 12## 13## "Redistribution and use in source and binary forms, with or without 14## modification, are permitted provided that the following conditions are 15## met: 16## * Redistributions of source code must retain the above copyright 17## notice, this list of conditions and the following disclaimer. 18## * Redistributions in binary form must reproduce the above copyright 19## notice, this list of conditions and the following disclaimer in 20## the documentation and/or other materials provided with the 21## distribution. 22## * Neither the name of The Qt Company Ltd nor the names of its 23## contributors may be used to endorse or promote products derived 24## from this software without specific prior written permission. 25## 26## 27## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 30## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 31## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 33## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 38## 39## $QT_END_LICENSE$ 40## 41############################################################################# 42 43"""PySide2 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt""" 44 45from PySide2.QtCore import (Signal, QDataStream, QMutex, QMutexLocker, 46 QThread, QWaitCondition) 47from PySide2.QtGui import QIntValidator 48from PySide2.QtWidgets import (QApplication, QDialogButtonBox, QGridLayout, 49 QLabel, QLineEdit, QMessageBox, QPushButton, QWidget) 50from PySide2.QtNetwork import (QAbstractSocket, QHostAddress, QNetworkInterface, 51 QTcpSocket) 52 53 54class FortuneThread(QThread): 55 newFortune = Signal(str) 56 57 error = Signal(int, str) 58 59 def __init__(self, parent=None): 60 super(FortuneThread, self).__init__(parent) 61 62 self.quit = False 63 self.hostName = '' 64 self.cond = QWaitCondition() 65 self.mutex = QMutex() 66 self.port = 0 67 68 def __del__(self): 69 self.mutex.lock() 70 self.quit = True 71 self.cond.wakeOne() 72 self.mutex.unlock() 73 self.wait() 74 75 def requestNewFortune(self, hostname, port): 76 locker = QMutexLocker(self.mutex) 77 self.hostName = hostname 78 self.port = port 79 if not self.isRunning(): 80 self.start() 81 else: 82 self.cond.wakeOne() 83 84 def run(self): 85 self.mutex.lock() 86 serverName = self.hostName 87 serverPort = self.port 88 self.mutex.unlock() 89 90 while not self.quit: 91 Timeout = 5 * 1000 92 93 socket = QTcpSocket() 94 socket.connectToHost(serverName, serverPort) 95 96 if not socket.waitForConnected(Timeout): 97 self.error.emit(socket.error(), socket.errorString()) 98 return 99 100 while socket.bytesAvailable() < 2: 101 if not socket.waitForReadyRead(Timeout): 102 self.error.emit(socket.error(), socket.errorString()) 103 return 104 105 instr = QDataStream(socket) 106 instr.setVersion(QDataStream.Qt_4_0) 107 blockSize = instr.readUInt16() 108 109 while socket.bytesAvailable() < blockSize: 110 if not socket.waitForReadyRead(Timeout): 111 self.error.emit(socket.error(), socket.errorString()) 112 return 113 114 self.mutex.lock() 115 fortune = instr.readQString() 116 self.newFortune.emit(fortune) 117 118 self.cond.wait(self.mutex) 119 serverName = self.hostName 120 serverPort = self.port 121 self.mutex.unlock() 122 123 124class BlockingClient(QWidget): 125 def __init__(self, parent=None): 126 super(BlockingClient, self).__init__(parent) 127 128 self.thread = FortuneThread() 129 self.currentFortune = '' 130 131 hostLabel = QLabel("&Server name:") 132 portLabel = QLabel("S&erver port:") 133 134 for ipAddress in QNetworkInterface.allAddresses(): 135 if ipAddress != QHostAddress.LocalHost and ipAddress.toIPv4Address() != 0: 136 break 137 else: 138 ipAddress = QHostAddress(QHostAddress.LocalHost) 139 140 ipAddress = ipAddress.toString() 141 142 self.hostLineEdit = QLineEdit(ipAddress) 143 self.portLineEdit = QLineEdit() 144 self.portLineEdit.setValidator(QIntValidator(1, 65535, self)) 145 146 hostLabel.setBuddy(self.hostLineEdit) 147 portLabel.setBuddy(self.portLineEdit) 148 149 self.statusLabel = QLabel( 150 "This example requires that you run the Fortune Server example as well.") 151 self.statusLabel.setWordWrap(True) 152 153 self.getFortuneButton = QPushButton("Get Fortune") 154 self.getFortuneButton.setDefault(True) 155 self.getFortuneButton.setEnabled(False) 156 157 quitButton = QPushButton("Quit") 158 159 buttonBox = QDialogButtonBox() 160 buttonBox.addButton(self.getFortuneButton, QDialogButtonBox.ActionRole) 161 buttonBox.addButton(quitButton, QDialogButtonBox.RejectRole) 162 163 self.getFortuneButton.clicked.connect(self.requestNewFortune) 164 quitButton.clicked.connect(self.close) 165 self.hostLineEdit.textChanged.connect(self.enableGetFortuneButton) 166 self.portLineEdit.textChanged.connect(self.enableGetFortuneButton) 167 self.thread.newFortune.connect(self.showFortune) 168 self.thread.error.connect(self.displayError) 169 170 mainLayout = QGridLayout() 171 mainLayout.addWidget(hostLabel, 0, 0) 172 mainLayout.addWidget(self.hostLineEdit, 0, 1) 173 mainLayout.addWidget(portLabel, 1, 0) 174 mainLayout.addWidget(self.portLineEdit, 1, 1) 175 mainLayout.addWidget(self.statusLabel, 2, 0, 1, 2) 176 mainLayout.addWidget(buttonBox, 3, 0, 1, 2) 177 self.setLayout(mainLayout) 178 179 self.setWindowTitle("Blocking Fortune Client") 180 self.portLineEdit.setFocus() 181 182 def requestNewFortune(self): 183 self.getFortuneButton.setEnabled(False) 184 self.thread.requestNewFortune(self.hostLineEdit.text(), 185 int(self.portLineEdit.text())) 186 187 def showFortune(self, nextFortune): 188 if nextFortune == self.currentFortune: 189 self.requestNewFortune() 190 return 191 192 self.currentFortune = nextFortune 193 self.statusLabel.setText(self.currentFortune) 194 self.getFortuneButton.setEnabled(True) 195 196 def displayError(self, socketError, message): 197 if socketError == QAbstractSocket.HostNotFoundError: 198 QMessageBox.information(self, "Blocking Fortune Client", 199 "The host was not found. Please check the host and port " 200 "settings.") 201 elif socketError == QAbstractSocket.ConnectionRefusedError: 202 QMessageBox.information(self, "Blocking Fortune Client", 203 "The connection was refused by the peer. Make sure the " 204 "fortune server is running, and check that the host name " 205 "and port settings are correct.") 206 else: 207 QMessageBox.information(self, "Blocking Fortune Client", 208 "The following error occurred: %s." % message) 209 210 self.getFortuneButton.setEnabled(True) 211 212 def enableGetFortuneButton(self): 213 self.getFortuneButton.setEnabled(self.hostLineEdit.text() != '' and 214 self.portLineEdit.text() != '') 215 216 217if __name__ == '__main__': 218 219 import sys 220 221 app = QApplication(sys.argv) 222 client = BlockingClient() 223 client.show() 224 sys.exit(app.exec_()) 225