Issue
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.
Solution
Here's how I solved it (in C++)
- Connect the
selectionChanged
signal from theTableView
'sSelectionModel
to a slot on your data Model:
connect(selectionModel(), &QItemSelectionModel::selectionChanged,
&model, &Model::onSelectionChanged);
- Every time the selection changes, retain a copy of it in the model. This might look like this:
void Model::onSelectionChanged(QItemSelection const& selected, QItemSelection const& deselected)
{
if (auto sm = qobject_cast<QItemSelectionModel*>(sender()))
{
mLastSelection = sm->selection();
}
}
- In your
setData()
function in the model, when updating in response to aCheckState
change, iterate through the entire selection to apply the sameCheckState
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.- In C++, to connect these types in a queued connection, they need to be registered using
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;
}();
}
Answered By - Tim MB
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.