Qt编程:实现图像查看器

功能特点

  1. 自定义模型

    • 继承 QAbstractListModel 实现自定义数据模型

    • 支持多种数据角色(显示、缩略图、完整路径等)

    • 自动生成缩略图并缓存

  2. 横向排列

    • 使用 QListView 的 IconMode 和 LeftToRight 流动方向

    • 禁用换行和垂直滚动条

    • 自定义样式美化外观

  3. 完整功能

    • 加载本地图片文件夹

    • 显示大图和缩略图

    • 图片计数和自动切换功能

    • 响应式布局适应窗口大小变化

  4. 性能优化

    • 只在需要时加载缩略图

    • 使用 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();
}

扩展迭代

  1. 添加图片过滤功能(按大小、类型等)

  2. 实现图片缩放和旋转功能

  3. 添加幻灯片播放模式

  4. 支持拖放添加图片

  5. 实现多选和批量操作功能

  6. 添加图片元数据(EXIF)显示功能

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值