By the sounds of it, what you want is a TableView, with at least one custom delegate. I mention this because a ListView will only display a single ‘column’ from your data.
The custom delegate route may seem complicated, but it becomes clearer after going through the motions at least once. I see it as a long term investment; I’ve solved many UI issues using delegates.
Note that checkboxes can be handled directly in the model itself through the data/setData methods; it’s a PySide.QtCore.Qt.ItemDataRole in itself called QtCore.Qt.CheckStateRole.
I don’t think you would need to re-implement the paint method for a custom delegate. You’d want to re-implement paint if the existing widgets don’t cover your needs. Something like a star rating widget would require re-implementing paint (this is in fact the Qt example for re-implementing paint).
The process of adding a delegate to a data column is rather straight forward:
-define what existing widget will be used as an editor for the data you want to display/edit.
-implement the delegate with that editor; define the virtual methods createEditor, setEditorData, and setModelData.
-set what column in your view will use the delegate.
As an example, here’s a custom delegate that handles the python enum34 (enum34 · PyPI) which I used in certain columns of various tools:
class EnumComboDelegate(QtGui.QStyledItemDelegate):
"""
Combobox delegate to represent an Enum.
Assigning this delegate to a column will display enum as a combobox if the underlying model data is derived from
the Enum class.
"""
def __init__(self, parent=None):
super(EnumComboDelegate, self).__init__(parent)
@staticmethod
def data(index):
"""
Custom method to derive the model data from the model.
Note: assumes there is an intermediate proxyModel.
"""
# get the sourceModel, we know that the index.model() is a proxy model.
# If you don't know you can use the following:
# proxy_model = index.model()
# try:
# source_model = proxy_model.sourceModel()
# source_index = proxy_model.mapToSource(index)
# except:
# source_model = proxy_model
# source_index = index
# return source_model.getItemFromIndex(source_index).qt_data(index.column())
proxy_model = index.model()
# obtain the source model from the proxy
source_model = proxy_model.sourceModel()
# map the index back to the source model's index (true index)
source_index = proxy_model.mapToSource(index)
# use custom method in source model to obtain data for column
return source_model.getItemFromIndex(source_index).qt_data(index.column())
def createEditor(self, parent, option, index):
"""
Inits delegate; creates the widget to be used.
"""
data = self.data(index)
if isinstance(data, Enum):
combo_box = QtGui.QComboBox(parent)
names = type(data).__members__.keys()
combo_box.addItems(names)
# connect the signal so that when the index changes, the view can signal the model
combo_box.currentIndexChanged.connect(self.currentIndexChanged)
return combo_box
else:
return QtGui.QStyledItemDelegate.createEditor(self, parent, option, index)
def setEditorData(self, editor, index):
"""
Init editor with current data from the model when the user 'activates' the editor.
"""
data = self.data(index)
if isinstance(data, Enum):
names = type(data).__members__.keys()
editor.setCurrentIndex(names.index(data.name))
else:
return QtGui.QStyledItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
"""
This writes data to the model when the user has finished entering values.
"""
data = self.data(index)
if isinstance(data, Enum):
enum_type = type(data)
names = enum_type.__members__.keys()
value = enum_type[names[editor.currentIndex()]]
model.setData(index, value)
else:
QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
def currentIndexChanged(self):
"""
Sends signal to the sender (view) that the data can be committed.
"""
self.commitData.emit(self.sender())
In the snippet above, the data method is a custom method used to fetch column data from the model. While the getItemFromIndex is a custom method of the model, to fetch data based on its column.
To assign the delegate in your view, simply add this to your widget’s init after declaring your view/model/etc…:
self.myTableView.setItemDelegateForColumn(0, EnumComboDelegate(self))
Hopefully, this gets you started.