You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:17:05