I'm using Pyside2, Python 3.8. I have a QTableView, the value of the first column is a bool, I want to implement a delegate to paint Editable CheckBoxs in the first column. I've tried many SO answers, the one that've been the most helpful is this one. It partially works, ie: You have to double click the cell, then you get a 'kind of' combobox instead of a checkbox in my first column (see picture below)
I've managed to do this without going through a Delegate, using the CkeckStateRole, it worked, the checkboxs aren't editable, but I have many editable other columns and It seems that there's no escape from implementing a delegate as it is the more proper way to do it.
here's my Table Model class:
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, mlist=None):
super(TableModel, self).__init__()
self._items = [] if mlist == None else mlist
self._header = []
def rowCount(self, parent = QtCore.QModelIndex):
return len(self._items)
def columnCount(self, parent = QtCore.QModelIndex):
return len(self._header)
def flags(self, index):
if index.column() == 0:
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
return QtCore.Qt.ItemIsEnabled
def data(self, index, role = QtCore.Qt.DisplayRole):
if not index.isValid():
return None
elif role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
return self._items[index.row()][index.column()]
elif role == QtCore.Qt.CheckStateRole:
return None
else:
return None
def setData(self, index, value, role = QtCore.Qt.EditRole):
if value is not None and role == QtCore.Qt.EditRole:
self._items[index.row()][index.column()] = value
self.dataChanged.emit(index, index)
return True
return False
Here's my CheckBox Delegate class:
class CheckBoxDelegate(QtWidgets.QItemDelegate):
def __init__(self, parent = None):
QtWidgets.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
if not (QtCore.Qt.ItemIsEditable & index.flags()):
return None
check = QtWidgets.QCheckBox(parent)
check.clicked.connect(self.stateChanged)
return check
def setEditorData(self, editor, index):
editor.blockSignals(True)
editor.setChecked(index.data())
editor.blockSignals(False)
def setModelData(self, editor, model, index):
model.setData(index, editor.isChecked(), QtCore.Qt.EditRole)
def paint(self, painter, option, index):
value = index.data()
if value:
value = QtCore.Qt.Checked
else:
value = QtCore.Qt.Unchecked
self.drawCheck(painter, option, option.rect, value)
self.drawFocus(painter, option, option.rect)
@QtCore.Slot()
def stateChanged(self):
print("sender", self.sender())
self.commitData.emit(self.sender())
Edit #1
After some research, I've found out that I've forgot to put self.MyTableView.setItemDelegateForColumn(0, CheckBoxDelegate(self))
in my MainWindow __init__
Well, What happens now is I get a centered CheckBox in my first column, I can't edit it, but, when I double click it, It creates a second Checkbox in the same cell at the left, which is Editable and also sets the new CheckState to my Model. Let's say my centered CheckBox is set to True, I double click, a second Checked CheckBox is created to its left, I Uncheck it, I click on another cell, the second checkBox disappears, the centered checkBox is set to Unchecked, and my ModelData is updated. (see picture below for what happens after double clicking the centered CheckBox)
Fix attempts
1. Not overriding paint():
The column contains either True or false, double clicking the cell creates an editable checkbox which sets the data in the model to the new value and then disappears.
2. the createEditor() method returns None
def createEditor(self, parent, option, index):
return None
It creates a single centered checkbox, no labels, but the checkbox isn't editable
3. Not overriding createEditor() method
Creates a single centered checkbox with no labels, double clicking the cell replace the checkbox with a combobox (True or False), from which I can change the CheckBox state.
Neither of those fixes gave me what I'm looking for: A signe centered checkbox created that is editable with a single click