PyQt6删除带有嵌套类的自定义小部件会导致程序崩溃



我使用的是Python 3.9.5。

我在我的项目中遇到了一些严重的问题,这里有一个最低限度的可复制示例代码,以及一些描述。

from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
class Editor(QTextEdit):
doubleClicked = pyqtSignal(QTextEdit)
def __init__(self):
super().__init__()
self.setReadOnly(True)

def mouseDoubleClickEvent(self, e: QMouseEvent) -> None:
self.doubleClicked.emit(self)
class textcell(QGroupBox):
def __init__(self, text):
super().__init__()
self.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
self.label = QLabel(text)
self.apply = makebutton('Apply')
self.apply.hide()
self.editor = Editor()
self.editor.doubleClicked.connect(lambda: self.editor.setReadOnly(False))
self.editor.doubleClicked.connect(self.apply.show)
self.hbox = QHBoxLayout()
self.hbox.addSpacerItem(spacer)
self.hbox.addWidget(self.apply)
self.vbox = QVBoxLayout()
self.vbox.addWidget(self.label)
self.vbox.addWidget(self.editor)
self.vbox.addLayout(self.hbox)
self.setLayout(self.vbox)
self.apply.clicked.connect(self.on_ApplyClick)

def on_ApplyClick(self):
self.editor.setReadOnly(True)
self.apply.hide()

def makebutton(text):
button = QPushButton()
button.setFixedSize(60, 20)
button.setText(text)
return button

class songpage(QGroupBox):
def __init__(self, texts):
super().__init__()
self.init(texts)
self.setCheckable(True)
self.setChecked(False)

def init(self, texts):
self.vbox = QVBoxLayout()
artist = textcell('Artist')
artist.editor.setText(texts[0])
album = textcell('Album')
album.editor.setText(texts[1])
title = textcell('Title')
title.editor.setText(texts[2])
self.vbox.addWidget(artist)
self.vbox.addWidget(album)
self.vbox.addWidget(title)
self.setLayout(self.vbox)
spacer = QSpacerItem(0, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
class Ui_MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(405, 720)
self.setWindowTitle('example')
frame = self.frameGeometry()
center = self.screen().availableGeometry().center()
frame.moveCenter(center)
self.move(frame.topLeft())
self.centralwidget = QWidget(self)
vbox = QVBoxLayout(self.centralwidget)
hbox = QHBoxLayout()
add = makebutton('Add')
delete = makebutton('Delete')
hbox.addWidget(add)
hbox.addSpacerItem(spacer)
hbox.addWidget(delete)
vbox.addLayout(hbox)
self.scrollArea = QScrollArea(self.centralwidget)
self.scrollArea.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
self.verticalLayout = QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.scrollArea.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop)
vbox.addWidget(self.scrollArea)
self.setCentralWidget(self.centralwidget)
add.clicked.connect(self.addObj)
delete.clicked.connect(self.deleteObj)
def addObj(self):
Obj = songpage(('AAA', 'BBB', 'CCC'))
self.verticalLayout.addWidget(Obj)
def deleteObj(self):
item = self.verticalLayout.itemAt(0)
widget = item.widget()
self.verticalLayout.removeItem(item)
self.verticalLayout.removeWidget(widget)

app = QApplication([])
window = Ui_MainWindow()
window.show()
app.exec()

问题很简单,如果我点击添加按钮,小部件就会被添加,一切都很好,如果我双击QTextEdit,它的应用按钮就会显示,它将从只读变为可编辑。

单击应用按钮后,该按钮将隐藏,相应的QTextEdit将再次只读。

我终于设法在QTextEdit中添加了一个双击信号。

问题是,如果我点击删除按钮,而不是按预期删除,它会导致整个应用程序崩溃。

很抱歉,可复制的最小示例太长了,但我只用所有的代码复制了这个问题,我真的不知道出了什么问题。

那么如何修复呢?

好吧,我已经弄清楚了,这是由我添加到每一个使用固定大小按钮的水平布局中的间隔项引起的。

所有这些布局都使用相同的间隔件,完全相同的间隔物,而不仅仅是相同的。

根据我的观察,间隔项都是对同一个对象的引用,它们都是生活在固定内存地址的同一对象的回声。

老实说,我不明白这是怎么回事,同一个对象不仅可以添加到多个布局中并同时出现在所有布局中,还可以多次添加到同一布局中,但它始终是原始对象,而不是重复的对象。

我想,当我在多个布局中添加相同的间隔项时,我没有添加原始的间隔项,而是添加了原始项的副本,这些副本是相同的,但位于不同的内存地址,显然Python不是这样工作的。

因此,当我删除一个小部件时,它里面的所有东西,布局里面的所有内容都会被删除,间隔项也会被删除。

因为所有布局中的所有间隔项都是对原始间隔项的引用,所以当我删除其中一个布局时,原始间隔项也被删除,并且间隔项从所有其他布局中删除,但其阴影仍然存在,并且该项没有从所有其他布局中正确删除,所以布局包含对不再存在的对象的引用,因此应用程序崩溃。

通过删除间隔项的定义并将添加的间隔项替换为.addStretch(),修复了该错误。

最新更新