Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Unable to add a widget to a QScrollArea object dynamically



  • Hi, guys! I met two problems when I want to add a widget to a QScrollArea object dynamically. When I comment that line, the new widget I want to add could not be displayed on the screen. Moreover, I found that I could not scroll that area on the screen.

    Thank you very much for any help!

    from PyQt5.QtWidgets import *
    
    
    class MyList(QScrollArea):
        def __init__(self):
            super().__init__()
            self._widget = QWidget()
            self._layout = QVBoxLayout()
            
            # If I comment this line, MyButton._add function will not work, and the screen will not display new widget.
            self._layout.addWidget(QPushButton('test'))
    
            # set layout and widget
            self._widget.setLayout(self._layout)
            self.setWidget(self._widget)
    
            # display settings
            self.setMinimumSize(1024, 540)
    
    
    class MyButton(QPushButton):
        def __init__(self, text, _list):
            super().__init__(text=text)
            self._list = _list
            self.clicked.connect(self._add)
        
    
        def _add(self):
            self._list._layout.addWidget(QPushButton('test'))
    
    
    class MainWindow(QWidget):
        def __init__(self):
            super().__init__()
            self._layout = QVBoxLayout()
            self._my_list = MyList()
            self._my_button = MyButton(text='Add', _list=self._my_list)
            self._layout.addWidget(self._my_list)
            self._layout.addWidget(self._my_button)
    
            # set layout
            self.setLayout(self._layout)
    
            # display settings
            self.setWindowTitle('My Demo')
    
    
    def main():
        app = QApplication([])
        main_window = MainWindow()
    
        main_window.show()
        app.exec_()
    
    
    if __name__ == "__main__":
        main()    
    


  • I figure out the reason with the help of @T.sagiv.
    The solution is that I have to adjust the scrollarea. Just replace self.setMinimumSize(1024, 540) with self.setWidgetResizable(True).


  • Lifetime Qt Champion

    @sunshk1227 said in Unable to add a widget to a QScrollArea object dynamically:

    When I comment that line, the new widget I want to add could not be displayed on the screen

    Of course not. But you did not explain what happens if you do NOT comment it out. So, what is actually the problem?



  • @jsulm Hi, sorry I was not clear. When I comment that out, nothing happens if I click the "Add" button. But I print the number of the widget in main_window._my_list._layout.count(), the number increases correctly.


  • Lifetime Qt Champion

    @sunshk1227 Try to call show() on new buttons:

    def _add(self):
        b = QPushButton('test')
        self._list._layout.addWidget(b)
        b.show()
    


  • @jsulm Hi, thank you for your help! But it seems that it does not work.

    (By the way, if I do NOT comment it out, a new button will be displayed on the screen when I click the "Add" button...)


  • Lifetime Qt Champion

    @sunshk1227 Strange.
    Not a solution for the problem, but an improvement idea: instead of exposing internals of the MyList class (_layout) to the outside world you should make it private and add a method to add new widgets (like buttons).

    class MyList(QScrollArea):
        ...
        def addWidget(self, widget):
            self.__layoutaddWidget(widget)
    

    This way MyButton does not have to know where to add new buttons to the list:

    def _add(self):
            self._list.addWidget(QPushButton('test'))
    

    You could improve your design even more if you remove self._list from MyButton completely and instead emit a signal in MyButton and connect it to a slot in MyList. This way MyButton and MyList do not have to know anything about each other.



  • @jsulm Thank you very much for your suggestions! Got it. But I still have a question... should I further improve my design like this? (In this case, could you please tell me how to connect a function with an additional argument, where I marked by '?' in my code?)
    Thank you again for your advice!

    class MyList(QScrollArea):
        ...
        def _addWidget(self, widget):
            self._layout.addWidget(widget)
    
    
    class MyButton(QPushButton):
        def __init__(self, text):
            super().__init__(text=text)
    
    
    class MainWindow(QWidget):
        def __init__(self):
            ...
    	self._my_list = MyList()
            self._my_button = MyButton(text='Add')
            self._my_button.clicked.connect(self._my_list._addWidget(?))
    	...
        ...
    ...
    

  • Lifetime Qt Champion

    @sunshk1227 said in Unable to add a widget to a QScrollArea object dynamically:

    self._my_button.clicked.connect(self._my_list._addWidget(?))

    This is wrong, should be:

    self._my_button.clicked.connect(self._my_list._addWidget)
    

    In this line you only connect the signal to the slot, you do not call the slot. Slot will be called as soon as signal is emitted.

    class MyButton(QPushButton):
        newWidget = pyqtSignal()
        def __init__(self, text):
            super().__init__(text=text)
            self.clicked.connect(self._add)
       
        def _add(self):
                newWidget.emit(QPushButton('test'))
    
    self._my_button.newWidget.connect(self._my_list._addWidget)
    

    Not sure this 100% correct as I don't use PyQt/PiSide :-)



  • I figure out the reason with the help of @T.sagiv.
    The solution is that I have to adjust the scrollarea. Just replace self.setMinimumSize(1024, 540) with self.setWidgetResizable(True).



  • @jsulm said in Unable to add a widget to a QScrollArea object dynamically:

    Not sure this 100% correct as I don't use PyQt/PiSide :-)

    newWidget.emit(QPushButton('test'))

    Whazzis? :)


  • Lifetime Qt Champion

    @JonB You're the Python guy here :-) It was meant to emit a signal with a parameter.



  • @jsulm
    Yeah but I was suprised at your parameter! Yours is emit newWidget(new QPushButton("test")), is that what you intended? I didn't really read what OP wanted :-; So you want to emit a signal which accepts an unplaced new widget, presumably the slot does something with it? Interesting!


  • Lifetime Qt Champion

    @JonB Yes, the emitter sends new widget to add via signal. The receiver adds it to its UI.


Log in to reply