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. How can attributes of a widget that triggered a clicked.connect function call be accessed?
QtWS25 Last Chance

How can attributes of a widget that triggered a clicked.connect function call be accessed?

Scheduled Pinned Locked Moved Solved Qt for Python
7 Posts 3 Posters 1.8k Views
  • 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.
  • S Offline
    S Offline
    Science_1
    wrote on 18 Aug 2022, 19:32 last edited by Science_1
    #1

    As a novice exercise, I (the novice) set myself the task of creating horizontal lines of widgets in a QVBoxLayout. Sound simple, right? The thing is, because the number of record sets is not fixed, the number of horizontal widget sets needed is not fixed either, so my program has to create these widget sets on the fly, one for each set of data, and then access that data (the QWidget attributes) later on.

    As an extremely simple example that gets to the heart of my issue, the following code creates 5 QPushButton widgets on the fly, and then attempts to display the name of whichever button a user presses, in a label using a .clicked.connect(self.update_label) call. If the called function can tell which button invoked it, the name of the button that was pressed should show up in the label, otherwise an attempt-counter and a failure message is shown on the label instead.

    It seems that identifying whichever widget made a function call and accessing it's attributes should be a VERY basic capability, but I haven't been able to search up any examples related to my problem. How would a more experienced Qt Coder accomplish it?

    A successful answer will provide a short, working code which accomplishes what my example program is attempting (and failing) to do. With a working example, I can redirect my studies accordingly. My apologies if mine is an already answered question, as I suspect it is. If so, chalk it up to my ignorance of Qt which prevented me from finding a previously asked question.

    Thank you, and here is my code:

    import sys
    from PyQt6.QtWidgets import (
        QApplication, 
        QMainWindow, 
        QPushButton, 
        QLabel,
        QVBoxLayout,
        QWidget, 
        )
    
    attempt_count = 0 # GLOBAL attempt counter.
    
    class my_window(QMainWindow):
        def __init__(self):
            super().__init__()
            layout = QVBoxLayout()
            
            self.label = QLabel("Name of Button")
            layout.addWidget(self.label)
            
            for cnt in range(1,6):
                txt = str(cnt)+"_Button"
                self.wid = QPushButton(txt)
                self.wid.setCheckable(True)
                self.wid.setObjectName(txt)
                self.wid.clicked.connect(self.update_labels)
                layout.addWidget(self.wid)
            
            container = QWidget()
            container.setLayout(layout)
            self.setCentralWidget(container)
            
        def update_labels(self,state):
            global attempt_count
            attempt_count += 1     
            
            try: # Attempt to show the name of the triggering button in label
                self.label.setText(parent.text()) #<-- What code will display the triggering button name?
                 # (Note: self.label.setText(self.wid.text()) shows '5_Button', the name of the last button created.)
                 # Apparently each new widget created overwrites the one before. I thought of using a master-list,
                 # but, again, how can the function tell which button was clicked?)
              
            except:# If the above code fails, give proof that label is connected and working.
                self.label.setText(str(attempt_count)+") Show button name failed.")
            
    app = QApplication(sys.argv)
    window = my_window()
    window.show()
    app.exec()
    
    J 1 Reply Last reply 18 Aug 2022, 20:36
    0
    • S Science_1
      18 Aug 2022, 19:32

      As a novice exercise, I (the novice) set myself the task of creating horizontal lines of widgets in a QVBoxLayout. Sound simple, right? The thing is, because the number of record sets is not fixed, the number of horizontal widget sets needed is not fixed either, so my program has to create these widget sets on the fly, one for each set of data, and then access that data (the QWidget attributes) later on.

      As an extremely simple example that gets to the heart of my issue, the following code creates 5 QPushButton widgets on the fly, and then attempts to display the name of whichever button a user presses, in a label using a .clicked.connect(self.update_label) call. If the called function can tell which button invoked it, the name of the button that was pressed should show up in the label, otherwise an attempt-counter and a failure message is shown on the label instead.

      It seems that identifying whichever widget made a function call and accessing it's attributes should be a VERY basic capability, but I haven't been able to search up any examples related to my problem. How would a more experienced Qt Coder accomplish it?

      A successful answer will provide a short, working code which accomplishes what my example program is attempting (and failing) to do. With a working example, I can redirect my studies accordingly. My apologies if mine is an already answered question, as I suspect it is. If so, chalk it up to my ignorance of Qt which prevented me from finding a previously asked question.

      Thank you, and here is my code:

      import sys
      from PyQt6.QtWidgets import (
          QApplication, 
          QMainWindow, 
          QPushButton, 
          QLabel,
          QVBoxLayout,
          QWidget, 
          )
      
      attempt_count = 0 # GLOBAL attempt counter.
      
      class my_window(QMainWindow):
          def __init__(self):
              super().__init__()
              layout = QVBoxLayout()
              
              self.label = QLabel("Name of Button")
              layout.addWidget(self.label)
              
              for cnt in range(1,6):
                  txt = str(cnt)+"_Button"
                  self.wid = QPushButton(txt)
                  self.wid.setCheckable(True)
                  self.wid.setObjectName(txt)
                  self.wid.clicked.connect(self.update_labels)
                  layout.addWidget(self.wid)
              
              container = QWidget()
              container.setLayout(layout)
              self.setCentralWidget(container)
              
          def update_labels(self,state):
              global attempt_count
              attempt_count += 1     
              
              try: # Attempt to show the name of the triggering button in label
                  self.label.setText(parent.text()) #<-- What code will display the triggering button name?
                   # (Note: self.label.setText(self.wid.text()) shows '5_Button', the name of the last button created.)
                   # Apparently each new widget created overwrites the one before. I thought of using a master-list,
                   # but, again, how can the function tell which button was clicked?)
                
              except:# If the above code fails, give proof that label is connected and working.
                  self.label.setText(str(attempt_count)+") Show button name failed.")
              
      app = QApplication(sys.argv)
      window = my_window()
      window.show()
      app.exec()
      
      J Offline
      J Offline
      JonB
      wrote on 18 Aug 2022, 20:36 last edited by JonB
      #5

      @SGaist
      Oh, OK! :) Well I started to type this in anyway, so....

      @Science_1

      "How are attributes of a widget that triggered a clicked.connect function call accessed?"

      If this boils down to your question. You just need to know which widget emitted the clicked signal, then you can of course access its attributes. There are two ways:

      • The "older" way is QObject::sender(). This is simple but has drawbacks, noted in the docs.

      • The "newer" way is to take advantage of lambdas, available in both Python and C++. You can use them as the connected slot and pass a parameter of the object which emitted the signal. In Python:

      self.wid.clicked.connect(lambda state, btn=self.wid : self.update_labels(btn))
      
      def update_labels(self, btn):
          print(btn.objectName())
      

      [Hope I got the above syntax right for Python!] This is discussed in more detail in e.g. https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt

      S 1 Reply Last reply 18 Aug 2022, 21:00
      1
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 18 Aug 2022, 20:22 last edited by
        #2

        Hi and welcome to devnet

        QObject::sender comes to mind but watch for the drawbacks.

        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 18 Aug 2022, 20:24
        0
        • S SGaist
          18 Aug 2022, 20:22

          Hi and welcome to devnet

          QObject::sender comes to mind but watch for the drawbacks.

          J Offline
          J Offline
          JonB
          wrote on 18 Aug 2022, 20:24 last edited by JonB
          #3

          @SGaist
          Given which, why do you not suggest a lambda ? :) [I got lost in the length of the question!]

          S 1 Reply Last reply 18 Aug 2022, 20:29
          0
          • J JonB
            18 Aug 2022, 20:24

            @SGaist
            Given which, why do you not suggest a lambda ? :) [I got lost in the length of the question!]

            S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 18 Aug 2022, 20:29 last edited by
            #4

            @JonB one step after the other.

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

            1 Reply Last reply
            0
            • S Science_1
              18 Aug 2022, 19:32

              As a novice exercise, I (the novice) set myself the task of creating horizontal lines of widgets in a QVBoxLayout. Sound simple, right? The thing is, because the number of record sets is not fixed, the number of horizontal widget sets needed is not fixed either, so my program has to create these widget sets on the fly, one for each set of data, and then access that data (the QWidget attributes) later on.

              As an extremely simple example that gets to the heart of my issue, the following code creates 5 QPushButton widgets on the fly, and then attempts to display the name of whichever button a user presses, in a label using a .clicked.connect(self.update_label) call. If the called function can tell which button invoked it, the name of the button that was pressed should show up in the label, otherwise an attempt-counter and a failure message is shown on the label instead.

              It seems that identifying whichever widget made a function call and accessing it's attributes should be a VERY basic capability, but I haven't been able to search up any examples related to my problem. How would a more experienced Qt Coder accomplish it?

              A successful answer will provide a short, working code which accomplishes what my example program is attempting (and failing) to do. With a working example, I can redirect my studies accordingly. My apologies if mine is an already answered question, as I suspect it is. If so, chalk it up to my ignorance of Qt which prevented me from finding a previously asked question.

              Thank you, and here is my code:

              import sys
              from PyQt6.QtWidgets import (
                  QApplication, 
                  QMainWindow, 
                  QPushButton, 
                  QLabel,
                  QVBoxLayout,
                  QWidget, 
                  )
              
              attempt_count = 0 # GLOBAL attempt counter.
              
              class my_window(QMainWindow):
                  def __init__(self):
                      super().__init__()
                      layout = QVBoxLayout()
                      
                      self.label = QLabel("Name of Button")
                      layout.addWidget(self.label)
                      
                      for cnt in range(1,6):
                          txt = str(cnt)+"_Button"
                          self.wid = QPushButton(txt)
                          self.wid.setCheckable(True)
                          self.wid.setObjectName(txt)
                          self.wid.clicked.connect(self.update_labels)
                          layout.addWidget(self.wid)
                      
                      container = QWidget()
                      container.setLayout(layout)
                      self.setCentralWidget(container)
                      
                  def update_labels(self,state):
                      global attempt_count
                      attempt_count += 1     
                      
                      try: # Attempt to show the name of the triggering button in label
                          self.label.setText(parent.text()) #<-- What code will display the triggering button name?
                           # (Note: self.label.setText(self.wid.text()) shows '5_Button', the name of the last button created.)
                           # Apparently each new widget created overwrites the one before. I thought of using a master-list,
                           # but, again, how can the function tell which button was clicked?)
                        
                      except:# If the above code fails, give proof that label is connected and working.
                          self.label.setText(str(attempt_count)+") Show button name failed.")
                      
              app = QApplication(sys.argv)
              window = my_window()
              window.show()
              app.exec()
              
              J Offline
              J Offline
              JonB
              wrote on 18 Aug 2022, 20:36 last edited by JonB
              #5

              @SGaist
              Oh, OK! :) Well I started to type this in anyway, so....

              @Science_1

              "How are attributes of a widget that triggered a clicked.connect function call accessed?"

              If this boils down to your question. You just need to know which widget emitted the clicked signal, then you can of course access its attributes. There are two ways:

              • The "older" way is QObject::sender(). This is simple but has drawbacks, noted in the docs.

              • The "newer" way is to take advantage of lambdas, available in both Python and C++. You can use them as the connected slot and pass a parameter of the object which emitted the signal. In Python:

              self.wid.clicked.connect(lambda state, btn=self.wid : self.update_labels(btn))
              
              def update_labels(self, btn):
                  print(btn.objectName())
              

              [Hope I got the above syntax right for Python!] This is discussed in more detail in e.g. https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt

              S 1 Reply Last reply 18 Aug 2022, 21:00
              1
              • S Offline
                S Offline
                Science_1
                wrote on 18 Aug 2022, 20:49 last edited by Science_1
                #6

                Between @SGaist and @JonB, this question has been answered and resolved. My thanks to both of you, but especially to JonB who gave the key coding I needed to finish my challenge.

                For completeness, here is the final corrected and working python code that creates five buttons on the fly and then displays in the label, the object name of any button pressed.

                import sys
                from PyQt6.QtWidgets import (
                    QApplication, 
                    QMainWindow, 
                    QPushButton, 
                    QLabel,
                    QVBoxLayout,
                    QWidget, 
                    )
                
                class my_window(QMainWindow):
                    def __init__(self):
                        super().__init__()
                        layout = QVBoxLayout()
                        
                        self.label = QLabel("Name of Button")
                        layout.addWidget(self.label)
                        
                        for cnt in range(1,6):
                            txt = str(cnt)+"_Button"
                            wid = QPushButton(txt)
                            wid.setCheckable(True)
                            wid.setObjectName(txt)            
                            wid.clicked.connect(lambda state, btn=wid : self.update_labels(btn))
                            layout.addWidget(wid)
                        
                        container = QWidget()
                        container.setLayout(layout)
                        self.setCentralWidget(container)
                        
                    def update_labels(self, btn):
                        self.label.setText(btn.objectName())
                          
                app = QApplication(sys.argv)
                window = my_window()
                window.show()
                app.exec()
                
                1 Reply Last reply
                0
                • J JonB
                  18 Aug 2022, 20:36

                  @SGaist
                  Oh, OK! :) Well I started to type this in anyway, so....

                  @Science_1

                  "How are attributes of a widget that triggered a clicked.connect function call accessed?"

                  If this boils down to your question. You just need to know which widget emitted the clicked signal, then you can of course access its attributes. There are two ways:

                  • The "older" way is QObject::sender(). This is simple but has drawbacks, noted in the docs.

                  • The "newer" way is to take advantage of lambdas, available in both Python and C++. You can use them as the connected slot and pass a parameter of the object which emitted the signal. In Python:

                  self.wid.clicked.connect(lambda state, btn=self.wid : self.update_labels(btn))
                  
                  def update_labels(self, btn):
                      print(btn.objectName())
                  

                  [Hope I got the above syntax right for Python!] This is discussed in more detail in e.g. https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt

                  S Offline
                  S Offline
                  Science_1
                  wrote on 18 Aug 2022, 21:00 last edited by Science_1
                  #7

                  It has been mentioned that .sender() has it's drawbacks, yet in reading over https://stackoverflow.com/questions/35819538/using-lambda-expression-to-connect-slots-in-pyqt I see that this lambda method has a potential drawback of its own; a potential garbage-collection issue leading to a memory leak, if you connect your signal to a lambda slot with a reference to self. I bring this warning to your attention because it seems important, even though it may be considered outside the topic of this thread. Read about it on the stackoverflow thread, found at the link, if you think it is a concern. There are ways to juggle the garbage collection issue. Of course if your lambda use widget is never deleted during the run of your program, the issue never arises.

                  1 Reply Last reply
                  0

                  6/7

                  18 Aug 2022, 20:49

                  • Login

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