Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-1545

Weird behaviour when connecting signals using lambda in a loop

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Not Evaluated
    • Resolution: Invalid
    • Affects Version/s: 5.15.3, 6.0.3
    • Fix Version/s: None
    • Component/s: PySide
    • Labels:
      None
    • Platform/s:
      Linux/X11

      Description

        In order to be able to pass additional information to the callback, I'm using a lambda function to call the real handler when the signal is triggered.

      Doing so works in general, however there is a specific case where this breaks inexplicably: when done in a loop, feeding the lambda a variable that is part of the loop and is out of its scope.

      I have built a proof of concept based on the basic tutorial:

      import sys
      from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication,QRadioButton, QVBoxLayout, QDialog,QMainWindow, QLabel)
      
      class Form(QDialog):
          def set_label(self,t,x):
              if t:
                  self.lab.setText(x)
                  print(x)
          def __init__(self, parent=None):
              super(Form, self).__init__(parent)
              self.lab = QLabel("ok")
              self.r1 = QRadioButton("1")
              self.r2 = QRadioButton("2")
              layout = QVBoxLayout()
              layout.addWidget(self.lab)
              layout.addWidget(self.r1)
              layout.addWidget(self.r2)
              self.setLayout(layout)
              self.set_label(True,'okok')
              ### This would work
              #self.r1.toggled.connect(lambda t: self.set_label(t,"1"))
              #self.r2.toggled.connect(lambda t: self.set_label(t,"2"))
              for num in [1,2]:
                  if num == 1:
                      x = self.r1
                  else:
                      x = self.r2
                  print(num)
                  print(x)
                  x.toggled.connect(lambda toggled: self.set_label(toggled, f"{num}"))
                  # This would work
                  #x.toggled.connect(lambda toggled,num=num: self.set_label(toggled, f"{num}"))
      
      if __name__ == '__main__':
          app = QApplication(sys.argv)
          form = Form()
          form.show()
          sys.exit(app.exec_())
      
      
      

      This program should write "1" or "2" on its label, depending on which radio button is checked.

      If the connecting loop is replaced by the individual connections, commented above it, it works.

      Oddly enough if the variable is passed explicitly and re-scoped (see the comment below, the only difference being "num=num" as an additional lambda parameter) it also works.

      I have tried reproducing this behaviour in pure python as such:

      F = lambda: None
      def set_f(f):
          global F
          F = f
      def p(a,b):
          print(a,b)
      for x in range(1,4):
          j =  lambda t: p(t,x)
          set_f(j)
          F(2)
      
      

      but this program works as expected, which makes me think that the problem lies somewhere between QT and PySide.

       

      I was able to reproduce this in both PySide2 and PySide6.

       

      I am attaching both my test scripts for convenience.

        Attachments

        1. borked_lambda.py
          1 kB
        2. pure_lambda.py
          0.2 kB
        3. pyside1545.py
          2 kB
        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

            Assignee:
            crmaurei Cristian Maureira-Fredes
            Reporter:
            agrassi Alessandro Grassi
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Gerrit Reviews

                There are no open Gerrit changes