
功能特点
-
自定义模型:
-
继承 QAbstractListModel 实现自定义数据模型
-
支持多种数据角色(显示、缩略图、完整路径等)
-
自动生成缩略图并缓存
-
-
横向排列:
-
使用 QListView 的 IconMode 和 LeftToRight 流动方向
-
禁用换行和垂直滚动条
-
自定义样式美化外观
-
-
完整功能:
-
加载本地图片文件夹
-
显示大图和缩略图
-
图片计数和自动切换功能
-
响应式布局适应窗口大小变化
-
-
性能优化:
-
只在需要时加载缩略图
-
使用 QVector 存储图片数据
-
合理的模型更新机制
-
#pragma once
#include <QAbstractListModel>
#include <QPixmap>
#include <QFileInfo>
#include <QDir>
#include <QImageReader>
class ImageListModel : public QAbstractListModel {
Q_OBJECT
public:
explicit ImageListModel(QObject* parent = nullptr) : QAbstractListModel(parent) {}
int rowCount(const QModelIndex& parent = QModelIndex()) const override {
Q_UNUSED(parent);
return m_images.size();
}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override {
if (!index.isValid() || index.row() >= m_images.size())
return QVariant();
const ImageData& image = m_images.at(index.row());
switch (role) {
case Qt::DisplayRole:
return image.name;
case Qt::DecorationRole:
return image.thumbnail;
case Qt::UserRole:
return image.filePath;
case Qt::ToolTipRole:
return QString("%1\n%2x%3").arg(image.name).arg(image.width).arg(image.height);
default:
return QVariant();
}
}
void loadImagesFromFolder(const QString& folderPath) {
beginResetModel();
m_images.clear();
QDir dir(folderPath);
QStringList filters;
for (const QByteArray& format : QImageReader::supportedImageFormats()) {
filters << "*." + QString(format).toLower();
}
QStringList imageFiles = dir.entryList(filters, QDir::Files | QDir::Readable, QDir::Name);
for (const QString& fileName : imageFiles) {
QString filePath = dir.absoluteFilePath(fileName);
QImage image(filePath);
if (!image.isNull()) {
ImageData data;
data.filePath = filePath;
data.name = QFileInfo(fileName).baseName();
data.width = image.width();
data.height = image.height();
// 生成缩略图
data.thumbnail = QPixmap::fromImage(image.scaled(
80, 80,
Qt::KeepAspectRatio,
Qt::SmoothTransformation
));
m_images.append(data);
}
}
endResetModel();
}
QString getImagePath(const QModelIndex& index) const {
if (index.isValid() && index.row() < m_images.size()) {
return m_images.at(index.row()).filePath;
}
return QString();
}
private:
struct ImageData {
QString filePath;
QString name;
QPixmap thumbnail;
int width;
int height;
};
QVector<ImageData> m_images;
};
#include <QListView>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
#include <QMessageBox>
#include "ImageListModul.h"
#include <QTimer>
#include <QScrollBar>
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(QWidget* parent = nullptr) : QWidget(parent) {
setupUI();
connectSignals();
}
private:
void setupUI() {
QVBoxLayout* mainLayout = new QVBoxLayout(this);
// 图片显示区域
m_imageLabel = new QLabel(this);
m_imageLabel->setAlignment(Qt::AlignCenter);
m_imageLabel->setStyleSheet("QLabel { background-color : #333; }");
m_imageLabel->setMinimumSize(400, 300);
m_imageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout->addWidget(m_imageLabel);
// 图片列表视图
m_listView = new QListView(this);
m_listView->setViewMode(QListView::IconMode);
m_listView->setFlow(QListView::LeftToRight);
m_listView->setWrapping(false);
m_listView->setSpacing(10);
m_listView->setResizeMode(QListView::Adjust);
m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
m_listView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_listView->setIconSize(QSize(80, 80));
m_listView->setSelectionMode(QAbstractItemView::SingleSelection);
//m_listView->setUniformItemSizes(true);
m_listView->setGridSize(QSize(90, 90)); // 略大于缩略图尺寸
// 自定义滚动条样式
m_listView->setStyleSheet(
"QListView {"
" background-color: #f0f0f0;"
" border: none;"
"}"
"QListView::item {"
" border: 1px solid #ddd;"
" border-radius: 4px;"
" padding: 0;"
"}"
"QScrollBar:horizontal {"
" height: 16px;"
" margin: 0 16px 0 16px;"
" background: transparent;"
" border: none;"
"}"
"QScrollBar::handle:horizontal {"
" background: #aaa;"
" min-width: 30px;"
" border-radius: 8px;"
"}"
"QScrollBar::add-line:horizontal {"
" width: 16px;"
" background: #ddd;"
" subcontrol-position: right;"
" border-top-right-radius: 8px;"
" border-bottom-right-radius: 8px;"
"}"
"QScrollBar::sub-line:horizontal {"
" width: 16px;"
" background: #ddd;"
" subcontrol-position: left;"
" border-top-left-radius: 8px;"
" border-bottom-left-radius: 8px;"
"}"
"QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {"
" background: none;"
"}"
);
m_listView->setMaximumHeight(110);
//mainLayout->addWidget(m_listView);
// 创建容器widget和布局
QWidget* container = new QWidget(this);
//QVBoxLayout* containerLayout = new QVBoxLayout(container);
//containerLayout->setContentsMargins(0, 0, 0, 0);
//containerLayout->setSpacing(0);
// 添加列表视图
//containerLayout->addWidget(m_listView);
// 添加水平滚动条控制按钮
QHBoxLayout* scrollControlLayout = new QHBoxLayout(container);
scrollControlLayout->setContentsMargins(16, 0, 16, 0);
scrollControlLayout->setSpacing(0);
container->setMaximumHeight(120);
QPushButton* leftBtn = new QPushButton("<", this);
QPushButton* rightBtn = new QPushButton(">", this);
// 设置按钮样式
QString btnStyle = "QPushButton {"
" background: #ddd;"
" border: none;"
" min-width: 16px;"
" max-width: 16px;"
" border-radius: 8px;"
"}"
"QPushButton:hover {"
" background: #ccc;"
"}";
leftBtn->setStyleSheet(btnStyle);
rightBtn->setStyleSheet(btnStyle);
scrollControlLayout->addWidget(leftBtn);
scrollControlLayout->addWidget(m_listView);
//scrollControlLayout->addStretch();
scrollControlLayout->addWidget(rightBtn);
//containerLayout->addLayout(scrollControlLayout);
mainLayout->addWidget(container);
// 连接按钮信号
//connect(leftBtn, &QPushButton::clicked, this, [this]() {
// m_listView->horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
// });
//connect(rightBtn, &QPushButton::clicked, this, [this]() {
// m_listView->horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
// });
// 连接按钮信号
connect(leftBtn, &QPushButton::clicked, this, [this]() {
m_listView->horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub);
});
connect(rightBtn, &QPushButton::clicked, this, [this]() {
m_listView->horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd);
});
// 控制面板
QHBoxLayout* controlLayout = new QHBoxLayout();
m_imageCounter = new QLabel("Src (0/0)", this);
controlLayout->addWidget(m_imageCounter);
m_autoSwitchCheck = new QCheckBox("AutoSwitch", this);
controlLayout->addWidget(m_autoSwitchCheck);
m_loadFolderBtn = new QPushButton("LoadFolder", this);
controlLayout->addWidget(m_loadFolderBtn);
m_runSelectedBtn = new QPushButton("Running", this);
controlLayout->addWidget(m_runSelectedBtn);
controlLayout->addStretch();
mainLayout->addLayout(controlLayout);
// 创建模型
m_imageModel = new ImageListModel(this);
m_listView->setModel(m_imageModel);
}
void connectSignals() {
connect(m_loadFolderBtn, &QPushButton::clicked, this, &ImageViewer::loadImageFolder);
connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ImageViewer::showSelectedImage);
connect(m_runSelectedBtn, &QPushButton::clicked, this, &ImageViewer::runSelectedImage);
connect(m_autoSwitchCheck, &QCheckBox::stateChanged, this, &ImageViewer::toggleAutoSwitch);
}
private slots:
void loadImageFolder() {
QString dirPath = QFileDialog::getExistingDirectory(
this,
"Folder",
QDir::homePath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
);
if (!dirPath.isEmpty()) {
m_imageModel->loadImagesFromFolder(dirPath);
updateImageCounter();
// 自动选择第一张图片
if (m_imageModel->rowCount() > 0) {
m_listView->setCurrentIndex(m_imageModel->index(0));
}
}
}
void showSelectedImage(const QModelIndex& current) {
QString imagePath = m_imageModel->getImagePath(current);
if (!imagePath.isEmpty()) {
QPixmap pixmap(imagePath);
if (!pixmap.isNull()) {
pixmap = pixmap.scaled(
m_imageLabel->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation
);
m_imageLabel->setPixmap(pixmap);
}
updateImageCounter();
}
}
void runSelectedImage() {
QModelIndex current = m_listView->currentIndex();
if (current.isValid()) {
QString imagePath = m_imageModel->getImagePath(current);
QMessageBox::information(this, "RunningImage",
QString("RunningImage: %1").arg(QFileInfo(imagePath).fileName()));
// 这里添加实际运行图片的代码
}
}
void toggleAutoSwitch(int state) {
if (state == Qt::Checked) {
// 启动自动切换定时器
m_autoSwitchTimer = new QTimer(this);
connect(m_autoSwitchTimer, &QTimer::timeout, this, &ImageViewer::switchToNextImage);
m_autoSwitchTimer->start(3000); // 3秒切换一次
}
else {
// 停止自动切换
if (m_autoSwitchTimer) {
m_autoSwitchTimer->stop();
m_autoSwitchTimer->deleteLater();
m_autoSwitchTimer = nullptr;
}
}
}
void switchToNextImage() {
int currentRow = m_listView->currentIndex().row();
int nextRow = (currentRow + 1) % m_imageModel->rowCount();
m_listView->setCurrentIndex(m_imageModel->index(nextRow));
}
void updateImageCounter() {
int current = m_listView->currentIndex().row() + 1;
int total = m_imageModel->rowCount();
m_imageCounter->setText(QString("Src (%1/%2)").arg(current).arg(total));
}
private:
QLabel* m_imageLabel;
QListView* m_listView;
ImageListModel* m_imageModel;
QLabel* m_imageCounter;
QCheckBox* m_autoSwitchCheck;
QPushButton* m_loadFolderBtn;
QPushButton* m_runSelectedBtn;
QTimer* m_autoSwitchTimer = nullptr;
};
#include <QApplication>
#include "ImageViewer.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
ImageViewer viewer;
viewer.resize(600, 500);
viewer.setWindowTitle("ImageViewer");
viewer.show();
return app.exec();
}
扩展迭代
-
添加图片过滤功能(按大小、类型等)
-
实现图片缩放和旋转功能
-
添加幻灯片播放模式
-
支持拖放添加图片
-
实现多选和批量操作功能
-
添加图片元数据(EXIF)显示功能
1347

被折叠的 条评论
为什么被折叠?



