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
45import random
46
47from PyQt5.QtCore import (pyqtSignal, QByteArray, QDataStream, QIODevice,
48        QThread)
49from PyQt5.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel,
50        QMessageBox, QPushButton, QVBoxLayout)
51from PyQt5.QtNetwork import (QHostAddress, QNetworkInterface, QTcpServer,
52        QTcpSocket)
53
54
55class FortuneThread(QThread):
56    error = pyqtSignal(QTcpSocket.SocketError)
57
58    def __init__(self, socketDescriptor, fortune, parent):
59        super(FortuneThread, self).__init__(parent)
60
61        self.socketDescriptor = socketDescriptor
62        self.text = fortune
63
64    def run(self):
65        tcpSocket = QTcpSocket()
66        if not tcpSocket.setSocketDescriptor(self.socketDescriptor):
67            self.error.emit(tcpSocket.error())
68            return
69
70        block = QByteArray()
71        outstr = QDataStream(block, QIODevice.WriteOnly)
72        outstr.setVersion(QDataStream.Qt_4_0)
73        outstr.writeUInt16(0)
74        outstr.writeQString(self.text)
75        outstr.device().seek(0)
76        outstr.writeUInt16(block.size() - 2)
77
78        tcpSocket.write(block)
79        tcpSocket.disconnectFromHost()
80        tcpSocket.waitForDisconnected()
81
82
83class FortuneServer(QTcpServer):
84    FORTUNES = (
85        "You've been leading a dog's life. Stay off the furniture.",
86        "You've got to think about tomorrow.",
87        "You will be surprised by a loud noise.",
88        "You will feel hungry again in another hour.",
89        "You might have mail.",
90        "You cannot kill time without injuring eternity.",
91        "Computers are not intelligent. They only think they are.")
92
93    def incomingConnection(self, socketDescriptor):
94        fortune = self.FORTUNES[random.randint(0, len(self.FORTUNES) - 1)]
95
96        thread = FortuneThread(socketDescriptor, fortune, self)
97        thread.finished.connect(thread.deleteLater)
98        thread.start()
99
100
101class Dialog(QDialog):
102    def __init__(self, parent=None):
103        super(Dialog, self).__init__(parent)
104
105        self.server = FortuneServer()
106
107        statusLabel = QLabel()
108        statusLabel.setWordWrap(True)
109        quitButton = QPushButton("Quit")
110        quitButton.setAutoDefault(False)
111
112        if not self.server.listen():
113            QMessageBox.critical(self, "Threaded Fortune Server",
114                    "Unable to start the server: %s." % self.server.errorString())
115            self.close()
116            return
117
118        for ipAddress in QNetworkInterface.allAddresses():
119            if ipAddress != QHostAddress.LocalHost and ipAddress.toIPv4Address() != 0:
120                break
121        else:
122            ipAddress = QHostAddress(QHostAddress.LocalHost)
123
124        ipAddress = ipAddress.toString()
125
126        statusLabel.setText("The server is running on\n\nIP: %s\nport: %d\n\n"
127                "Run the Fortune Client example now." % (ipAddress, self.server.serverPort()))
128
129        quitButton.clicked.connect(self.close)
130
131        buttonLayout = QHBoxLayout()
132        buttonLayout.addStretch(1)
133        buttonLayout.addWidget(quitButton)
134        buttonLayout.addStretch(1)
135
136        mainLayout = QVBoxLayout()
137        mainLayout.addWidget(statusLabel)
138        mainLayout.addLayout(buttonLayout)
139        self.setLayout(mainLayout)
140
141        self.setWindowTitle("Threaded Fortune Server")
142
143
144if __name__ == '__main__':
145
146    import sys
147
148    app = QApplication(sys.argv)
149    dialog = Dialog()
150    dialog.show()
151    sys.exit(dialog.exec_())
152