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!
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_()
Here is a little change that will make the button delete itself once clicked:
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(TestButton())
class TestButton(QtGui.QPushButton):
def __init__( self, parent=None):
super(TestButton, self).__init__(parent)
self.setText("I am in Test widget")
self.clicked.connect(self.deleteLater)
app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()
This way it shouldn't mem leak when deleted and the button can actually be used for stuff. I follow this pattern for progress bars by the dozens for downloads and chunks monitoring and it works just fine even with threading and multi processing. And not the easy QThreads...
If u want to say , delete a widget whenever a button is clicked , or during any event of ur program , use the deleteLater() method :self.yourwidget.deleteLater()
self.button.clicked.connect(delete_widget);
def delete_widget(self):
self.widget.deleteLater();
self.widget.deleteLater();
Using the above function makes that widget dissappear from the Application