Qt高性能列表控件之QListView的使用及优点

Qt高性能列表控件之QListView的使用及优点

1.高性能列表如何能实线高性能?

高性能列表无非就解决两个痛点:

数据存放在哪里

数据如何展示

为何QListWidget就不能高性能呢?QListWidget 是一个方便的控件,它内部管理了一个项目列表,并提供了一些简单的接口来添加、删除和修改这些项目。但没有对数据存储和数据展示进行过多的优化,这种方式适合于简单的应用场景,其中列表的大小不会很大,因为每个项目都会被存储为一个 QListWidgetItem 对象。

在QListView体系里,QAbstractListModel解决的是“数据存哪”,解决的是第一个问题,而QAbstractItemDelegate解决的是数据“如何展示”,解决的是第二个问题。

2.QListView和QAbstractListModel解决数据存哪

这里就不从QAbstractListModel派生写自定义的类了,直接使用Qt从QAbstractListModel派生的类QStandardItemModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//初始化QListView
QListView* photoListViewphotoListView = new QListView(this);
photoListView->setViewMode(QListView::IconMode);
photoListView->setResizeMode(QListView::Adjust);
photoListView->setIconSize(QSize(150, 150));
photoListView->setSpacing(10);
photoListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
photoListView->setUniformItemSizes(true);
photoListView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
photoListView->setAttribute(Qt::WA_Hover); // 启用 Hover 事件
photoListView->viewport()->setAttribute(Qt::WA_Hover);

//初始化QStandardItemModel
QStandardItemModel* m_photoModel = new QStandardItemModel(this);
ui->listView_Photos->setItemDelegate(new PhotoDelegate(ui->listView_Photos));//这是解决数据如何展示的
ui->listView_Photos->model()->removeRows(0, ui->listView_Photos->model()->rowCount(QModelIndex()));
//模拟数据
for (auto photoInfo : m_vecPhoto) {
QStandardItem* item = new QStandardItem;
item->setEditable(false);
item->setData(":/images/Demo.png", Qt::UserRole);
//item->setData(photoInfo.duration,Qt::UserRole);
item->setData(photoInfo.fileName, Qt::UserRole + 1);
item->setData(photoInfo.width + "x" + photoInfo.height, Qt::UserRole + 2);
item->setData(photoInfo.dateCreated, Qt::UserRole + 3);
item->setData(photoInfo.fileSize, Qt::UserRole + 4);
item->setData(photoInfo.dir, Qt::UserRole + 5);
//将数据存放到QStandardItemModel里面
m_photoModel->appendRow(item);
}

3.QListView和QAbstractItemDelegate解决数据如何展示

QStyledItemDelegate是从QAbstractItemDelegate派生的类,QListView的View模型采用的是paint函数来呈现,paint的形式用起来更复杂,但性能天花板更高。这就是QListView的性能如此高的原因。

包括了IconMode以及ListMode的显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
class PhotoDelegate : public QStyledItemDelegate {
Q_OBJECT

public:
enum ViewMode { ListMode, GridMode };
explicit PhotoDelegate(QListView* parent = nullptr)
: QStyledItemDelegate(parent) {
listView = parent;
};
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override {
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QListView::ViewMode viewMode = listView->viewMode();
QPixmap pixmap(index.data(Qt::UserRole).toString());
//图片信息
QString fileName = index.data(Qt::UserRole + 1).toString();
QString fileReso = index.data(Qt::UserRole + 2).toString();
QString fileDate = index.data(Qt::UserRole + 3).toString();
QString fileSize = index.data(Qt::UserRole + 4).toString();

QRect iconRect = QRect(option.rect.left() + 5, option.rect.top() + 5, 50, 50); // 图标区域
QRect nameRect = QRect(iconRect.right() - 50, option.rect.top() + 100, option.rect.width(), 20); // 文件名
QRect dateRect = QRect(iconRect.right() - 50, option.rect.top() + 120, option.rect.width(), 20); // 日期
QRect sizeRect = QRect(iconRect.right() - 50, option.rect.top() + 140, option.rect.width(), 20); // 大小

bool isSelected = option.state & QStyle::State_Selected;
bool isHovered = option.state & QStyle::State_MouseOver;


if (viewMode == QListView::ViewMode::IconMode)
{
QString filePath = index.data(Qt::UserRole).value<QString>();
QRect imageRect = option.rect;

// 设置图片为正方形,填充区域
int size = qMin(imageRect.width(), imageRect.height());
QRect squareRect(imageRect.topLeft(), QSize(size, size));
squareRect.moveCenter(imageRect.center());

// 抗锯齿
painter->setRenderHint(QPainter::Antialiasing);

// 裁剪,绘图
QPainterPath clipPath;
clipPath.addRoundedRect(squareRect, 10, 10);
painter->setClipPath(clipPath);
painter->drawPixmap(squareRect, pixmap, pixmap.rect());
if (!isSelected)
{
// 图标
QPixmap checkIcon(":/images/unselect.png");
QRect checkRect(squareRect.topRight() - QPoint(SELECT_ICON_SIZE + 5, -5),
QSize(SELECT_ICON_SIZE, SELECT_ICON_SIZE));
painter->drawPixmap(checkRect, checkIcon);
}

// 绘制选中时的边框和勾选图标
if (isSelected) {
// 蓝框
painter->setPen(QPen(QColor(41, 95, 204), 3));
painter->drawRoundedRect(squareRect, 10, 10);

// 图标
QPixmap checkIcon(":/images/selected.png");
QRect checkRect(squareRect.topRight() - QPoint(SELECT_ICON_SIZE + 5, -5), QSize(SELECT_ICON_SIZE, SELECT_ICON_SIZE));
painter->drawPixmap(checkRect, checkIcon);
}
if (isHovered)
{
// 蓝框
painter->setPen(QPen(QColor(41, 95, 204), 3));
painter->drawRoundedRect(squareRect, 10, 10);


painter->setPen(Qt::black);
painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, fileName);
painter->drawText(dateRect, Qt::AlignLeft | Qt::AlignVCenter, fileDate);
painter->drawText(sizeRect, Qt::AlignLeft | Qt::AlignVCenter, fileSize);
}
}
else if (viewMode == QListView::ViewMode::ListMode)
{
//listView->setSpacing(5);
if (isHovered)
{
QRect fileRect(option.rect.topLeft(), option.rect.size());
int borderRadius = 10; // 圆角的半径

painter->setPen(Qt::NoPen); // 去掉边框
painter->setBrush(QColor(235, 241, 255)); // 设置填充颜色

// 圆角背景
painter->drawRoundedRect(fileRect, borderRadius, borderRadius);
}

// 选中状态边框
if (isSelected) {
QRect fileRect(option.rect.topLeft(), option.rect.size());
int borderRadius = 10; // 圆角的半径
painter->setPen(Qt::NoPen);
// 设置背景颜色
painter->setBrush(QColor(235, 241, 255));

// 绘制带圆角的背景
painter->drawRoundedRect(fileRect, borderRadius, borderRadius);

// 绘制选中图标
QRect checkIconRect = QRect(option.rect.left() + 15, option.rect.top() + 20, SELECT_ICON_SIZE, SELECT_ICON_SIZE);
QPixmap checkIcon(":/images/selected.png");
painter->drawPixmap(checkIconRect, checkIcon);
}
else
{
QRect checkIconRect = QRect(option.rect.left() + 15, option.rect.top() + 20, SELECT_ICON_SIZE, SELECT_ICON_SIZE);
QPixmap checkIcon(":/images/unselect.png");
painter->drawPixmap(checkIconRect, checkIcon);
}

// 绘制区域
QRect iconRect = QRect(option.rect.left() + 45, option.rect.top() + 7, 44, 44); // 图标区域
QRect nameRect = QRect(iconRect.right() + 40, option.rect.top() + 20, 200, 20); // 文件名
QRect resoRect = QRect(nameRect.right() + 40, option.rect.top() + 20, 100, 20); // 分辨率
QRect dateRect = QRect(resoRect.right() + 40, option.rect.top() + 20, 200, 20); // 日期
QRect sizeRect = QRect(dateRect.right() + 40, option.rect.top() + 20, 100, 20); // 大小

// 绘制圆角图标
int borderRadius = 10;
QPainterPath path;
path.addRoundedRect(iconRect, borderRadius, borderRadius);

painter->save();
// 抗锯齿
painter->setRenderHint(QPainter::Antialiasing, true);
// 裁剪区域
painter->setClipPath(path);
painter->drawPixmap(iconRect, pixmap);
painter->restore();

// 绘制文件名、日期和大小
painter->setPen(Qt::black);
painter->drawText(nameRect, Qt::AlignLeft | Qt::AlignVCenter, fileName);
painter->setPen(Qt::gray);
painter->drawText(resoRect, Qt::AlignLeft | Qt::AlignVCenter, fileReso);
painter->drawText(dateRect, Qt::AlignLeft | Qt::AlignVCenter, fileDate);

painter->drawText(sizeRect, Qt::AlignLeft | Qt::AlignVCenter, fileSize);

}


painter->restore();
}



QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override {
Q_UNUSED(index);
//固定大小
if (listView->viewMode() == QListView::ViewMode::IconMode) {
return QSize(170, 170);
}
else
{
return QSize(50, 60);
}

}
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index) override {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QListView::ViewMode viewMode = listView->viewMode();

QRect checkRect;
if (viewMode == QListView::ViewMode::IconMode) {
checkRect = QRect(option.rect.topRight() - QPoint(SELECT_ICON_SIZE + 5, -5),
QSize(SELECT_ICON_SIZE, SELECT_ICON_SIZE));
}
else {
checkRect = QRect(option.rect.left() + 15, option.rect.top() + 20, SELECT_ICON_SIZE, SELECT_ICON_SIZE);
}

if (checkRect.contains(mouseEvent->pos())) {
bool isSelected = index.data(Qt::UserRole + 15).toBool();
model->setData(index, !isSelected, Qt::UserRole + 15);
QItemSelectionModel* selectionModel = listView->selectionModel();
if (!isSelected) {
selectionModel->select(index, QItemSelectionModel::Select);
}
else {
selectionModel->select(index, QItemSelectionModel::Deselect);
}

return true;
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
private:
QListView* listView;
};

4.效果图