如何在QAbstractTableModel类内访问视图与代理模型并实现多单元格批量编辑?
Hey there! Let's tackle this problem properly—first, let's address your core question, then dive into the best practices for Qt's MVC pattern here.
Can QAbstractTableModel directly access the view or proxy model?
Short answer: You technically could, but you absolutely shouldn't. Qt's MVC architecture is built around decoupling models, views, and proxies. If your model starts depending on the view or proxy, you break this separation:
- Your model becomes tied to a specific view implementation, making it impossible to reuse with other views (like QListView or a custom view).
- It makes your code harder to test, debug, and maintain—changes to the view could break the model unexpectedly.
- Violates the single responsibility principle: models should only manage data, not handle view-specific logic.
Better Implementation Approaches
Here are the cleanest, most Qt-idiomatic ways to implement multi-cell sync editing:
1. Coordinate via a Parent Component/Controller
The best approach is to have a middle layer (like your main window or a dedicated controller class) handle communication between the view, proxy, and model. Here's how it works:
- When the user finishes editing a cell (listen to the delegate's
commitDatasignal), get all selected indexes from the view. - Convert those proxy indexes to source model indexes using
QSortFilterProxyModel::mapToSource(). - Pass the source indexes and the new value to a custom batch update method in your model.
Example Code Snippet
First, add a batch update method to your MyTableModel:
class MyTableModel : public QAbstractTableModel { Q_OBJECT private: QVector<QVector<QVariant>> m_data; // Your data storage public: // ... existing methods like rowCount(), columnCount(), data(), setData() ... void batchUpdateSelectedCells(const QModelIndexList& sourceIndexes, const QVariant& newValue) { if (sourceIndexes.isEmpty()) return; // Optional: Deduplicate indexes to avoid redundant updates QSet<QPair<int, int>> uniqueCells; for (const auto& idx : sourceIndexes) { uniqueCells.insert({idx.row(), idx.column()}); } // Trigger model update (use begin/end for proper view refresh) beginResetModel(); for (const auto& cell : uniqueCells) { int row = cell.first; int col = cell.second; if (row >= 0 && row < m_data.size() && col >=0 && col < m_data[row].size()) { m_data[row][col] = newValue; } } endResetModel(); // Alternatively, use a more precise dataChanged signal if you know the range: // emit dataChanged(sourceIndexes.first(), sourceIndexes.last()); } };
Then, in your parent component (e.g., MainWindow), connect the delegate's signal to handle the batch update:
// Assume these are members of your MainWindow: // QTableView* m_tableView; // QSortFilterProxyModel* m_proxyModel; // MyTableModel* m_sourceModel; connect(m_tableView->itemDelegate(), &QStyledItemDelegate::commitData, this, [=](QWidget* editor) { // Get selected indexes from the view (these are proxy indexes) QModelIndexList proxyIndexes = m_tableView->selectedIndexes(); QModelIndexList sourceIndexes; // Convert to source model indexes for (const auto& proxyIdx : proxyIndexes) { sourceIndexes.append(m_proxyModel->mapToSource(proxyIdx)); } // Get the edited value (adjust based on your editor type: QLineEdit, QSpinBox, etc.) QVariant newValue = qobject_cast<QLineEdit*>(editor)->text(); // Trigger batch update in the source model m_sourceModel->batchUpdateSelectedCells(sourceIndexes, newValue); });
2. Custom Delegate for Batch Editing
If you want to encapsulate more logic in the delegate, you can create a custom item delegate that handles the selected indexes and updates the model. This keeps the view and model decoupled, as the delegate acts as the intermediary.
- Override the delegate's
commitDatamethod to gather selected indexes, convert them to source indexes, and call the model's batch update method. - This works well if you want to reuse the multi-edit behavior across multiple views.
3. Avoid Common Pitfalls
- Always convert proxy indexes to source indexes: Never pass proxy indexes directly to the source model—they're only valid for the proxy.
- Deduplicate indexes: The
selectedIndexes()method can return duplicate entries (e.g., if a whole row/column is selected), so deduplicate to avoid redundant work. - Properly trigger model signals: Use
beginResetModel()/endResetModel()ordataChanged()to notify the view of updates—this ensures the view refreshes correctly.
内容的提问来源于stack exchange,提问作者fredrik

