[PyQt] Bug report: strange memory leak in overridden method paint in QItemDelegate
Roman Liverovskiy
r.liverovskiy at gmail.com
Wed Jul 27 08:32:05 BST 2016
After updating to PyQt5 version 5.5.1 from PyQt5 version 5.4.1 I have
strange memory leaks, there are no any leaks on 5.4.1 version. I have
custom QAbstractTableModel and custom QTableView in my project with
delegates for data types. Here is example code, which has memory leaks in
lines 98 and 117 (it can be found using tracemalloc). Memory leak occur
when you select cells in table.
Also there is memory leak in PyQt5 version 5.6.
#!/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_())
--
Faithfully yours, Roman I. Liverovskiy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20160727/f66bd11e/attachment.html>
More information about the PyQt
mailing list