title: PyQt5 - Adding widget to layout dynamically
body: <p>I'm trying to create a custom widget where the user can dynamically add more [some other] custom widget onto the window. Here is my attempt:</p>
  <pre class="lang-py prettyprint-override"><code>from PyQt5 import QtCore, QtGui, QtWidgets
  from PyQt5.QtCore import Qt
  
  
  class LoadModelWidget(QtWidgets.QWidget):
      def __init__(self, *args, **kwargs):
          super(LoadModelWidget, self).__init__(*args, **kwargs)
  
          # ------------------------ GUI Components ------------------------ #
          self.setFixedHeight(100)
          self._container = QtWidgets.QGroupBox(self)
          self._container.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding))
          self._container.setFixedHeight(100)
          self._container.setFixedWidth(210)
          layout = QtWidgets.QGridLayout()
  
          self._model_to_select_label = QtWidgets.QLabel(&quot;Selected model&quot;)
          layout.addWidget(self._model_to_select_label, 0, 0)
          self._model_to_select_list = QtWidgets.QComboBox()
          layout.addWidget(self._model_to_select_list, 0, 1)
          _slice_method_label = QtWidgets.QLabel(&quot;Slice method&quot;)
          layout.addWidget(_slice_method_label, 1, 0)
          self._list_of_slicing_method = QtWidgets.QComboBox()
          layout.addWidget(self._list_of_slicing_method, 1, 1)
  
          self._bottom_radius_label = QtWidgets.QLabel(&quot;Bottom radius&quot;)
          layout.addWidget(self._bottom_radius_label, 2, 0)
          self._bottom_radius_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._bottom_radius_edit, 2, 1)
          self._top_radius_label = QtWidgets.QLabel(&quot;Top radius&quot;)
          layout.addWidget(self._top_radius_label, 3, 0)
          self._top_radius_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._top_radius_edit, 3, 1)
          self._height_label = QtWidgets.QLabel(&quot;Height&quot;)
          layout.addWidget(self._height_label, 4, 0)
          self._height_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._height_edit, 4, 1)
  
          self._x_dir_label = QtWidgets.QLabel(&quot;X direction&quot;)
          layout.addWidget(self._x_dir_label, 5, 0)
          self._x_dir_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._x_dir_edit, 5, 1)
          self._y_dir_label = QtWidgets.QLabel(&quot;Y direction&quot;)
          layout.addWidget(self._y_dir_label, 6, 0)
          self._y_dir_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._y_dir_edit, 6, 1)
          self._z_dir_label = QtWidgets.QLabel(&quot;Z direction&quot;)
          layout.addWidget(self._z_dir_label, 7, 0)
          self._z_dir_edit = QtWidgets.QLineEdit()
          layout.addWidget(self._z_dir_edit, 7, 1)
  
          self._remove_btn = QtWidgets.QPushButton(&quot;Remove model&quot;)
          layout.addWidget(self._remove_btn, 8, 0)
  
          self._bottom_radius_label.setVisible(False)
          self._bottom_radius_edit.setVisible(False)
          self._top_radius_label.setVisible(False)
          self._top_radius_edit.setVisible(False)
          self._height_label.setVisible(False)
          self._height_edit.setVisible(False)
          self._x_dir_label.setVisible(False)
          self._x_dir_edit.setVisible(False)
          self._y_dir_label.setVisible(False)
          self._y_dir_edit.setVisible(False)
          self._z_dir_label.setVisible(False)
          self._z_dir_edit.setVisible(False)
  
          self._container.setLayout(layout)
  
          # -------------------- Other attributes -------------------- &quot;
          self._filename = &quot;&quot;
  
          # -------------------- Assign slot and signal -------------------- &quot;
          self._list_of_slicing_method.currentIndexChanged.connect(self.slice_method_changed)
          self._remove_btn.clicked.connect(self.remove_model)
  
      def set_model_dir(self, filename, filedir, suggested_slicing_method=&quot;Parallel&quot;):
          # Assign only the name of the file, not the whole stl model
          self._filename = filename
          self._filedir = filedir
  
          self._list_of_slicing_method.addItem(&quot;Parallel&quot;)
          self._list_of_slicing_method.addItem(&quot;Revolution&quot;)
          self._list_of_slicing_method.addItem(&quot;Radial&quot;)
          self._list_of_slicing_method.addItem(&quot;ParallelCurve&quot;)
  
          self._list_of_slicing_method.setCurrentText(suggested_slicing_method)
  
      def get_model_full_dir(self):
          return self._filedir + &quot;\\&quot; + self._filename
  
      def remove_model(self):
          self._filename = &quot;&quot;
          self._filedir = &quot;&quot;
          self._list_of_slicing_method.clear()
  
      def slice_method_changed(self):
          is_parallel = self._list_of_slicing_method.currentText() == &quot;Parallel&quot;
          is_radial = self._list_of_slicing_method.currentText() == &quot;Radial&quot;
  
          self._bottom_radius_label.setVisible(is_radial)
          self._bottom_radius_edit.setVisible(is_radial)
          self._top_radius_label.setVisible(is_radial)
          self._top_radius_edit.setVisible(is_radial)
          self._height_label.setVisible(is_radial)
          self._height_edit.setVisible(is_radial)
  
          self._x_dir_label.setVisible(is_parallel)
          self._x_dir_edit.setVisible(is_parallel)
          self._y_dir_label.setVisible(is_parallel)
          self._y_dir_edit.setVisible(is_parallel)
          self._z_dir_label.setVisible(is_parallel)
          self._z_dir_edit.setVisible(is_parallel)
  
          padding = 50
          minimum_height = 100
          new_height = (self._bottom_radius_label.height() + padding)*is_radial + (self._top_radius_edit.height() + padding)*is_radial + (self._height_label.height() + padding)*is_radial + (self._x_dir_label.height() + padding)*is_parallel + (self._y_dir_label.height() + padding)*is_parallel + (self._z_dir_label.height() + padding)*is_parallel
          if (new_height &gt; minimum_height):
              self._container.setFixedHeight(new_height)
              self.setFixedHeight(new_height)
          else:
              self._container.setFixedHeight(minimum_height)
              self.setFixedHeight(minimum_height)
  
  class LoadModelColumn(QtWidgets.QWidget):
      def __init__(self, *args, **kwargs):
          super(LoadModelColumn, self).__init__(*args, **kwargs)
  
          # ------------------------ GUI Components ------------------------ #
          self._layout = QtWidgets.QGridLayout()
          self._add_model_btn = QtWidgets.QPushButton(&quot;Add model(s)&quot;)
          self._layout.addWidget(self._add_model_btn, 0, 0)
          self._clear_all_btn = QtWidgets.QPushButton(&quot;Clear all&quot;)
          self._layout.addWidget(self._clear_all_btn, 0, 1)
  
          self._all_load_model_widgets = []
  
          self.verticalSpacer = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.MinimumExpanding)
          self._layout.addItem(self.verticalSpacer)
  
          self._load_model_group = QtWidgets.QGroupBox()
          self._load_model_group.setLayout(self._layout)
  
          self._scroll_area = QtWidgets.QScrollArea(self)
          self._scroll_area.setWidgetResizable(True)
          self._scroll_area.setFixedHeight(600)
          self._scroll_area.setFixedWidth(250)
          self._scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
          self._scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
          self._scroll_area.setWidget(self._load_model_group)
  
          # -------------------- Assign slot and signal -------------------- &quot;
          self._clear_all_btn.clicked.connect(self._clear_all_models)
          self._add_model_btn.clicked.connect(self._add_models)
  
      def _clear_all_models(self):
          for widget in self._all_load_model_widgets:
              self._layout.removeWidget(widget)
              widget.deleteLater()
  
          self._all_load_model_widgets = []
  
      def _add_models(self):
          widget = LoadModelWidget()
          widget.set_model_dir(&quot;hi&quot;, &quot;hello&quot;)
          self._all_load_model_widgets.append(widget)
          self._layout.addWidget(widget, len(self._all_load_model_widgets), 0, 1, 0)
  </code></pre>
  <p>Currently, when a widget is added, for some reason, it will try to occupy all the scroll area. But obviously, I don't really want that since I would like it to be allocated to the necessary space only.</p>
  <p>Here's an example of what it is doing right now when the user press Add Model(s) button:
  <a href="https://i.stack.imgur.com/n91tC.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/n91tC.png" alt="enter image description here" /></a></p>
  <p>But this is what I would like it to be:</p>
  <p><a href="https://i.stack.imgur.com/0kcix.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/0kcix.png" alt="enter image description here" /></a></p>