Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function
-
@JesusM97
In that case you create a new instance ofAdditionalWindow
, and a newself.senialComboBox = QComboBox()
each time. It certainly won't remember the previous value you setself.senial
to.Meanwhile let's have:
self.clientTcpSocket.disconnected.connect(lambda: print("self.clientTcpSocket.disconnected") : self.clientTcpSocket.deleteLater())
I don't know if the
:
between the two statements is correct for Python/lambda on one line, you'll have to sort that out. But I'd like to know whether the original/firstself.clientTcpSocket
ever gets disconnected....@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
hat case you create a new instance of AdditionalWindow, and a new self.senialComboBox = QComboBox() each time. It certainly won't remember the previous value you set self.senial to.
self.senial
is a function variable, not the class one.self.senialSelected
is the class variable. It default value is "Antorchas". The first selected value which works well is shown in the console screenshots (first two prints). I don't know if this is exactly what you mean.@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I don't know if the : between the two statements is correct for Python/lambda on one line, you'll have to sort that out. But I'd like to know whether the original/first self.clientTcpSocket ever gets disconnected....
I created a function and I connected the disconnected SIGNAL with that function:
self.clientTcpSocket.disconnected.connect(lambda: self.deleteTcpSocket()) def deleteTcpSocket(self): print("self.clientTcpSocket.disconnected") self.clientTcpSocket.deleteLater()
This is the console output:
-
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
hat case you create a new instance of AdditionalWindow, and a new self.senialComboBox = QComboBox() each time. It certainly won't remember the previous value you set self.senial to.
self.senial
is a function variable, not the class one.self.senialSelected
is the class variable. It default value is "Antorchas". The first selected value which works well is shown in the console screenshots (first two prints). I don't know if this is exactly what you mean.@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I don't know if the : between the two statements is correct for Python/lambda on one line, you'll have to sort that out. But I'd like to know whether the original/first self.clientTcpSocket ever gets disconnected....
I created a function and I connected the disconnected SIGNAL with that function:
self.clientTcpSocket.disconnected.connect(lambda: self.deleteTcpSocket()) def deleteTcpSocket(self): print("self.clientTcpSocket.disconnected") self.clientTcpSocket.deleteLater()
This is the console output:
@JesusM97
OK.
Another thought: because of the way you are using lambdas for your connects I believe you are preventing Python from destroying youradditionalWindow
instances. I now wonder whether multiple instances ofself.tcpServer = QTcpServer()
are left listening for connections.At the end of
deleteTcpSocket(self)
appendself.tcpServer.close()
Any change in behaviour?
-
@JesusM97
OK.
Another thought: because of the way you are using lambdas for your connects I believe you are preventing Python from destroying youradditionalWindow
instances. I now wonder whether multiple instances ofself.tcpServer = QTcpServer()
are left listening for connections.At the end of
deleteTcpSocket(self)
appendself.tcpServer.close()
Any change in behaviour?
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
Another thought: because of the way you are using lambdas for your connects I believe you are preventing Python from destroying your additionalWindow instances. I now wonder whether multiple instances of self.tcpServer = QTcpServer() are left listening for connections.
I tried to add the attribute DeleteOnClose to the additional window to ensure it is fully destroyed but sometimes I get an exit error which seems to be an access violation (l read it here) and I don't know why is happening so I eliminated that attribute.
self.setAttribute(Qt.WA_DeleteOnClose, True)
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
At the end of deleteTcpSocket(self) append
self.tcpServer.close()Any change in behaviour?
Yes! Now the variable is beeing sending with the right value. Thank!
-
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
Another thought: because of the way you are using lambdas for your connects I believe you are preventing Python from destroying your additionalWindow instances. I now wonder whether multiple instances of self.tcpServer = QTcpServer() are left listening for connections.
I tried to add the attribute DeleteOnClose to the additional window to ensure it is fully destroyed but sometimes I get an exit error which seems to be an access violation (l read it here) and I don't know why is happening so I eliminated that attribute.
self.setAttribute(Qt.WA_DeleteOnClose, True)
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
At the end of deleteTcpSocket(self) append
self.tcpServer.close()Any change in behaviour?
Yes! Now the variable is beeing sending with the right value. Thank!
@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
Yes! Now the variable is beeing sending with the right value. Thank!
Yes, I kind of had a feeling this might be the root cause of the problem.... What this is telling you is that when you closed the first
additionalWindow
it did not destroy that window/widget, it left it in existence but not visible. That meant theself.tcpServer = QTcpServer()
did not get destroyed, and it was still "listening" for a new connection fromself.tcpServer.listen(QHostAddress.Any, 5555)
. When you then created a fresh instance you executed that line a second time. You then had two instances ofQTcpServer
both listening for any incoming connections on port 5555. That is bad! It turned out that the first one created accepted the new client connection, and that instance still had the oldself.senialSelected = senial
value from the firstself.senialComboBox = QComboBox()
, which you could no longer see/interact with. Ugh!You have a real problem with Python lambdas here, especially with Qt. Your problem comes from the fact that you are using lambdas as your slots on signals. This is an issue with Python/Qt. Let me explain.
If you have a class with:
class MyClass(QObject): def __init__(self): self.something.signal.connect(self.slot) def slot(self): pass
the signal is connected to a slot which is a method in self. If you remove all live references to a created
MyClass
instance (e.g. you close the window) Python will do what it normally does: once all references gone/closed, its garbage collector will notice and release theMyClass
instance (together with e.g. anyself.tcpServer = QTcpServer()
member variables, so theQTcpServer()
gets freed/destroyed. This is a good situation.The problem comes when you use a lambda in a connect:
self.something.signal.connect(lambda: self.slot())
Now Python creates an "anonymous" function/method for a lambda. And Python sees that as a "live" reference to the
self
class instance. It is not clever enough to understand that "anonymous" function/method can be destroyed if theself
instance is destroyed, unlike the case with aconnect(self.method)
. So it thinks the instance is "still in use", and so does not release/destroy the instance.This is a real problem. Basically for the code you showed so far you could have not used any lambdas and written member slot methods instead, then you would not have had this problem. If you do have to use a lambda for a slot you will experience this nasty issue.
If you need to read up about this try Googling for something about
PyQt5
orPySide2
together withslot
and something aboutinstance not freed
, there must be something out there for this. -
@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
Yes! Now the variable is beeing sending with the right value. Thank!
Yes, I kind of had a feeling this might be the root cause of the problem.... What this is telling you is that when you closed the first
additionalWindow
it did not destroy that window/widget, it left it in existence but not visible. That meant theself.tcpServer = QTcpServer()
did not get destroyed, and it was still "listening" for a new connection fromself.tcpServer.listen(QHostAddress.Any, 5555)
. When you then created a fresh instance you executed that line a second time. You then had two instances ofQTcpServer
both listening for any incoming connections on port 5555. That is bad! It turned out that the first one created accepted the new client connection, and that instance still had the oldself.senialSelected = senial
value from the firstself.senialComboBox = QComboBox()
, which you could no longer see/interact with. Ugh!You have a real problem with Python lambdas here, especially with Qt. Your problem comes from the fact that you are using lambdas as your slots on signals. This is an issue with Python/Qt. Let me explain.
If you have a class with:
class MyClass(QObject): def __init__(self): self.something.signal.connect(self.slot) def slot(self): pass
the signal is connected to a slot which is a method in self. If you remove all live references to a created
MyClass
instance (e.g. you close the window) Python will do what it normally does: once all references gone/closed, its garbage collector will notice and release theMyClass
instance (together with e.g. anyself.tcpServer = QTcpServer()
member variables, so theQTcpServer()
gets freed/destroyed. This is a good situation.The problem comes when you use a lambda in a connect:
self.something.signal.connect(lambda: self.slot())
Now Python creates an "anonymous" function/method for a lambda. And Python sees that as a "live" reference to the
self
class instance. It is not clever enough to understand that "anonymous" function/method can be destroyed if theself
instance is destroyed, unlike the case with aconnect(self.method)
. So it thinks the instance is "still in use", and so does not release/destroy the instance.This is a real problem. Basically for the code you showed so far you could have not used any lambdas and written member slot methods instead, then you would not have had this problem. If you do have to use a lambda for a slot you will experience this nasty issue.
If you need to read up about this try Googling for something about
PyQt5
orPySide2
together withslot
and something aboutinstance not freed
, there must be something out there for this.@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
This is a real problem. Basically for the code you showed so far you could have not used any lambdas and written member slot methods instead, then you would not have had this problem. If you do have to use a lambda for a slot you will experience this nasty issue.
I tried to use the connect method without lambda functions but I had problems. For example, in my main window class I have a
QListWidget
. I tried to connect the double click event to a funtion in the__init__()
in this way:
self.listaFases.itemDoubleClicked.connect(self.editarItem(self.listaFases))
The problem was that the functionself.editarItem()
was called at this right moment, when I connected the signal to that function. And this happens to me with other SIGNALS. The solution I found on the Internet was to define the SLOT functions as lambda funtions. If there is any other way to solve it I can modify the code without problems, it is not very big yet. -
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
This is a real problem. Basically for the code you showed so far you could have not used any lambdas and written member slot methods instead, then you would not have had this problem. If you do have to use a lambda for a slot you will experience this nasty issue.
I tried to use the connect method without lambda functions but I had problems. For example, in my main window class I have a
QListWidget
. I tried to connect the double click event to a funtion in the__init__()
in this way:
self.listaFases.itemDoubleClicked.connect(self.editarItem(self.listaFases))
The problem was that the functionself.editarItem()
was called at this right moment, when I connected the signal to that function. And this happens to me with other SIGNALS. The solution I found on the Internet was to define the SLOT functions as lambda funtions. If there is any other way to solve it I can modify the code without problems, it is not very big yet.@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
self.listaFases.itemDoubleClicked.connect(self.editarItem(self.listaFases))
I do not understand this. It is (should not) be legal? You can do that (pass a parameter) with a lambda, but not with the non-lambda connection. A normal, non-lambda connection must look like:
self.object.signal.connect(self.slot_method)
No more and no less. See that there are no parentheses and no parameters allowed.
For this case, where the
self.listaFases
parameter is the sender of the signal (self.listaFases.itemDoubleClicked.connect()
), you can useQObject.sender()
in the slot to obtain that without using a lambda with a parameter. It's a shame because lambdas are preferable to that.I did not say you cannot use lambdas in Python. I used them when I wrote Python/PyQt5 code, where I had to. What I said is you must be careful of the consequences, and understand them, because of this Python-no-release-instance-with-lambda-connection issue. Like I said if you want to understand the consequences you need to Google judiciously for this. Examples:
- https://stackoverflow.com/questions/47941743/lifetime-of-object-in-lambda-connected-to-pyqtsignal
- https://stackoverflow.com/questions/60771418/does-using-a-lambda-slot-function-in-a-signal-cause-a-memory-leak
- https://stackoverflow.com/questions/61871629/memory-profiler-while-using-lambda-expression-to-connect-slots
- https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt/48501804#48501804
but there are probably more discussions. I Googled
pyqt5 lambda free instance
, but there may be other hits if you try various similar terms.UPDATE
Oohh, I discovered I myself raised this issue on this forum 4 years ago! Read through
https://forum.qt.io/topic/92825/python-pyqt-qdialog-with-connect-lambda-leaks
I see my last post there, https://forum.qt.io/topic/92825/python-pyqt-qdialog-with-connect-lambda-leaks/13, discusses answer/code I got back from PyQt5 mailing list, though I did not deploy it myself.You can see the whole topic is "nasty"....
-
@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
self.listaFases.itemDoubleClicked.connect(self.editarItem(self.listaFases))
I do not understand this. It is (should not) be legal? You can do that (pass a parameter) with a lambda, but not with the non-lambda connection. A normal, non-lambda connection must look like:
self.object.signal.connect(self.slot_method)
No more and no less. See that there are no parentheses and no parameters allowed.
For this case, where the
self.listaFases
parameter is the sender of the signal (self.listaFases.itemDoubleClicked.connect()
), you can useQObject.sender()
in the slot to obtain that without using a lambda with a parameter. It's a shame because lambdas are preferable to that.I did not say you cannot use lambdas in Python. I used them when I wrote Python/PyQt5 code, where I had to. What I said is you must be careful of the consequences, and understand them, because of this Python-no-release-instance-with-lambda-connection issue. Like I said if you want to understand the consequences you need to Google judiciously for this. Examples:
- https://stackoverflow.com/questions/47941743/lifetime-of-object-in-lambda-connected-to-pyqtsignal
- https://stackoverflow.com/questions/60771418/does-using-a-lambda-slot-function-in-a-signal-cause-a-memory-leak
- https://stackoverflow.com/questions/61871629/memory-profiler-while-using-lambda-expression-to-connect-slots
- https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt/48501804#48501804
but there are probably more discussions. I Googled
pyqt5 lambda free instance
, but there may be other hits if you try various similar terms.UPDATE
Oohh, I discovered I myself raised this issue on this forum 4 years ago! Read through
https://forum.qt.io/topic/92825/python-pyqt-qdialog-with-connect-lambda-leaks
I see my last post there, https://forum.qt.io/topic/92825/python-pyqt-qdialog-with-connect-lambda-leaks/13, discusses answer/code I got back from PyQt5 mailing list, though I did not deploy it myself.You can see the whole topic is "nasty"....
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I do not understand this. It is (should not) be legal? You can do that (pass a parameter) with a lambda, but not with the non-lambda connection. A normal, non-lambda connection must look like:
self.object.signal.connect(self.slot_method)No more and no less. See that there are no parentheses and no parameters allowed.
Maybe thats the problem. I was defining the SLOTS in a wrong way (including the parentheses and using parameters inside the connect) and python executed the function in that moment (when I connected it to the SIGNAL).
I can't try it now because I leave the computer but I will try as soon as possible and post the results.Thanks!
-
@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I do not understand this. It is (should not) be legal? You can do that (pass a parameter) with a lambda, but not with the non-lambda connection. A normal, non-lambda connection must look like:
self.object.signal.connect(self.slot_method)No more and no less. See that there are no parentheses and no parameters allowed.
Maybe thats the problem. I was defining the SLOTS in a wrong way (including the parentheses and using parameters inside the connect) and python executed the function in that moment (when I connected it to the SIGNAL).
I can't try it now because I leave the computer but I will try as soon as possible and post the results.Thanks!
@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I was defining the SLOTS in a wrong way (including the parentheses and using parameters inside the connect) and python executed the function in that moment (when I connected it to the SIGNAL).
Yes, that was what was happening the way you wrote it.
One final update on this whole Python lambda-slot thing, for you or anyone else reading this.
If I understand correctly, to allow destruction/garbage collection of a
QObject
which has lambda(s) on its signal(s), you can achieve that if you explicitly disconnect ALLconnect()
s which have a lambda-slot. But you must be very careful to do that.In your example it was even worse: your
additionalWindow
has:self.tcpServer = QTcpServer() self.tcpServer.newConnection.connect(lambda: self.socketConnet())
So you had a member variable which itself had a lambda-connect, and that needs disconnecting too. I believe
self.tcpServer.newConnection.disconnect()
will disconnect all signals including lambdas? You should test this in place of theself.tcpServer.close()
I got you to add to make it work. Does it still work if you do this? Then you will know what sort of thing is required. -
@JesusM97 said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
I was defining the SLOTS in a wrong way (including the parentheses and using parameters inside the connect) and python executed the function in that moment (when I connected it to the SIGNAL).
Yes, that was what was happening the way you wrote it.
One final update on this whole Python lambda-slot thing, for you or anyone else reading this.
If I understand correctly, to allow destruction/garbage collection of a
QObject
which has lambda(s) on its signal(s), you can achieve that if you explicitly disconnect ALLconnect()
s which have a lambda-slot. But you must be very careful to do that.In your example it was even worse: your
additionalWindow
has:self.tcpServer = QTcpServer() self.tcpServer.newConnection.connect(lambda: self.socketConnet())
So you had a member variable which itself had a lambda-connect, and that needs disconnecting too. I believe
self.tcpServer.newConnection.disconnect()
will disconnect all signals including lambdas? You should test this in place of theself.tcpServer.close()
I got you to add to make it work. Does it still work if you do this? Then you will know what sort of thing is required.@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
So you had a member variable which itself had a lambda-connect, and that needs disconnecting too. I believe self.tcpServer.newConnection.disconnect() will disconnect all signals including lambdas? You should test this in place of the self.tcpServer.close() I got you to add to make it work. Does it still work if you do this? Then you will know what sort of thing is required.
It doesn't work. It takes the default value of
self.senialSelected
, not even the old one.
I will try now to replace lambda functions with normal SLOT functions and pass parameters throughQObject.sender
as you said.@JonB said in Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function:
Yes, that was what was happening the way you wrote it.
I changed some SLOTS and they work well. There are SLOTS that I can't change because sometimes I can't get the parameters through the sender. For example, I have a context menu when i right click over a
QListWidget
. This context menu hasQActions
that need the current item selected. The QSender is theQAction
so I can't access anyQListWidget
function to get that item so I use lambda funcion to pass the paremeter through the connect method. -
Hi,
You don't need a lambda for that. The signal gives you the coordinates of the mouse. You can use the QListWidget::indexAt method to retrieve the index.
-
Hi,
You don't need a lambda for that. The signal gives you the coordinates of the mouse. You can use the QListWidget::indexAt method to retrieve the index.