我有一个相当大的项目,用Python 3.4和PyQt5 5.5.1编写。从PyQt5版本5.4.1更新到PyQt5版本5.5.1后,我有奇怪的内存泄漏,5.4.1版本没有任何泄漏。我在项目中使用自定义QAbstractTableModel和自定义QTableView以及数据类型的委托。这是示例代码,它在第98行和第117行中有内存泄漏(可以使用tracemalloc找到它)。在表中选择单元格时会发生内存泄漏。
为什么会这样?
#!/usr/bin/python3
import tracemalloc
tracemalloc.start()
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
RW = (Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled)
my_array = [['00','01','02'],
['10','11','12'],
['20','21','22']]
class MyWindow(QWidget):
def __init__(self, *args):
QWidget.__init__(self, *args)
tablemodel = MyTableModel(my_array, self)
tableview = QTableView()
tableview.setModel(tablemodel)
self.cdelegates = []
for i in range(3):
d = RSpinDelegate(self)
self.cdelegates.append(d)
tableview.setItemDelegateForColumn(i, d)
layout = QVBoxLayout(self)
layout.addWidget(tableview)
self.setLayout(layout)
self.startTimer(5000)
def timerEvent(self, e):
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
print(stat)
class RSpinDelegate(QItemDelegate):
def __init__(self, parent=None, decimals=0, step=1, range_=(0, 1e9), edit=RW, suffix='', colorfill=None, stepFilter=None):
super(RSpinDelegate, self).__init__(parent)
self.decimals = decimals
self.step = step
self.range_ = range_
self.edit = edit
self.suffix = suffix
self.colorfill = colorfill
self.stepFilter = stepFilter
def setDecimals(self, decimals):
self.decimals = decimals
def createEditor(self, parent, option, index):
if self.edit == RW:
if self.decimals:
decimals = self.decimals
dec = int(index.model().data(index, RBaseTableModel.DECIMALS_ROLE))
decimals = dec
d = 10 ** (-decimals)
editor = RDoubleSpinBox(parent)
if self.stepFilter != None:
editor.installEventFilter(self.stepFilter)
editor.setSingleStep(d)
editor.setDecimals(decimals)
editor.setRange(self.range_[0], self.range_[1])
editor.setSuffix(self.suffix)
self._editor = editor
return editor
else:
editor = RSpinBox(parent)
if self.stepFilter != None:
editor.installEventFilter(self.stepFilter)
editor.setSingleStep(self.step)
editor.setRange(self.range_[0], self.range_[1])
editor.setSuffix(self.suffix)
self._editor = editor
return editor
return None
return None
def setEditorData(self, editor, index):
val = index.model().data(index, Qt.EditRole)
try:
editor.setValue(float(val.replace(' ', '')) if self.decimals != 0 else int(val.replace(' ', '')))
except:
editor.setValue(editor.minimum())
def setModelData(self, editor, model, index):
model.setData(index, editor.value(), Qt.EditRole)
def getBrush(self, option):
brush = option.palette.base()
if option.state & QStyle.State_Selected:# memory leak is here!
if option.state & QStyle.State_Active:
brush = option.palette.highlight()
else:
brush = option.palette.light()
return brush
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
def paint(self, painter, option, index):
opt = QStyleOptionViewItem(option)
if self.colorfill:
brush = self.colorfill(index.model().data(index, RBaseTableModel.INDEX_ROLE), option)
if not(option.state & QStyle.State_Selected):
painter.fillRect(option.rect, brush)
opt.palette.setBrush(QPalette.Highlight, brush)
else:
brush = self.getBrush(option)
painter.fillRect(option.rect, brush)# memory leak is here!
super(RSpinDelegate, self).paint(painter, opt, index)
# création du modèle
class MyTableModel(QAbstractTableModel):
refreshTable = pyqtSignal()
def __init__(self, datain, parent = None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.arraydata = datain
self.timer = self.startTimer(300)
def timerEvent(self, e):
if self.timer == e.timerId():
self.refreshTable.emit()
else:
super(RBaseTableView, self).timerEvent(e)
def refreshTableSlot(self):
self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit()
def rowCount(self, parent):
return len(self.arraydata)
def columnCount(self, parent):
return len(self.arraydata[0])
def data(self, index, role):
if not index.isValid():
return None
elif role != Qt.DisplayRole:
return None
return (self.arraydata[index.row()][index.column()])
if __name__ == "__main__":
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())