- 问题现象是,在QListWidget的一个窗口中有一些item,可以用IconMode和ListMode两种模式显示,一开始在IconMode用滚轮滑动速度是正常的,切换到ListMode后滚动速度也正常,但是再切换回来,滚动速度明显变慢,只要不切换ListMode速度就不会变化
最后调查发现是Qt自身的限制,这里先做一个简单的demo,可以再现这个问题

- idiombrowser.cpp
c
#include "idiombrowser.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QListWidgetItem>
IdiomBrowser::IdiomBrowser(QWidget *parent) : QWidget(parent)
{
setupUI();
populateList();
connectSignals();
}
void IdiomBrowser::switchToListMode()
{
listWidget->setViewMode(QListView::ListMode);
listWidget->setDragDropMode(QAbstractItemView::NoDragDrop);
adjustListSize();
}
void IdiomBrowser::switchToIconMode()
{
listWidget->setViewMode(QListView::IconMode);
listWidget->setDragDropMode(QAbstractItemView::NoDragDrop);
listWidget->setIconSize(QSize(64, 64));
adjustListSize();
}
void IdiomBrowser::setupUI()
{
QVBoxLayout *mainLayout = new QVBoxLayout(this);
// 创建按钮布局
QHBoxLayout *buttonLayout = new QHBoxLayout();
listModeButton = new QPushButton("list mode", this);
iconModeButton = new QPushButton("icon mode", this);
buttonLayout->addWidget(listModeButton);
buttonLayout->addWidget(iconModeButton);
// 创建列表控件
listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
// 初始设置为列表模式
switchToIconMode();
mainLayout->addLayout(buttonLayout);
mainLayout->addWidget(listWidget);
setWindowTitle("scroll test");
resize(600, 400);
// listWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
void IdiomBrowser::populateList()
{
QStringList idioms = {
"aaaa", "bbbb", "cccc", "dddd",
"eeee", "ffff", "gggg", "hhhh",
"iiii", "jjjj", "kkkk", "llll",
"mmmm", "nnnn", "oooo", "pppp",
"qqqq", "rrrr", "ssss", "tttt",
"uuuu", "vvvv", "wwww", "xxxx",
"vvvv", "wwww",
};
for (const QString &idiom : idioms) {
QListWidgetItem *item = new QListWidgetItem(idiom);
listWidget->addItem(item);
}
}
void IdiomBrowser::connectSignals()
{
connect(listModeButton, &QPushButton::clicked, this, &IdiomBrowser::switchToListMode);
connect(iconModeButton, &QPushButton::clicked, this, &IdiomBrowser::switchToIconMode);
}
void IdiomBrowser::adjustListSize()
{
if (listWidget->viewMode() == QListView::IconMode) {
listWidget->setGridSize(QSize(100, 100));
listWidget->setSpacing(10);
listWidget->setWordWrap(true);
} else {
listWidget->setGridSize(QSize());
listWidget->setSpacing(2);
}
}
- idiombrowser.h
c
#ifndef IDIOMBROWSER_H
#define IDIOMBROWSER_H
#include <QWidget>
class QListWidget;
class QPushButton;
class IdiomBrowser : public QWidget
{
Q_OBJECT
public:
IdiomBrowser(QWidget *parent = nullptr);
private slots:
void switchToListMode();
void switchToIconMode();
private:
void setupUI();
void populateList();
void connectSignals();
void adjustListSize();
QListWidget *listWidget;
QPushButton *listModeButton;
QPushButton *iconModeButton;
};
#endif // IDIOMBROWSER_H
- main.cpp
c
#include <QApplication>
#include "idiombrowser.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
IdiomBrowser browser;
browser.show();
return app.exec();
}
通过对Qt6源码进行debug发现了问题原因
首先是在void QListModeViewBase::updateVerticalScrollBar(const QSize &step)这个函数中
如果切换到ListMode就会走进第一个if,然后setSingleStep(1)

设置为1后,这里的viewMayChangeSingleStep就会变成false

viewMayChangeSingleStep是false的情况下,后续切换模式时,就无法切换step,也就是滚动单步步长singleStep永远是1
这里源码的注释也说明了,如果将setSingleStep设置为一个reasonable值,就无法切换了,需要再设为-1才能恢复

singleStep用于滑动时计算步长

解决方案是将滚动模式设为按像素滚动,这样不会走setSingleStep(1);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);