Klaudikus
Klaudikus

Reputation: 392

How to set checked/unchecked multiple selected rows of a QTableView

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:

For loaded data models, I'm not sure this is the best approach.

Upvotes: 1

Views: 2487

Answers (2)

Tim MB
Tim MB

Reputation: 4521

Here's how I solved it (in C++)

  • Connect the selectionChanged signal from the TableView's SelectionModel 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 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.

  • 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;
    }();
}

Upvotes: 3

Klaudikus
Klaudikus

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:

  • 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: 0

Related Questions