dynamically adding and removing widgets in PyQt

boundless picture boundless · Dec 28, 2011 · Viewed 52.3k times · Source

using PyQt, I am trying to create an interface for which I can add or remove widget dynamically. I want to define a separate class for the widget that will be added or removed. I can't seem to be able to get the widget that I instantiate to display inside the main interface. Here is the code I am using:

from PyQt4 import QtGui, QtCore
import sys

class Main(QtGui.QMainWindow):
    def __init__(self, parent = None):
        super(Main, self).__init__(parent)

        # central widget
        self.centralWidget = QtGui.QWidget(self)

        # main layout
        self.vLayout = QtGui.QVBoxLayout(self.centralWidget)

        # main button
        self.pButton_add = QtGui.QPushButton(self.centralWidget)
        self.pButton_add.setText('button to add other widgets')

        # scroll area
        self.scrollArea = QtGui.QScrollArea(self.centralWidget)
        self.scrollArea.setWidgetResizable(True)

        # scroll area widget contents
        self.scrollAreaWidgetContents = QtGui.QWidget(self.scrollArea)

        # scroll area widget contents - layout
        self.formLayout = QtGui.QFormLayout(self.scrollAreaWidgetContents)

        self.scrollArea.setWidget(self.scrollAreaWidgetContents)

        # add all main to the main vLayout
        self.vLayout.addWidget(self.pButton_add)
        self.vLayout.addWidget(self.scrollArea)
        # set central widget
        self.setCentralWidget(self.centralWidget)
        # connections
        self.pButton_add.clicked.connect(self.addWidget)


    def addWidget(self):
        z = Test(self.scrollAreaWidgetContents)
        count = self.formLayout.rowCount()
        self.formLayout.setWidget(count, QtGui.QFormLayout.LabelRole, z)


class Test(QtGui.QWidget):
  def __init__( self, parent):
      super(Test, self).__init__(parent)

      self.pushButton = QtGui.QPushButton(self)


app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()

the thing is, when I use the below code inside my 'addWidget' method, it exactly does what I want it to do, but the the class method doesn't seem to work.

z = QtGui.QPushButton(self.scrollAreaWidgetContents)
count = self.formLayout.rowCount())
self.formLayout.setWidget(count, QtGui.QFormLayout.LabelRole, z)

I wonder why the z = Test() is not yielding any results? Any ideas? Thanks!

Answer

Avaris picture Avaris · Dec 28, 2011

Actually, it does work. Problem is, your Test widget has a QPushButton without any layout management. So it can't calculate its minimumSize with taking the button into consideration. When you put that widget in a layout, it just shrinks to 0 (since a QWidget has no default minimumSize) and you don't see anything.

You have two choices, either you manually manage layout and enter a needless world of pain, or you rely on layout managers. In general, you should prefer the latter.

I would re-write your script like this (Although I'm not sure why you use QFormLayout, I leave it as it is.):

from PyQt4 import QtGui, QtCore
import sys

class Main(QtGui.QMainWindow):
    def __init__(self, parent = None):
        super(Main, self).__init__(parent)

        # main button
        self.addButton = QtGui.QPushButton('button to add other widgets')
        self.addButton.clicked.connect(self.addWidget)

        # scroll area widget contents - layout
        self.scrollLayout = QtGui.QFormLayout()

        # scroll area widget contents
        self.scrollWidget = QtGui.QWidget()
        self.scrollWidget.setLayout(self.scrollLayout)

        # scroll area
        self.scrollArea = QtGui.QScrollArea()
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setWidget(self.scrollWidget)

        # main layout
        self.mainLayout = QtGui.QVBoxLayout()

        # add all main to the main vLayout
        self.mainLayout.addWidget(self.addButton)
        self.mainLayout.addWidget(self.scrollArea)

        # central widget
        self.centralWidget = QtGui.QWidget()
        self.centralWidget.setLayout(self.mainLayout)

        # set central widget
        self.setCentralWidget(self.centralWidget)

    def addWidget(self):
        self.scrollLayout.addRow(Test())


class Test(QtGui.QWidget):
  def __init__( self, parent=None):
      super(Test, self).__init__(parent)

      self.pushButton = QtGui.QPushButton('I am in Test widget')

      layout = QtGui.QHBoxLayout()
      layout.addWidget(self.pushButton)
      self.setLayout(layout)



app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()