I am trying to implement a custom TCP Socket and Server using the
PyQt5.QtNetwork.QTcpSocket
and PyQt5.QtNetwork.QTcpServer
as baseclasses. The documentation
states one has to override QTcpServer.incomingConnection
to return
custom Socket objects. This seems to work fine in principle, but I keep stumbling over some odd behavior. Whenn calling QTcpServer.nextPendingConnection
, the method does indeed seem to return the expected MyNewSocket
instance, however when one accesses any attribute or method of the new socket, the interpreter first returns said attribute / method, but then goes on to inform you that
AttributeError: 'NoneType' object has no attribute 'anyAttribute'
which I can find no reasonable explanation for so far.
Here is a MWE that shows the behavior. I am using PyQt5
v5.15.
tcpmodule.py
:
from PyQt5.QtNetwork import QTcpSockert, QTcpServer
class MyNewSocket(QTcpSocket):
"""do some additional stuff to the
original QTcpSocket stuff"""
pass
class MyNewServer(QTcpServer):
def incomingConnection(self, handle):
"""Returns a MyNewSocket instance instead of
original QTcpSocket instance"""
socket = MyNewSocket(self)
socket.setSocketDescriptor(handle)
self.addPendingConnection(socket)
self.newConnection.emit()
server.py
from tcpmodule import MyNewServer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtNetwork import QHostAddress
app = QApplication([])
win = QMainWindow()
server = MyNewServer(win)
server.listen(QHostAddress.SpecialAddress.LocalHost, 9999)
def newConnection():
socket = server.nextPendingConnection()
print("Acces any Attribute ", socket.connected)
print("Socket is ", socket, " Socket Type is", type(socket))
server.newConnection.connect(newConnection)
win.show()
app.exec()
client.py
#!/usr/bin/env python3
from tcpmodule import MyNewSocket
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtNetwork import QHostAddress
app = QApplication([])
win = QMainWindow()
socket = MyNewSocket(win)
socket.connectToHost(QHostAddress.SpecialAddress.LocalHost, 9999)
def connected():
socket.write(b"Hello, World!")
socket.connected.connect(connected)
win.show()
app.exec()
After running the server.py
and subsequent client.py
connection,
I get the following output
Acces any Attribute <bound PYQT_SIGNAL connected of MyNewSocket object at 0x7f4be28597e0>
Socket is <tcpmodule.MyNewSocket object at 0x7f4be28597e0> Socket Type is <class 'tcpmodule.MyNewSocket'>
Traceback (most recent call last):
File "./server.py", line 13, in newConnection
print("Acces any Attribute ", socket.connected)
AttributeError: 'NoneType' object has no attribute 'connected'
Can anyone please tell me what is going on?!
According to the documentation, it seems that incomingConnection()
emits the newConnection()
signal, but, in fact it does not.
By looking at the sources, we see that it just creates the socket, sets its descriptor and appends it to the pending connections:
void QTcpServer::incomingConnection(qintptr socketDescriptor)
{
#if defined (QTCPSERVER_DEBUG)
qDebug("QTcpServer::incomingConnection(%i)", socketDescriptor);
#endif
QTcpSocket *socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescriptor);
addPendingConnection(socket);
}
In fact, the signal is only emitted by the private function readNotification()
, which calls incomingConnection()
and finally emits the signal.
So, you don't have to emit it on your own, because in that case the second time you call nextPendingConnection()
you'll get a nullptr
(None
) as the socket has been already read in the previous call, and there are no more connections available.