Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function
Forum Updated to NodeBB v4.3 + New Features

Problem sending a class variable (string) via TCPSocket when it has been modified in a SLOT function

Scheduled Pinned Locked Moved Solved Qt for Python
17 Posts 3 Posters 582 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • JonBJ JonB

    @JesusM97
    In that 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.

    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/first self.clientTcpSocket ever gets disconnected....

    J Offline
    J Offline
    JesusM97
    wrote on last edited by
    #7

    @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:
    example.PNG

    JonBJ 1 Reply Last reply
    0
    • J JesusM97

      @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:
      example.PNG

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #8

      @JesusM97
      OK.
      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.

      At the end of deleteTcpSocket(self) append

      self.tcpServer.close()
      

      Any change in behaviour?

      J 1 Reply Last reply
      0
      • JonBJ JonB

        @JesusM97
        OK.
        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.

        At the end of deleteTcpSocket(self) append

        self.tcpServer.close()
        

        Any change in behaviour?

        J Offline
        J Offline
        JesusM97
        wrote on last edited by
        #9

        @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!

        JonBJ 1 Reply Last reply
        0
        • J JesusM97

          @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!

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #10

          @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 the self.tcpServer = QTcpServer() did not get destroyed, and it was still "listening" for a new connection from self.tcpServer.listen(QHostAddress.Any, 5555). When you then created a fresh instance you executed that line a second time. You then had two instances of QTcpServer 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 old self.senialSelected = senial value from the first self.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 the MyClass instance (together with e.g. any self.tcpServer = QTcpServer() member variables, so the QTcpServer() 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 the self instance is destroyed, unlike the case with a connect(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 or PySide2 together with slot and something about instance not freed, there must be something out there for this.

          J 1 Reply Last reply
          1
          • JonBJ JonB

            @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 the self.tcpServer = QTcpServer() did not get destroyed, and it was still "listening" for a new connection from self.tcpServer.listen(QHostAddress.Any, 5555). When you then created a fresh instance you executed that line a second time. You then had two instances of QTcpServer 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 old self.senialSelected = senial value from the first self.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 the MyClass instance (together with e.g. any self.tcpServer = QTcpServer() member variables, so the QTcpServer() 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 the self instance is destroyed, unlike the case with a connect(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 or PySide2 together with slot and something about instance not freed, there must be something out there for this.

            J Offline
            J Offline
            JesusM97
            wrote on last edited by JesusM97
            #11

            @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 function self.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.

            JonBJ 1 Reply Last reply
            0
            • J JesusM97

              @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 function self.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.

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #12

              @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 use QObject.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"....

              J 1 Reply Last reply
              0
              • JonBJ JonB

                @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 use QObject.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"....

                J Offline
                J Offline
                JesusM97
                wrote on last edited by JesusM97
                #13

                @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!

                JonBJ 1 Reply Last reply
                0
                • J JesusM97

                  @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!

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #14

                  @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 ALL connect()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 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.

                  J 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @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 ALL connect()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 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.

                    J Offline
                    J Offline
                    JesusM97
                    wrote on last edited by JesusM97
                    #15

                    @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 through QObject.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 has QActions that need the current item selected. The QSender is the QAction so I can't access any QListWidget function to get that item so I use lambda funcion to pass the paremeter through the connect method.

                    1 Reply Last reply
                    0
                    • SGaistS Offline
                      SGaistS Offline
                      SGaist
                      Lifetime Qt Champion
                      wrote on last edited by
                      #16

                      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.

                      Interested in AI ? www.idiap.ch
                      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                      J 1 Reply Last reply
                      2
                      • SGaistS SGaist

                        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.

                        J Offline
                        J Offline
                        JesusM97
                        wrote on last edited by
                        #17

                        @SGaist Oh I didn't think on that. Thanks!

                        1 Reply Last reply
                        1

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved