QScrollArea 中的自定义小部件仅在滚动时严重重绘

Posted

技术标签:

【中文标题】QScrollArea 中的自定义小部件仅在滚动时严重重绘【英文标题】:Custom Widget in QScrollArea Badly Redrawing Only on Scroll 【发布时间】:2018-09-22 19:57:54 【问题描述】:

我正在尝试在 QT 中获取自定义滚动小部件,但在滚动时出现重绘错误。 Alt-tab 或其他重绘事件正确重绘。

我以http://doc.qt.io/qt-5/qtwidgets-widgets-charactermap-example.html 的示例为基础

repeatingwidget.cpp(摘录):

QSize RepeatingWidget::sizeHint() const 
    return QSize(500, itemHeight * displayItems.size() + 1);


void RepeatingWidget::paintEvent(QPaintEvent *event) 
    QPainter painter(this);
    painter.fillRect(event->rect(), QBrush(Qt::white));
    painter.setFont(displayFont);

    QRect itemRect = event->rect();

    int top = itemRect.top();

    QFontMetrics fontMetrics(*displayFont);
    for (auto item : displayItems) 
        painter.setPen(QPen(Qt::gray));
        painter.drawRect(itemRect.left(), top, itemRect.right(), itemHeight);
        painter.setPen(QPen(Qt::black));
        painter.drawText(8, 4 + top + fontMetrics.ascent(), item.name);

        top += itemHeight;
    

mainwindow.cpp(摘录):

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)

    QMenu *filemenu = menuBar()->addMenu(tr("File"));
    filemenu->addAction(tr("Quit"), this, &QWidget::close);

    auto *centralWidget = new QWidget;

    scrollArea = new QScrollArea;

    repeatingArea = new RepeatingWidget();
    scrollArea->setWidget(repeatingArea);

    auto *centralLayout = new QVBoxLayout;
    centralLayout->addWidget(scrollArea, 1);

    centralWidget->setLayout(centralLayout);

    setCentralWidget(centralWidget);
    setWindowTitle(tr("Widget Test"));

这似乎与示例相符,但我遇到了在charmap 中不会发生的重绘错误。

我尝试了setGeometrysetWidgetResizable 和不同的大小策略,但我仍然收到这些重绘错误。

滚动后:

我不知道自己做错了什么,因为它在重要方面与charmap 中的示例代码基本相同。

这是完整代码:https://gist.github.com/jonasbuckner/2acc1a960e457946ce4756199de3fb57

【问题讨论】:

【参考方案1】:

QPaintEvent是一种可以让你进行智能绘画的方法,即在需要的地方进行绘画,从而节省资源,例如它通过event->rect()给了我们必须绘制的矩形的信息,用这样我们就可以计算出必须绘制的项目,因为其他项目将被隐藏,因此不需要绘制它们:

void RepeatingWidget::paintEvent(QPaintEvent *event)

    QPainter painter(this);
    painter.fillRect(event->rect(), QBrush(Qt::white));
    painter.setFont(displayFont);
    QFontMetrics fontMetrics(displayFont);
    int i = std::max(event->rect().top()/itemHeight, 0);
    int j = std::min(event->rect().bottom()/itemHeight+1, displayItems.size());
    QRect itemRect(0, i*itemHeight, width(), itemHeight);
    for(; i < j; i++)
        painter.setPen(QPen(Qt::gray));
        painter.drawRect(itemRect);
        painter.setPen(QPen(Qt::black));
        painter.drawText(8, 4 + itemRect.top() + fontMetrics.ascent(), displayItems[i].name);
        itemRect.translate(0, itemHeight);
    

【讨论】:

哦,我需要滑动矩形并更新矩形。当然。我担心每个矩形都需要一个额外的小部件。我试图用绝对值来画,但那太愚蠢了。谢谢。【参考方案2】:

您的原始代码不起作用,因为您正在绘制所有项目,但使用 event->rect,它可能只是 RepeatingWidget 的一部分。

有时很难像@eyllanesc 显示的那样计算 event->rect 中有哪些项目。在这些情况下,只需使用 clientRect 代替 - Qt 将为您剪辑绘图。

【讨论】:

以上是关于QScrollArea 中的自定义小部件仅在滚动时严重重绘的主要内容,如果未能解决你的问题,请参考以下文章

将小部件放在 QScrollArea 的中心

以编程方式滚动 QScrollArea

PySide QScrollArea 以编程方式滚动到特定的子小部件

为 QScrollArea 保留空间

即使在滚动时如何也不移动 QScrollArea 中的内容

QScrollArea 搞砸了 QGridLayout:QGridLayout 隐藏并且没有滚动