删除小部件后布局不正确
The layout is incorrect after remove widget
我正在使用 pyqt5 实现我的项目。目前,我有一个 window 包括许多小部件。现在,我想删除一些小部件。 window 看起来像:
现在,我想删除 'name1' 小部件,包括 QLabel 和 QPushButton。
但是,在移除所有'name1' widgets后,包括QLabel和QPushButton在内的'name2' widgets无法与window自适应,如:
我所有的代码是:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subText = QLabel(name)
subText.setObjectName(name)
subBtn = QPushButton(name)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
def remove(self, checked=False):
name = self.__removeText.text()
while True:
child = self.__ROIsLayout.takeAt(0)
if child == None:
break
while True:
subChild = child.takeAt(0)
if subChild == None:
break
obName = subChild.widget().objectName()
if name == obName:
widget = subChild.widget()
widget.setParent(None)
child.removeWidget(widget)
self.__ROIsLayout.removeWidget(widget)
del widget
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
更新:
实际上,问题可能出在 takeAt
。以下代码是可行的:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subLayout.setObjectName(name)
subText = QLabel(name, parent=self)
subText.setObjectName(name)
subBtn = QPushButton(name, parent=self)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
print(name, subLayout, subText, subBtn)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
self.record = [subLayout, subText, subBtn]
def remove(self, checked=False):
layout = self.record[0]
txt = self.record[1]
btn = self.record[2]
layout.removeWidget(txt)
txt.setParent(None)
txt.deleteLater()
layout.removeWidget(btn)
btn.setParent(None)
btn.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
但是,我在 self.record 中打印了 QLabel/QPushButton,我发现它与 child.takeAt(0).widget().[=15 中的相同=]
您代码中的主要问题是您一直在使用 takeAt()
。结果是 __ROIsLayout
布局中的所有项目都将从中删除(但不会删除),在您的情况下,它们是子布局。这显然不是一个好方法:只有具有相应对象名称的小部件才会被实际删除,而其他小部件仍将由其先前的父级“拥有”,仍将在其先前的位置可见,并且它们的几何图形不会被删除已更新,因为它们不再由布局管理。
您的问题有多种解决方案,具体取决于您的需要。
如果您需要从布局中删除 行,我会考虑改为在布局上设置对象名称,并使用 self.findChild()
查找它。
另请考虑,虽然 Qt 允许为多个对象设置相同的对象名称,但不建议这样做。
最后,虽然通常使用 del
就足够了,但通常最好为所有 Qt 对象调用 deleteLater()
,这样可以确保 Qt 正确删除所有对象(以及相关的 parentship/connections) .
对于这种特定情况,另一种可能性是使用 QFormLayout。
我正在使用 pyqt5 实现我的项目。目前,我有一个 window 包括许多小部件。现在,我想删除一些小部件。 window 看起来像:
现在,我想删除 'name1' 小部件,包括 QLabel 和 QPushButton。
但是,在移除所有'name1' widgets后,包括QLabel和QPushButton在内的'name2' widgets无法与window自适应,如:
我所有的代码是:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subText = QLabel(name)
subText.setObjectName(name)
subBtn = QPushButton(name)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
def remove(self, checked=False):
name = self.__removeText.text()
while True:
child = self.__ROIsLayout.takeAt(0)
if child == None:
break
while True:
subChild = child.takeAt(0)
if subChild == None:
break
obName = subChild.widget().objectName()
if name == obName:
widget = subChild.widget()
widget.setParent(None)
child.removeWidget(widget)
self.__ROIsLayout.removeWidget(widget)
del widget
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
更新:
实际上,问题可能出在 takeAt
。以下代码是可行的:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subLayout.setObjectName(name)
subText = QLabel(name, parent=self)
subText.setObjectName(name)
subBtn = QPushButton(name, parent=self)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
print(name, subLayout, subText, subBtn)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
self.record = [subLayout, subText, subBtn]
def remove(self, checked=False):
layout = self.record[0]
txt = self.record[1]
btn = self.record[2]
layout.removeWidget(txt)
txt.setParent(None)
txt.deleteLater()
layout.removeWidget(btn)
btn.setParent(None)
btn.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
但是,我在 self.record 中打印了 QLabel/QPushButton,我发现它与 child.takeAt(0).widget().[=15 中的相同=]
您代码中的主要问题是您一直在使用 takeAt()
。结果是 __ROIsLayout
布局中的所有项目都将从中删除(但不会删除),在您的情况下,它们是子布局。这显然不是一个好方法:只有具有相应对象名称的小部件才会被实际删除,而其他小部件仍将由其先前的父级“拥有”,仍将在其先前的位置可见,并且它们的几何图形不会被删除已更新,因为它们不再由布局管理。
您的问题有多种解决方案,具体取决于您的需要。
如果您需要从布局中删除 行,我会考虑改为在布局上设置对象名称,并使用 self.findChild()
查找它。
另请考虑,虽然 Qt 允许为多个对象设置相同的对象名称,但不建议这样做。
最后,虽然通常使用 del
就足够了,但通常最好为所有 Qt 对象调用 deleteLater()
,这样可以确保 Qt 正确删除所有对象(以及相关的 parentship/connections) .
对于这种特定情况,另一种可能性是使用 QFormLayout。