QScrollArea 中的自定义小部件仅在滚动时严重重绘
Custom Widget in QScrollArea Badly Redrawing Only on Scroll
我正在尝试在 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 中不会发生的重绘错误。
我已经尝试了 setGeometry
、setWidgetResizable
和不同的大小策略,但我仍然遇到这些重绘错误。
滚动后:
我不知道我做错了什么,因为它在重要方面与 charmap 中的示例代码基本相同。
这是完整代码:https://gist.github.com/jonasbuckner/2acc1a960e457946ce4756199de3fb57
QPaintEvent
是一种智能绘画的方法,即在需要的地方绘画,从而节省资源,例如它通过[给我们提供了必须绘制的矩形的信息。 =12=],这样我们就可以计算出需要绘制的项目,因为其他项目将被隐藏,因此不需要绘制它们:
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);
}
}
您的原始代码不起作用,因为您正在绘制所有项目,但使用的是 event->rect,它可能只是 RepeatingWidget 的一部分。
有时计算事件中有哪些项目并不容易->如@eyllanesc 所示。在这些情况下,只需使用 clientRect 代替 - Qt 将为您裁剪绘图。
我正在尝试在 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 中不会发生的重绘错误。
我已经尝试了 setGeometry
、setWidgetResizable
和不同的大小策略,但我仍然遇到这些重绘错误。
滚动后:
我不知道我做错了什么,因为它在重要方面与 charmap 中的示例代码基本相同。
这是完整代码:https://gist.github.com/jonasbuckner/2acc1a960e457946ce4756199de3fb57
QPaintEvent
是一种智能绘画的方法,即在需要的地方绘画,从而节省资源,例如它通过[给我们提供了必须绘制的矩形的信息。 =12=],这样我们就可以计算出需要绘制的项目,因为其他项目将被隐藏,因此不需要绘制它们:
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);
}
}
您的原始代码不起作用,因为您正在绘制所有项目,但使用的是 event->rect,它可能只是 RepeatingWidget 的一部分。
有时计算事件中有哪些项目并不容易->如@eyllanesc 所示。在这些情况下,只需使用 clientRect 代替 - Qt 将为您裁剪绘图。