在PyQt中动态添加和移除小部件

15 投票
3 回答
66164 浏览
提问于 2025-04-17 09:07

我在用PyQt创建一个界面,想要实现可以动态添加或删除小部件(widget)。我想为要添加或删除的小部件定义一个单独的类。不过,我发现我创建的小部件在主界面里显示不出来。以下是我使用的代码:

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_()

问题是,当我在'addWidget'方法里使用下面的代码时,它正好能实现我想要的效果,但类的方法似乎不起作用。

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

我在想,为什么z = Test()没有任何结果呢?有什么想法吗?谢谢!

3 个回答

3

如果你想要在每次点击按钮或者在程序的任何事件中删除一个小部件,可以使用一个叫做deleteLater()的方法:self.yourwidget.deleteLater()

self.button.clicked.connect(delete_widget);

def delete_widget(self):
     self.widget.deleteLater();

self.widget.deleteLater();

使用这个方法后,那个小部件就会从应用程序中消失。

4

这里有一个小改动,可以让按钮在被点击后自动删除自己:

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_()

这样做可以避免内存泄漏,而且按钮实际上可以用来做其他事情。我在处理下载和监控数据块时,通常会用这种方式来做进度条,即使在多线程和多进程的情况下也能很好地工作,而不是简单的QThreads...

20

其实,这个是可以工作的。问题在于,你的 Test 小部件里面有一个 QPushButton,但是没有进行布局管理。所以它无法计算出按钮的 minimumSize(最小大小)。当你把这个小部件放进一个布局里时,它就会缩小到 0(因为 QWidget 默认没有 minimumSize),所以你什么都看不见。

你有两个选择,要么手动管理布局,这样会让你陷入不必要的麻烦,要么依赖布局管理器。一般来说,建议你选择后者。

我会把你的脚本改成这样(虽然我不太明白你为什么使用 QFormLayout,但我还是保持原样):

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_()

撰写回答