【问题标题】:dynamically adding and removing widgets in PyQt在 PyQt 中动态添加和删除小部件
【发布时间】:2012-01-28 22:04:52
【问题描述】:

使用 PyQt,我正在尝试创建一个可以动态添加或删除小部件的界面。我想为要添加或删除的小部件定义一个单独的类。我似乎无法让我实例化的小部件显示在主界面中。这是我正在使用的代码:

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() 没有产生任何结果?有任何想法吗?谢谢!

【问题讨论】:

    标签: python pyqt pyqt4


    【解决方案1】:

    实际上,它确实有效。问题是,您的 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_()
    

    【讨论】:

    • 感谢您的详细解答!我使用表单布局的原因是我希望小部件开始出现在滚动区域的顶部并从那里填充空白位置,而不是出现在中间并在创建新小部件时重新排列它们自己。虽然我确信还有其他方法可以做到这一点,但 formlayout 对我来说似乎是一个简单的解决方案。再次感谢!
    • @boundless:啊,我明白了。我的方法是将scrollLayout 包装在另一个QVBoxLayout 中,并在其下方添加一段1。这应该把事情放在首位。
    • 我喜欢 formlayout 的另一件事是,它可以更容易地让两个小部件彼此相邻,同时将事物保持在顶部。虽然也许在那一点上,我应该只使用网格布局。再次感谢您的提示!
    • @Avaris 有没有一种方法可以在单击按钮时调用 .py 文件并将其加载到框架中。 .py 文件包含很少的属性和按钮。所以在按钮单击时,我希望将文件加载到框架中。
    【解决方案2】:

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

    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...

    【讨论】:

    • 改了哪一部分,为什么会有这个效果?
    【解决方案3】:

    如果你想说,只要单击按钮,或在你的程序的任何事件期间删​​除一个小部件,请使用 deleteLater() 方法:self.yourwidget.deleteLater()

    self.button.clicked.connect(delete_widget);
    
    def delete_widget(self):
         self.widget.deleteLater();
    

    self.widget.deleteLater();

    使用上述功能会使该小部件从应用程序中消失

    【讨论】:

    • 令人惊叹的。终于找到这个问题不到100行代码的解决方案了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    相关资源
    最近更新 更多