Reputation: 392
I'm attempting to apply the principle outlined in the following post, but for the CheckStateRole of my model (which is defined both in the data/setData methods of my model):
how can i achieve to update multiple rows in a qtableview
However, I realized checking/unchecking a column (in my case column 0), doesn't seem to call commitData at all.
Here's the code snippet:
class MyTableView(QtGui.QTableView):
"""
View that will set all selected rows to the current CheckedState.
"""
def __init__(self, parent=None):
super(MyTableView, self).__init__(parent)
def commitData(self, editor):
# this method is not called when a user checks/unchecks an item
super(MyTableView, self).commitData(editor)
model = self.currentIndex().model()
current_row, current_column = self.currentIndex().row(), 0
value = model.data(model.index(current_row, current_column), QtCore.Qt.CheckStateRole)
for row in self.selectionModel().selectedRows(0):
if row != current_row:
index = model.index(row, current_column)
model.setData(index, value, QtCore.Qt.CheckStateRole)
How can achieve this for checkboxes in a QTableView using a QAbstractTableModel?
Internally, how is the checkBox handled? Is it not considered a delegate which calls commitData?
SOLVED ---------------------------------
I ended up handling this by emitting dataChanged for the CheckStateRole from the setData method in the model, then connecting the signal to a method that applies the passed indices check state to the selection.
Two issues arose:
This would cause an infinite loop (dataChanged calling a method that affected other indices which in turn would emit dataChanged...). The solution was to blockSignals when setting the selected indices check state in the method.
Because of the blockSignals the ui is not refreshed. Internally I assume dataChanged invokes a refresh of the view for the index range. A simple update on the view fixed this.
For loaded data models, I'm not sure this is the best approach.
Upvotes: 1
Views: 2487
Reputation: 4521
Here's how I solved it (in C++)
selectionChanged
signal from the TableView
's SelectionModel
to a slot on your data Model:connect(selectionModel(), &QItemSelectionModel::selectionChanged,
&model, &Model::onSelectionChanged);
void Model::onSelectionChanged(QItemSelection const& selected, QItemSelection const& deselected)
{
if (auto sm = qobject_cast<QItemSelectionModel*>(sender()))
{
mLastSelection = sm->selection();
}
}
setData()
function in the model, when updating in response to a CheckState
change, iterate through the entire selection to apply the same CheckState
change to all selected cells.Next, to deal with the problem that the TableView clears the selection when the user clicks on a checkbox:
Emit a setSelection signal from the model with the same signature as QItemSelectionModel::select
:
Q_EMIT setSelection(mLastSelection, QItemSelectionModel::Select);
Connect this signal back to the selection model but, crucially, use a QueuedConnection
to do this, so that it arrives in the next event loop cycle.
qRegisterMetaType
. You can do this neatly in an anonymous namespace at the top of your cpp file like this:{
bool const _ = []{
qRegisterMetaType<QItemSelection>();
qRegisterMetaType<QItemSelectionModel::SelectionFlags>();
return true;
}();
}
Upvotes: 3
Reputation: 392
I ended up handling this by emitting dataChanged for the CheckStateRole from the setData method in the model, then connecting the signal to a method that applies the passed indices check state to the selection.
Two issues arose:
Upvotes: 0