I've prepared a minimal reproducible example of this problem. There are two TreeView
delegates: one for checkboxes and one for progress bars. If I add any one of these delegates, but not the other one, the program doesn't crash. But if I add both, it crashes (upon trying to show the rows and the delegates). It gives the Segmentation Fault error when crashing.
delegates.py
from PySide6 import QtWidgets
from PySide6.QtCore import Qt, QEvent, QPoint, QRect
from PySide6 import QtCore, QtGui
class CheckBoxDelegate(QtWidgets.QStyledItemDelegate):
"""
A delegate that places a fully functioning QCheckBox cell of the column to which it's applied.
"""
def __init__(self, parent = None):
QtWidgets.QStyledItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
"""
Important, otherwise an editor is created if the user clicks in this cell.
"""
return None
def paint(self, painter, option, index):
checked = bool(index.model().data(index, Qt.DisplayRole))
check_box_style_option = QtWidgets.QStyleOptionButton()
if (index.flags() & Qt.ItemIsEditable):
check_box_style_option.state |= QtWidgets.QStyle.State_Enabled
else:
check_box_style_option.state |= QtWidgets.QStyle.State_ReadOnly
if checked:
check_box_style_option.state |= QtWidgets.QStyle.State_On
else:
check_box_style_option.state |= QtWidgets.QStyle.State_Off
check_box_style_option.rect = self.getCheckBoxRect(option)
QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_CheckBox, check_box_style_option, painter)
def editorEvent(self, event, model, option, index):
if not (index.flags() & Qt.ItemIsEditable):
return False
# Do not change the checkbox-state
if event.type() == QEvent.MouseButtonRelease or event.type() == QEvent.MouseButtonDblClick:
if event.button() != Qt.LeftButton or not self.getCheckBoxRect(option).contains(event.pos()):
return False
if event.type() == QEvent.MouseButtonDblClick:
return True
elif event.type() == QEvent.KeyPress:
if event.key() != Qt.Key_Space and event.key() != Qt.Key_Select:
return False
else:
return False
# Change the checkbox-state
self.setModelData(None, model, index)
return True
def getCheckBoxRect(self, option):
check_box_style_option = QtWidgets.QStyleOptionButton()
check_box_rect = QtWidgets.QApplication.style().subElementRect(QtWidgets.QStyle.SE_CheckBoxIndicator, check_box_style_option, None)
check_box_point = QPoint (option.rect.x() +
option.rect.width() / 2 -
check_box_rect.width() / 2,
option.rect.y() +
option.rect.height() / 2 -
check_box_rect.height() / 2)
return QRect(check_box_point, check_box_rect.size())
def setModelData (self, editor, model, index):
newValue = not bool(index.model().data(index, Qt.DisplayRole))
model.setData(index, newValue, Qt.EditRole)
class ProgressBarDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
def paint(self, painter, option, index):
# Get the data for the item
progress = index.data(QtCore.Qt.ItemDataRole.UserRole)
# Draw the progress bar
painter.save()
rect = option.rect
rect.setWidth(int(rect.width() * progress))
painter.fillRect(rect, QtGui.QColor("#00c0ff"))
painter.restore()
mainwindow.py
from ui_form import Ui_MainWindow
from PySide6.QtCore import QThread, SIGNAL, Slot
from PySide6 import QtGui, QtCore
from PySide6.QtWidgets import QApplication, QMainWindow
from delegates import CheckBoxDelegate, ProgressBarDelegate
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.row_data = []
self.model = QtGui.QStandardItemModel()
self.ui.actionExit.triggered.connect(self.exit)
def reinit_model(self):
self.model.clear()
self.rootItem = self.model.invisibleRootItem()
self.model.setHorizontalHeaderLabels(['Checkbox', 'Title', 'Progress'])
self.ui.treeView.setModel(self.model)
def get_row_data(self):
row_data = []
for i in range(3):
title = f"Col 1 Row {i}"
row_data.append([title])
return row_data
def reset_row_data(self):
self.row_data.clear()
self.row_data = self.get_row_data()
class Show_list(QThread):
def __init__(self, AppWindow):
QThread.__init__(self)
self.AppWindow = AppWindow
def run(self):
self.AppWindow.reset_row_data()
def populate_window_list(self):
self.reinit_model()
for title_link in self.row_data:
item = [QtGui.QStandardItem(),
QtGui.QStandardItem(title_link[0]),
QtGui.QStandardItem()]
progress = 0.5
item[2].setData(progress, QtCore.Qt.ItemDataRole.UserRole)
self.rootItem.appendRow(item)
self.ui.treeView.show()
cb_delegate = CheckBoxDelegate()
pb_delegate = ProgressBarDelegate()
self.ui.treeView.setItemDelegateForColumn(0, cb_delegate)
# self.ui.treeView.setItemDelegateForColumn(2, pb_delegate)
for i in range(3):
self.ui.treeView.resizeColumnToContents(i)
@Slot()
def show_list(self):
self.thread = self.Show_list(self)
self.connect(self.thread, SIGNAL("finished()"),
self.populate_window_list)
self.thread.start()
def exit(self):
QApplication.quit()
main.py
import sys
from mainwindow import MainWindow
from PySide6.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = MainWindow()
widget.reinit_model()
getRowDataButton = widget.ui.getRowDataButton
getRowDataButton.clicked.connect(widget.show_list)
widget.show()
sys.exit(app.exec())
Here's also ui_form.py
.
I've tried to add cb_delegate.deleteLater()
and pb_delegate.deleteLater()
, but it didn't help. The program loaded the list and crashed immediately.