【问题标题】:pyside - how to delete widgets from gridLayoutpyside - 如何从 gridLayout 中删除小部件
【发布时间】:2014-02-08 10:24:18
【问题描述】:

我在 QT Designer 中构建了一个 ui,然后使用 pyside-uic 将其转换为 python 文件,然后编写了一些代码以编程方式对其进行编辑。换句话说,我有一个按钮 Add Row,单击它时会将其重命名为 Remove1 并创建另一个按钮名称为 Add Row 并将其添加到布局中。

点击Add Row时的代码,更改名称和信号/槽:

self.pb_Row[-1].setText('Remove'+str(self.nRows))
self.pb_Row[-1].clicked.disconnect( self.addRow )
self.pb_Row[-1].clicked.connect( self.removeRow )

点击Remove时的代码,移除选中的按钮:

iRow = int(self.sender().objectName().split('_')[-1])-1
ind = self.PropertyLayout.indexOf(self.pb_Row[iRow])
t = self.PropertyLayout.takeAt(ind)
t.widget().deleteLater()
# self.pb_Row[iRow].hide()
# self.pb_Row[iRow].deleteLater()
self.pb_Row.pop(iRow)

这工作得很好,直到你添加至少一个然后删除它,下一轮它会搞砸。基本上,当我有两个按钮并删除一个然后尝试添加一个时,它会出现异常。我所说的行为不端是指新按钮最终位于旧按钮之上,有时它出现在下方而不是上方。

此外,如果我使用 .hide() 函数,它并没有真正重新组织网格布局中的内容。我不太确定我应该使用哪个。

谢谢!

这是一个产生不良结果的序列:

重新开始

点击添加后

点击删除后(到目前为止一切都很好),然后点击添加(没有明显的区别)

第二次点击添加后

点击 Remove2 后,Remove1 从其下方出现

“工作”代码示例

import numpy as np
import sys
from PySide import QtCore, QtGui
import matplotlib.pyplot as plt

from ModesInFiber import Ui_fiberModesMainWindow

class Ui_fiberModesMainWindow(object):
    def setupUi(self, fiberModesMainWindow):
        fiberModesMainWindow.setObjectName("fiberModesMainWindow")
        fiberModesMainWindow.resize(653, 597)
        self.centralwidget = QtGui.QWidget(fiberModesMainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.horizontalLayout_2 = QtGui.QHBoxLayout(self.centralwidget)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.MainLayout = QtGui.QGridLayout()
        self.MainLayout.setObjectName("MainLayout")
        self.PropertyLayout = QtGui.QGridLayout()
        self.PropertyLayout.setObjectName("PropertyLayout")
        self.lbl_Name = QtGui.QLabel(self.centralwidget)
        self.lbl_Name.setObjectName("lbl_Name")
        self.PropertyLayout.addWidget(self.lbl_Name, 0, 1, 1, 1)
        self.pb_addRow_1 = QtGui.QPushButton(self.centralwidget)
        self.pb_addRow_1.setObjectName("pb_addRow_1")
        self.PropertyLayout.addWidget(self.pb_addRow_1, 1, 5, 1, 1)
        self.ledit_Name_1 = QtGui.QLineEdit(self.centralwidget)
        self.ledit_Name_1.setObjectName("ledit_Name_1")
        self.PropertyLayout.addWidget(self.ledit_Name_1, 1, 1, 1, 1)
        self.MainLayout.addLayout(self.PropertyLayout, 0, 0, 1, 1)
        spacerItem2 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
        self.MainLayout.addItem(spacerItem2, 1, 0, 1, 1)
        self.horizontalLayout_2.addLayout(self.MainLayout)
        fiberModesMainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(fiberModesMainWindow)
        QtCore.QMetaObject.connectSlotsByName(fiberModesMainWindow)
#         fiberModesMainWindow.setTabOrder(self.ledit_Name_1, self.ledit_Width_1)
#         fiberModesMainWindow.setTabOrder(self.ledit_Width_1, self.cmb_RIType_1)
#         fiberModesMainWindow.setTabOrder(self.cmb_RIType_1, self.ledit_RIParam_1)
#         fiberModesMainWindow.setTabOrder(self.ledit_RIParam_1, self.pb_addRow_1)

    def retranslateUi(self, fiberModesMainWindow):
        fiberModesMainWindow.setWindowTitle(QtGui.QApplication.translate("fiberModesMainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.lbl_Name.setText(QtGui.QApplication.translate("fiberModesMainWindow", "Name", None, QtGui.QApplication.UnicodeUTF8))
        self.pb_addRow_1.setText(QtGui.QApplication.translate("fiberModesMainWindow", "Add Row", None, QtGui.QApplication.UnicodeUTF8))


class DesignerMainWindow(QtGui.QMainWindow, Ui_fiberModesMainWindow):

    def __init__(self, parent = None):
        super(DesignerMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.pb_addRow_1.clicked.connect( self.addRow )

        self.ledit_Name     = [ self.ledit_Name_1 ]
        self.pb_Row      = [ self.pb_addRow_1 ]

        # number of rows
        self.nRows = 1


    def addRow( self ):

        self.ledit_Name[-1].setEnabled(False)
        self.pb_Row[-1].setText('Remove'+str(self.nRows))
        self.pb_Row[-1].clicked.disconnect( self.addRow )
        self.pb_Row[-1].clicked.connect( self.removeRow )

        self.nRows += 1

        self.ledit_Name.append( QtGui.QLineEdit(self.centralwidget) )
        self.ledit_Name[-1].setObjectName('ledit_Name_'+str(self.nRows))
        self.PropertyLayout.addWidget( self.ledit_Name[-1], self.nRows, 1, 1, 1)

        self.pb_Row.append( QtGui.QPushButton(self.centralwidget) )
        self.pb_Row[-1].setObjectName( 'pb_addRow_'+str(self.nRows) )
        self.pb_Row[-1].setText('Add Row')
        self.pb_Row[-1].clicked.connect( self.addRow )
        self.PropertyLayout.addWidget( self.pb_Row[-1], self.nRows, 5, 1, 1)


    def removeRow( self ):

        iRow = int(self.sender().objectName().split('_')[-1])-1
        self.nRows -= 1

        ind = self.PropertyLayout.indexOf(self.ledit_Name[iRow])
        t = self.PropertyLayout.takeAt(ind)
        t.widget().setParent(None)
#             t.widget().deleteLater()
#             self.ledit_Name[iRow].hide()
#             self.ledit_Name[iRow].deleteLater()
#             self.ledit_Name[iRow].setParent(None)
        self.ledit_Name.pop(iRow)

        ind = self.PropertyLayout.indexOf(self.pb_Row[iRow])
        t = self.PropertyLayout.takeAt(ind)
        t.widget().setParent(None)
#             t.widget().deleteLater()
#             self.pb_Row[iRow].hide()
#             self.pb_Row[iRow].deleteLater()
#             self.pb_Row[iRow].setParent(None)
        self.pb_Row.pop(iRow)

        for iAfterRow in range(iRow, self.nRows):
            self.ledit_Name[iAfterRow].setObjectName( 'ledit_Name_' + str(iAfterRow+1) )
            self.pb_Row[iAfterRow].setObjectName( 'ledit_Name_' + str(iAfterRow+1) )

        print 'Remove row', iRow

if __name__ == '__main__':
    app = QtGui.QApplication( sys.argv )
    dmw = DesignerMainWindow()
    dmw.show()
    sys.exit( app.exec_() )

【问题讨论】:

  • 你能定义你所说的“行为不端”吗?您看到的错误是什么?我在自己的代码中看到了一个问题,即从网格布局中删除一个项目,并在其位置添加另一个项目,导致旧项目和新项目被绘制在彼此之上。这是你看到的吗?
  • 是的!这正是我所看到的!这让我发疯了
  • 酷,我会发布一个答案来告诉你我是如何修复它的!

标签: python layout pyqt4 pyside qgridlayout


【解决方案1】:

这里的问题是由QGridLayout的一个实现细节引起的。

每当从 QGridLayout 中删除项目时,logical 行和列的数量永远不会减少,即使 visual 行或列的数量可能会减少。因此,您应该始终使用 getItemPositionitemAtPosition 等方法直接处理 QGridLayout 中的项目。

下面是使用这种方法重写示例中的DesignerMainWindow 类。显然,它可能需要稍作调整才能与您的实际应用程序一起使用。

class DesignerMainWindow(QtGui.QMainWindow, Ui_fiberModesMainWindow):
    def __init__(self, parent = None):
        super(DesignerMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.pb_addRow_1.clicked.connect( self.addRow )

    def addRow( self ):
        rows = self.PropertyLayout.rowCount()
        columns = self.PropertyLayout.columnCount()
        for column in range(columns):
            layout = self.PropertyLayout.itemAtPosition(rows - 1, column)
            if layout is not None:
                widget = layout.widget()
                if isinstance(widget, QtGui.QPushButton):
                    widget.setText('Remove %d' % (rows - 1))
                    widget.clicked.disconnect(self.addRow)
                    widget.clicked.connect(self.removeRow)
                else:
                    widget.setEnabled(False)
        widget = QtGui.QLineEdit(self.centralwidget)
        self.PropertyLayout.addWidget(widget, rows, 1, 1, 1)
        widget = QtGui.QPushButton(self.centralwidget)
        widget.setText('Add Row')
        widget.clicked.connect(self.addRow)
        self.PropertyLayout.addWidget(widget, rows, columns - 1, 1, 1)

    def removeRow(self):
        index = self.PropertyLayout.indexOf(self.sender())
        row = self.PropertyLayout.getItemPosition(index)[0]
        for column in range(self.PropertyLayout.columnCount()):
            layout = self.PropertyLayout.itemAtPosition(row, column)
            if layout is not None:
                layout.widget().deleteLater()
                self.PropertyLayout.removeItem(layout)

【讨论】:

  • 首先谢谢!快到了,所以如果我理解正确,还有其他行。现在我是否正确地假设您的方法实际上并没有摆脱这些行,而只是找到了解决这个问题的方法?我之所以这么说是因为Remove按钮旁边的数字总是增加
  • 好的,我认为行数只会随着here 而增加
  • @evan54。是的,正如我在回答中所说,这是一个实现细节:添加的行不会被删除。我想您可以通过删除小部件并将布局项目保留在其中来重复使用行。但这会使事情的管理变得更加复杂。
【解决方案2】:

编辑:这并不能解决问题,我还找到了我的知识来源:Is there any way to remove a QWidget in a QGridLayout?

-- 出于某种原因,从布局中删除小部件很困难。一次找了好久的答案,已经不记得在哪里找到的了,但是跑题了……

您需要执行以下操作。首先找到要删除的项目。您可以使用layout.itemAt()layout.itemAtPosition 来获取对它的引用。

现在,要从布局中删除项目,只需调用item.widget().setParent(None)。这将具有从布局中删除项目的效果!

注意:如果您已经有对小部件的引用,您可能只需在其上调用setParent,而无需从布局中找到它。我还没有尝试过(但不明白为什么它不起作用)。

【讨论】:

  • 不幸的是,这并没有什么不同 :( ,我正在拍摄更多的屏幕截图来演示正在发生的事情
  • 第二次出现时可以点击“删除1”吗?它表现得像一个按钮还是只是在那里绘制?我认为我的解决方案会起作用,但也许你的 GUI 结构有点复杂,你只是没有在你应该拥有的所有东西上调用 setParent(None)?你能发布一个更完整的代码示例(也就是一个简单的工作示例),我可以运行它来测试吗?
  • 让我尝试提出简约的代码示例,一旦我点击 remove1 它就可以按预期工作,但问题是有时它们出现在上面然后可能在下面等等,即按钮的位置即使他们在那里,最终都会出错
  • 刚刚添加了一些工作代码,试图使其尽可能简约,但仍然相当冗长
  • 好吧,我错了,还有其他问题,但我不确定是什么。我现在没有太多时间详细查看它,但应该在 12 小时后再次查看。如果到那时还没有其他人解决它,我会再破解它
【解决方案3】:

您似乎没有为要删除的行中的所有单元格调用 QGridLayout::removeWidget()。然后由于 PySide,布局将继续引用小部件,当您调用 setParent(None) 时,它们并没有真正删除/删除。

另外,当你在没有 QLayout::removeWidget() 的单元格中尝试 deleteLater() 时,单元格不会被删除而是保持活动状态(单元格中会留下 QLayoutItem)

【讨论】:

  • 所以你建议我先做.removeWidget(widget),然后再做.deleteLater()?我试过了,它没有任何区别
猜你喜欢
  • 2012-04-11
  • 2020-07-09
  • 2013-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多