如何在QTableView/QTreeView中锁定选中项?
能否在QAbstractView中“锁定”选中项?
现有属性排查
Qt没有直接提供满足你需求的现成属性。你尝试的几种方法存在明显局限:
QAbstractItemModel::flags:仅能阻止选中其他项,无法拦截选中状态的清除操作QAbstractItemView::selectionMode:会限制视图整体选择逻辑,无法兼顾编辑新ID的需求QItemSelectionModel::NoUpdate:该枚举的作用是不更新视图显示,而非阻止选择状态变更,因此无法实现锁定效果
优化实现方案(规避子类化SelectionModel的维护风险)
方案1:通过事件过滤器拦截选择触发事件
给QTableView安装事件过滤器,在Editing状态下拦截会触发选择变更的鼠标、键盘事件,但保留对当前选中行(包括新增空行)的编辑权限:
class SelectionLockFilter : public QObject { Q_OBJECT public: explicit SelectionLockFilter(QAbstractItemView* view, QObject* parent = nullptr) : QObject(parent), m_view(view), m_locked(false) {} void setLocked(bool locked) { m_locked = locked; } protected: bool eventFilter(QObject* watched, QEvent* event) override { if (!m_locked || watched != m_view) { return QObject::eventFilter(watched, event); } // 拦截其他行的鼠标点击选择,允许当前选中行内的编辑操作 if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); QModelIndex index = m_view->indexAt(mouseEvent->pos()); if (index.isValid() && !m_view->selectionModel()->isSelected(index)) { return true; } } // 拦截方向键,阻止选中项切换 if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); if (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right) { return true; } } return QObject::eventFilter(watched, event); } private: QAbstractItemView* m_view; bool m_locked = false; };
使用方式:
// 初始化时为tableView安装过滤器 SelectionLockFilter* filter = new SelectionLockFilter(ui->tableView, this); ui->tableView->installEventFilter(filter); // 进入Editing状态时锁定选中 filter->setLocked(true); // 回到Viewing状态时解除锁定 filter->setLocked(false);
该方案无需修改模型或选择模型,不会被setModel操作重置,完全由Viewing/Editing状态驱动,维护成本极低。
方案2:重写QTableView的selectionChanged方法
若事件过滤器的粒度无法满足需求,可直接子类化QTableView,重写selectionChanged方法,在锁定状态下忽略新的选择变更:
class LockableTableView : public QTableView { Q_OBJECT public: explicit LockableTableView(QWidget* parent = nullptr) : QTableView(parent), m_selectionLocked(false) {} void setSelectionLocked(bool locked) { m_selectionLocked = locked; } protected: void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override { if (m_selectionLocked) { // 恢复原有选中状态,不处理新的选择请求 selectionModel()->select(selected, QItemSelectionModel::Deselect); selectionModel()->select(deselected, QItemSelectionModel::Select); return; } QTableView::selectionChanged(selected, deselected); } private: bool m_selectionLocked = false; };
使用时只需在状态切换时调用setSelectionLocked(true/false)即可。若你的数据库逻辑监听selectionChanged信号,可在逻辑中判断当前状态,跳过锁定状态下的变更处理,避免不必要的数据库调用。
总结
两种方案均无需依赖易被重置的setSelectionModel,完全由Viewing/Editing状态控制锁定行为,规避了子类化QItemSelectionModel带来的长期维护风险。事件过滤器方案更轻量,无需修改视图类;重写selectionChanged的方案对选择行为的控制更精准,可根据实际需求选择。
内容的提问来源于stack exchange,提问作者Atmo
相关产品推荐
相关产品推荐

