在QTableView中设置QPlainTextEdit Delegate的高度

Setting the Height of a QPlainTextEdit Delegate in a QTableView

本文关键字:Delegate 高度 QPlainTextEdit 设置 QTableView      更新时间:2023-10-16

我在这里工作的一个项目,目前我被困在以下问题。它是关于一个QTableView,它有一个列叫做"Description",这个列的单元格包含一个QPlainTextEditDelegate。每次输入时,我都无法设置QPlainTextEdit的高度。现在它的行为就像一个QLineEdit,直到我把QTableView的行(当时我在其中活动)拖大。

我想做的是改变QPlainTextEdit的高度,一旦我进入它。你有什么建议?我怎样才能把这件事做完呢?

提前感谢大家!

BTW抱歉我的英语不好:/

编辑:

我解决了这个问题,但没有sizeHint,我使用了updateEditorGeometry:

void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const;

在这个方法中,你可以设置你想要的宽度或高度

editor->setGeometry(option.rect.x(),option.rect.y(),<your_width>,<your_height>);

不过还是谢谢你!

您应该重新实现QAbstractItemDelegate::sizeHint方法,以便在创建编辑器时返回期望的高度。我不认为有必要在创建编辑器后发出QAbstractItemDelegate::sizeHintChanged信号,但文档没有说什么。如果没有它就不能工作,您应该在返回创建的编辑器小部件后发出sizeHintChanged,以通知视图需要更改行高。

我正面临着完全相同的问题,尽管在QTableView中有一个QPlainTextEdit作为编辑器的自定义委托。最初,编辑器的增长不会超过其表单元格的大小。

什么不适合我:

  • updateEditorGeometry():只调用初始show()
  • 自定义sizeHint():从未被调用,无论是在子类还是基类中,无论任何update(), updateGeometry()调用或信号

在看似无穷无尽的收集关于一些未记录的QPlainTextEdit/QTextDocument '功能'和Qt怪谈的小块信息后,我通过连接到QPlainTextEdit->document()->documentLayout()的documentsizechange (QSizeF)信号得到了我想要的。

下面是扩展编辑器所需的基本要素,同时将其限制在父QTableView的viewport (m_pTableView)中。

希望这对任何人都有意义。示例代码不能按原样编译,但应该提供足够的提示来开始。

h editor + delegate

#include <QPlainTextEdit>
// A custom QStyledItemDelegate
#include "library/tableitemdelegate.h"
/// A QPlainTextEdit to show all content lines in a scrollable view.
/// * finish editing with Return (like QLineEdit used for other metadata)
/// * add new line with Shift+Return.
/// Horizontal scrollbar is hidden as long as content is just one line.
/// Note: QTextEdit is no option here since it seems to transform content with
/// line breaks to html doc when committing data.
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 slots:
    void commitAndCloseEditor();
  private:
    void adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const;
    mutable QRect m_editRect;
    mutable int m_lineCount;
};

cpp编辑器+委托

#include "library/multilineeditdelegate.h"
#include <QAbstractTextDocumentLayout>
#include <cmath>
#include "moc_multilineeditdelegate.cpp"
MultiLineEditor::MultiLineEditor(QWidget* pParent)
        : QPlainTextEdit(pParent) {
    setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    // Disable line wrap for a predictable view (like QLineEdit).
    // Horizontal scrollbars show up automatically.
    setLineWrapMode(QPlainTextEdit::NoWrap);
    // Remove ugly content offset, most notable with one-liners
    setContentsMargins(0, 0, 0, 0);
    document()->setDocumentMargin(0);
    // Add event filter to catch right-clicks and key presses
    installEventFilter(this);
};
bool MultiLineEditor::eventFilter(QObject* obj, QEvent* event) {
    if (event->type() == QEvent::MouseButtonPress) {
        // Work around a strange quirk: right-clicks outside the rectangle of
        // the underlying table index are not triggering the context menu.
        // Simply returning true fixes it.
        QMouseEvent* me = static_cast<QMouseEvent*>(event);
        if (me->button() == Qt::RightButton &&
                 rect().contains(me->pos(), false)) {
            return true;
        }
    } else if (event->type() == QEvent::KeyPress) {
        // Finish editing with Return key like in QLineEdit
        QKeyEvent* ke = static_cast<QKeyEvent*>(event);
        if ((ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) &&
                ke->modifiers().testFlag(Qt::NoModifier)) {
            emit editingFinished();
            return false;
        }
    }
    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();
    // Adjust height to fit content and maybe shift vertically to fit into the
    // library view. documentSizeChanged() is emitted when text changed, incl.
    // initial fill.
    // We could also connect to QPlainTextEdit::blockCountChanged(), though
    // for some reason, that only works for documents with 2+ lines
    // appending line breaks to single-line docs won't fire that signal :|
    connect(pDocLayout,
            &QAbstractTextDocumentLayout::documentSizeChanged,
            this,
            [this, pEditor](const QSizeF size) {
                adjustEditor(pEditor, size);
            });
    // emitted when pressing Return key to act like QLineEdit
    connect(pEditor,
            &MultiLineEditor::editingFinished,
            this,
            &MultiLineEditDelegate::commitAndCloseEditor);
    // Store the initial rectangle so we can read the x/y origin and in adjustEditor()
    m_editRect = option.rect;
    return pEditor;
}
void MultiLineEditDelegate::adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const {
    // Compared to QTextEdit, size.height() is the line count (Qt speak: blocks)
    int newLineCount = static_cast<int>(round(size.height()));
    // Only act if line count changed
    if (newLineCount == m_lineCount) {
        return;
    } else {
        m_lineCount = newLineCount;
    }
    // Remove the scrollbars if content is just one line to emulate QLineEdit
    // appearace, else enable auto mode.
    Qt::ScrollBarPolicy pol(m_lineCount > 1 ? Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff);
    pEditor->setVerticalScrollBarPolicy(pol);
    pEditor->setHorizontalScrollBarPolicy(pol);
    // Calculate the content height
    int lines = m_lineCount;
    // Add extra margin so the horizontal scrollbar doesn't obstruct the last
    // line (which also avoids the vertical scrollbar as long as possible)
    lines += lines > 1 ? 1 : 0;
    QFontMetrics fm(pEditor->document()->defaultFont());
    int newH = fm.lineSpacing() * lines;
    // Limit editor to visible table height
    int tableH = m_pTableView->viewport()->rect().height();
    newH = std::min(newH, tableH);
    // If the editor overflows the table view, move it up so it's not clipped.
    // No need to care about y < 0 or y > (table height - line height) since the
    // table already ensures visibility when the index is selected.
    int newY = m_editRect.y();
    if ((newY + newH) > tableH) {
        newY = tableH - newH;
    }
    // Also limit width so scrollbars are visible and table is not scrolled if
    // cursor is moved horizontally.
    int newW = std::min(pEditor->width(), m_pTableView->viewport()->rect().width());
    pEditor->setGeometry(QRect(m_editRect.x(), newY, newW, newH));
}
void MultiLineEditDelegate::commitAndCloseEditor() {
    MultiLineEditor* pEditor = qobject_cast<MultiLineEditor*>(sender());
    emit commitData(pEditor);
    emit closeEditor(pEditor);
}