QTableView中QPlainTextEdit项委托的右键菜单异常问题
QTableView多行编辑器委托右键点击异常问题
我在QTableView的comment列使用了自定义QPlainTextEdit委托编辑器,已实现编辑器自适应内容扩展至表格单元格外,但遇到以下问题:
- 左键点击编辑器任意位置正常工作
- 右键点击编辑器超出单元格的区域时,编辑器直接关闭,无法弹出右键菜单
- 确认点击位置在编辑器、视口及QFrame范围内,但仍被判定为外部区域
- 尝试过重写
keypressEvent()、设置Qt::CustomContextMenu策略,均无效——点击能被识别但菜单无法弹出,编辑器始终关闭
相关代码
multilineeditdelegate.h
#include <QPlainTextEdit> // a QStyledItemDelegate subclass #include "library/tableitemdelegate.h" class MultiLineEditor : public QPlainTextEdit { Q_OBJECT public: MultiLineEditor(QWidget* pParent); bool eventFilter(QObject* obj, QEvent* event) override; signals: void editingFinished(); }; class MultiLineEditDelegate : public TableItemDelegate { Q_OBJECT public: explicit MultiLineEditDelegate(QTableView* pTrackTable); ~MultiLineEditDelegate() override = default; // called when the user starts editing an item QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: void adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const; };
multilineeditdelegate.cpp
#include "multilineeditdelegate.h" #include <QAbstractTextDocumentLayout> #include <cmath> #include "moc_multilineeditdelegate.cpp" MultiLineEditor::MultiLineEditor(QWidget* pParent) : QPlainTextEdit(pParent) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); setLineWrapMode(QPlainTextEdit::NoWrap); setContentsMargins(0, 0, 0, 0); document()->setDocumentMargin(0); installEventFilter(this); }; bool MultiLineEditor::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { QMouseEvent* me = static_cast<QMouseEvent*>(event); if (me->button() == Qt::RightButton && rect().contains(me->pos(), false)) { return true; } } return QPlainTextEdit::eventFilter(obj, event); } MultiLineEditDelegate::MultiLineEditDelegate(QTableView* pTableView) : TableItemDelegate(pTableView), m_lineCount(0) { } QWidget* MultiLineEditDelegate::createEditor(QWidget* pParent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(index); auto* pEditor = new MultiLineEditor(pParent); auto* pDocLayout = pEditor->document()->documentLayout(); connect(pDocLayout, &QAbstractTextDocumentLayout::documentSizeChanged, this, [this, pEditor](const QSizeF size) { adjustEditor(pEditor, size); }); } void MultiLineEditDelegate::adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const { // fancy calculations to expand editor to fit content // while limiting it to the rectangle of the parent QTableView pEditor->setGeometry(QRect(m_editRect.x(), newY, newW, newH)); }
解决方案
问题根源是QStyledItemDelegate默认逻辑仅检测点击是否在原始单元格区域内,编辑器扩展后,点击超出部分会被判定为“外部点击”触发编辑结束。需从以下层面修复:
1. 修复createEditor的遗漏问题
原代码未返回编辑器对象,导致委托无法正确管理编辑器,补充返回语句:
QWidget* MultiLineEditDelegate::createEditor(QWidget* pParent, const QStyleOptionViewItem& option, const QModelIndex& index) const { Q_UNUSED(index); auto* pEditor = new MultiLineEditor(pParent); auto* pDocLayout = pEditor->document()->documentLayout(); connect(pDocLayout, &QAbstractTextDocumentLayout::documentSizeChanged, this, [this, pEditor](const QSizeF size) { adjustEditor(pEditor, size); }); // 必须返回编辑器,否则委托无法关联编辑状态 return pEditor; }
2. 重写委托的editorEvent,修正点击区域判断
让委托识别编辑器扩展后的有效区域,避免误判为外部点击:
// 在MultiLineEditDelegate类声明中添加该方法声明 bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override; // 实现部分 bool MultiLineEditDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) { QMouseEvent* me = static_cast<QMouseEvent*>(event); // 获取当前活跃的编辑器 MultiLineEditor* pEditor = qobject_cast<MultiLineEditor*>(editor(index)); if (pEditor && pEditor->isVisible()) { // 将全局坐标转换为编辑器局部坐标 QPoint editorPos = pEditor->mapFromGlobal(me->globalPos()); if (pEditor->rect().contains(editorPos)) { // 点击在编辑器范围内,阻止委托触发编辑结束逻辑 return true; } } } return TableItemDelegate::editorEvent(event, model, option, index); }
3. 编辑器内直接处理右键菜单,拦截事件传递
替换原eventFilter逻辑,直接重写mousePressEvent处理右键操作:
// 在MultiLineEditor类声明中添加该方法声明 void mousePressEvent(QMouseEvent* event) override; // 实现部分 void MultiLineEditor::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::RightButton) { // 直接弹出右键菜单 QMenu menu(this); menu.addAction(tr("复制")); menu.addAction(tr("粘贴")); menu.addAction(tr("全选")); menu.exec(event->globalPos()); // 阻止事件继续传递,避免委托误判 event->accept(); return; } QPlainTextEdit::mousePressEvent(event); }
内容的提问来源于stack exchange,提问作者ronso0
相关产品推荐
相关产品推荐

