小项目实验:图片切换

1.概要

qt c++ 做一个图片流量功能的窗口,图片从一个文件夹内读取,将文件夹的呢图片文件生成一个列表。窗口有两个按钮是上一页和下一页,用于图片的切换;图片的下面有一个刻度的标识,从0~2560,刻度值显示100的整倍数的刻度值;这个文件夹的图片都是2560宽的,高度是100或者200;窗口的宽度是1280,所以图形的显示区域要有一个滚动条,一个刻度值,等于一个 像素,这个刻度的目的就是为了显示图片的像素位置,不要对图片进行缩放

2.内容

以下是实现不缩放图片的精确像素查看器完整代码:

绘图类

#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H

#include <QApplication>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QDebug>

class PixelRuler : public QGraphicsItem {
public:
    PixelRuler(qreal width) : m_width(width) {}

    QRectF boundingRect() const override {
        return QRectF(0, 0, m_width, 40); // 增加底部空间
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option,
               QWidget *widget) override {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        painter->setRenderHint(QPainter::Antialiasing);
        painter->fillRect(boundingRect(), Qt::white);
        painter->setPen(Qt::black);

        // 绘制主刻度线(每100像素,延长至25像素)
        for (int i = 0; i <= m_width; i += 100) {
            painter->drawLine(i, 10, i, 25); // 延长主刻度线

            // 绘制主刻度值
            painter->setFont(QFont("Arial", 8));
            QString text = QString::number(i);
            QRectF textRect(i - 15, 28, 30, 20); // 文字下移
            painter->drawText(textRect, Qt::AlignCenter, text);
        }

        // 绘制次要刻度线(每10像素,保持15像素高度)
        for (int i = 0; i <= m_width; i += 10) {
            if (i % 100 == 0) continue;
            painter->drawLine(i, 10, i, 15);
        }
    }

private:
    qreal m_width;
};

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr)
        : QWidget(parent), currentIndex(0) {
        setupUI();
        loadImages(QDir::homePath() + "/Pictures");
    }

private:
    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QGraphicsPixmapItem *imageItem;
    PixelRuler *ruler;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;
    QStringList imagePaths;
    int currentIndex;
    const int VIEW_WIDTH = 1280;
    const int RULER_HEIGHT = 30;
    const int IMAGE_WIDTH = 2560;

    void setupUI() {
        // 创建控件
        openFolderBtn = new QPushButton("Open Folder", this);
        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");

        // 按钮布局
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(nextBtn);
        btnLayout->addWidget(openFolderBtn);

        // 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new QGraphicsView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);
        graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
        graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

        // 主布局
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(graphicsView);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);

        // 初始场景设置
        imageItem = new QGraphicsPixmapItem;
        scene->addItem(imageItem);
        ruler = new PixelRuler(IMAGE_WIDTH);
        scene->addItem(ruler);
    }

    void loadImages(const QString &directory) {
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        QStringList allFiles = dir.entryList(filters, QDir::Files | QDir::Readable);

        imagePaths.clear();
        for (const QString &file : allFiles) {
            QString filePath = dir.absoluteFilePath(file);
            QPixmap pixmap(filePath);

            if (pixmap.width() == IMAGE_WIDTH &&
                (pixmap.height() == 60 || pixmap.height() == 100)) {
                imagePaths.append(filePath);
            }
        }

        if (imagePaths.isEmpty()) {
            QMessageBox::warning(this, "Error",
                                 "No valid images found (2560x100/200 required)");
            return;
        }

        std::sort(imagePaths.begin(), imagePaths.end());
        currentIndex = 0;
        updateDisplay();
    }

    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            qDebug() << "Failed to load image:" << imagePaths[currentIndex];
            QMessageBox::warning(this, "Error", "Failed to load image");
            return;
        }

        // 保持原始尺寸
        imageItem->setPixmap(pixmap);
        imageItem->setPos(0, 0);
        ruler->setPos(0, pixmap.height());

        // 设置场景大小
        scene->setSceneRect(0, 0, IMAGE_WIDTH,
                            pixmap.height() + RULER_HEIGHT);

        // 设置视图变换矩阵(禁用自动缩放)
        graphicsView->setTransform(QTransform());
        graphicsView->scale(1.0, 1.0);
        graphicsView->setSceneRect(scene->sceneRect());

        // 更新滚动条范围
        //graphicsView->horizontalScrollBar()->setRange(0, IMAGE_WIDTH - VIEW_WIDTH);
        //graphicsView->horizontalScrollBar()->setPageStep(VIEW_WIDTH);

        // 更新按钮状态
        prevBtn->setEnabled(currentIndex > 0);
        nextBtn->setEnabled(currentIndex < imagePaths.size() - 1);
    }

private slots:
    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            loadImages(dir);
        }
    }

    void showPrevious() {
        if (currentIndex > 0) {
            --currentIndex;
            updateDisplay();
        }
    }

    void showNext() {
        if (currentIndex < imagePaths.size() - 1) {
            ++currentIndex;
            updateDisplay();
        }
    }
};

#endif // IMAGEVIEWER_H

主函数 

#include "ImageViewer.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    ImageViewer viewer;
    viewer.setWindowTitle("Pixel-Perfect Image Viewer");
    viewer.resize(1280, 600);
    viewer.show();

    return app.exec();
}

运行效果

关键特性说明:

  1. 精确像素显示
    • 图片保持原始2560宽度,不进行任何缩放
    • 滚动条范围精确对应图片宽度(2560 - 1280)
    • 刻度标识与图片像素1:1对应
  2. 智能图片加载
    • 自动过滤不符合尺寸的图片(仅加载2560宽度,100/200高度的图片)
    • 支持常见图片格式(jpg/jpeg/png/bmp/gif)
    • 自动按文件名排序
  3. 界面功能
    • 固定1280像素宽的查看区域
    • 始终可见的水平滚动条
    • 精确的像素刻度标识(每10像素一个次要刻度,每100像素一个主刻度)
    • 导航按钮状态智能管理
  4. 用户体验优化
    • 平滑的滚动体验
    • 清晰的刻度标识
    • 即时的图片切换响应
    • 保持滚动位置记忆(切换图片时滚动条位置不变)

使用说明:

  1. 编译运行程序
  2. 点击"Open Folder"选择包含2560宽度图片的文件夹
  3. 使用"Previous"和"Next"按钮切换图片
  4. 拖动滚动条查看图片不同部分
  5. 观察底部刻度与图片像素位置的精确对应关系

注意事项:

  • 仅支持宽度为2560像素,高度为100或200像素的图片
  • 窗口高度会自动调整以适应不同高度的图片
  • 滚动条范围根据图片宽度自动计算
  • 刻度标识始终显示在图片底部,位置固定不变

2.功能升级-添加图片的缩放功能

需求;图片更加滑轮缩放

代码

#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H

#include <QApplication>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QDebug>


#include <QGraphicsView>
#include <QScrollBar>
#include <QWheelEvent>

//QGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);

class WheelZoomView : public QGraphicsView {
public:
    explicit WheelZoomView(QGraphicsScene *scene,QWidget *parent = nullptr)
        : QGraphicsView(scene,parent), m_scaleFactor(1.0) {


        // 设置渲染选项
        setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
        setDragMode(ScrollHandDrag); // 启用拖拽平移
    }

protected:
    void wheelEvent(QWheelEvent *event) override {
        // 计算缩放因子(每次15%)
        const qreal factor = event->angleDelta().y() > 0 ? 1.15 : 1.0 / 1.15;

        // 获取鼠标在场景中的位置
        const QPointF scenePos = mapToScene(event->position().toPoint());

        // 执行以鼠标位置为中心的缩放
        performZoom(factor, scenePos);

        event->accept();
    }

private:


    void performZoom(qreal factor, const QPointF &center) {
        // 限制缩放范围(0.1x 到 10x)
        m_scaleFactor *= factor;
        m_scaleFactor = qBound(0.1, m_scaleFactor, 10.0);

        // 计算缩放变换矩阵
        QTransform transform;
        transform.translate(center.x(), center.y());
        transform.scale(factor, factor);
        transform.translate(-center.x(), -center.y());

        // 应用变换
        setTransform(transform * this->transform());
    }

    QGraphicsScene *m_scene;
    qreal m_scaleFactor;
};

class PixelRuler : public QGraphicsItem {
public:
    PixelRuler(qreal width) : m_width(width) {}

    QRectF boundingRect() const override {
        return QRectF(0, 0, m_width, 40); // 增加底部空间
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option,
               QWidget *widget) override {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        painter->setRenderHint(QPainter::Antialiasing);
        painter->fillRect(boundingRect(), Qt::white);
        painter->setPen(Qt::black);

        // 绘制主刻度线(每100像素,延长至25像素)
        for (int i = 0; i <= m_width; i += 100) {
            painter->drawLine(i, 10, i, 25); // 延长主刻度线

            // 绘制主刻度值
            painter->setFont(QFont("Arial", 8));
            QString text = QString::number(i);
            QRectF textRect(i - 15, 28, 30, 20); // 文字下移
            painter->drawText(textRect, Qt::AlignCenter, text);
        }

        // 绘制次要刻度线(每10像素,保持15像素高度)
        for (int i = 0; i <= m_width; i += 10) {
            if (i % 100 == 0) continue;
            painter->drawLine(i, 10, i, 15);
        }
    }

private:
    qreal m_width;
};

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr)
        : QWidget(parent), currentIndex(0) {
        setupUI();
        loadImages(QDir::homePath() + "/Pictures");
    }

private:
    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QGraphicsPixmapItem *imageItem;
    PixelRuler *ruler;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;
    QStringList imagePaths;
    int currentIndex;
    const int VIEW_WIDTH = 1280;
    const int RULER_HEIGHT = 30;
    const int IMAGE_WIDTH = 2560;

    void setupUI() {
        // 创建控件
        openFolderBtn = new QPushButton("Open Folder", this);
        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");

        // 按钮布局
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(nextBtn);
        btnLayout->addWidget(openFolderBtn);

        // 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new WheelZoomView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);
        graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
        graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

        // 主布局
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(graphicsView);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);

        // 初始场景设置
        imageItem = new QGraphicsPixmapItem;
        scene->addItem(imageItem);
        ruler = new PixelRuler(IMAGE_WIDTH);
        scene->addItem(ruler);
    }

    void loadImages(const QString &directory) {
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        QStringList allFiles = dir.entryList(filters, QDir::Files | QDir::Readable);

        imagePaths.clear();
        for (const QString &file : allFiles) {
            QString filePath = dir.absoluteFilePath(file);
            QPixmap pixmap(filePath);

            if (pixmap.width() == IMAGE_WIDTH &&
                (pixmap.height() == 60 || pixmap.height() == 100)) {
                imagePaths.append(filePath);
            }
        }

        if (imagePaths.isEmpty()) {
            QMessageBox::warning(this, "Error",
                                 "No valid images found (2560x100/200 required)");
            return;
        }

        std::sort(imagePaths.begin(), imagePaths.end());
        currentIndex = 0;
        updateDisplay();
    }

    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            qDebug() << "Failed to load image:" << imagePaths[currentIndex];
            QMessageBox::warning(this, "Error", "Failed to load image");
            return;
        }

        // 保持原始尺寸
        imageItem->setPixmap(pixmap);
        imageItem->setPos(0, 0);
        ruler->setPos(0, pixmap.height());

        // 设置场景大小
        scene->setSceneRect(0, 0, IMAGE_WIDTH,
                            pixmap.height() + RULER_HEIGHT);

        // 设置视图变换矩阵(禁用自动缩放)
        graphicsView->setTransform(QTransform());
        graphicsView->scale(1.0, 1.0);
        graphicsView->setSceneRect(scene->sceneRect());

        // 更新滚动条范围
        //graphicsView->horizontalScrollBar()->setRange(0, IMAGE_WIDTH - VIEW_WIDTH);
        //graphicsView->horizontalScrollBar()->setPageStep(VIEW_WIDTH);

        // 更新按钮状态
        prevBtn->setEnabled(currentIndex > 0);
        nextBtn->setEnabled(currentIndex < imagePaths.size() - 1);
    }

private slots:
    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            loadImages(dir);
        }
    }

    void showPrevious() {
        if (currentIndex > 0) {
            --currentIndex;
            updateDisplay();
        }
    }

    void showNext() {
        if (currentIndex < imagePaths.size() - 1) {
            ++currentIndex;
            updateDisplay();
        }
    }
};

#endif // IMAGEVIEWER_H

主函数 

#include "ImageViewer.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    ImageViewer viewer;
    viewer.setWindowTitle("Pixel-Perfect Image Viewer");
    viewer.resize(1280, 600);
    viewer.show();

    return app.exec();
}

运行结果 

缩小

放大 

3.功能升级-添加文件夹切换,和位置标注、滚动

#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H

#include <QApplication>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QDebug>
#include <QLabel>


#include <QGraphicsView>
#include <QScrollBar>
#include <QWheelEvent>

//QGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr);

class WheelZoomView : public QGraphicsView {
public:
    explicit WheelZoomView(QGraphicsScene *scene,QWidget *parent = nullptr)
        : QGraphicsView(scene,parent), m_scaleFactor(1.0) {


        // 设置渲染选项
        setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
        setDragMode(ScrollHandDrag); // 启用拖拽平移
    }

protected:
    void wheelEvent(QWheelEvent *event) override {
        // 计算缩放因子(每次15%)
        const qreal factor = event->angleDelta().y() > 0 ? 1.15 : 1.0 / 1.15;

        // 获取鼠标在场景中的位置
        const QPointF scenePos = mapToScene(event->position().toPoint());

        // 执行以鼠标位置为中心的缩放
        performZoom(factor, scenePos);

        event->accept();
    }

private:


    void performZoom(qreal factor, const QPointF &center) {
        // 限制缩放范围(0.1x 到 10x)
        m_scaleFactor *= factor;
        m_scaleFactor = qBound(0.1, m_scaleFactor, 10.0);

        // 计算缩放变换矩阵
        QTransform transform;
        transform.translate(center.x(), center.y());
        transform.scale(factor, factor);
        transform.translate(-center.x(), -center.y());

        // 应用变换
        setTransform(transform * this->transform());
    }

    QGraphicsScene *m_scene;
    qreal m_scaleFactor;
};

class PixelRuler : public QGraphicsItem {
public:
    PixelRuler(qreal width) : m_width(width) {}

    QRectF boundingRect() const override {
        return QRectF(0, 0, m_width, 40); // 增加底部空间
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option,
               QWidget *widget) override {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        painter->setRenderHint(QPainter::Antialiasing);
        painter->fillRect(boundingRect(), Qt::white);
        painter->setPen(Qt::black);

        // 绘制主刻度线(每100像素,延长至25像素)
        for (int i = 0; i <= m_width; i += 100) {
            if(i==this->start||i==this->end){
                painter->setPen(Qt::red);
                painter->drawLine(i, 10, i, 25); // 延长主刻度线
                painter->setPen(Qt::black);
            }else{
                painter->drawLine(i, 10, i, 25); // 延长主刻度线
            }


            // 绘制主刻度值
            painter->setFont(QFont("Arial", 8));
            QString text = QString::number(i);
            QRectF textRect(i - 15, 28, 30, 20); // 文字下移
            painter->drawText(textRect, Qt::AlignCenter, text);
        }

        // 绘制次要刻度线(每10像素,保持15像素高度)
        for (int i = 0; i <= m_width; i += 10) {
            if (i % 100 == 0) continue;
            if(i==this->start||i==this->end){
                painter->setPen(Qt::red);
                painter->drawLine(i, 10, i, 15);
                painter->setPen(Qt::black);
            }else{
                painter->drawLine(i, 10, i, 15);
            }

        }
    }
public:
    int start;
    int end;
private:
    qreal m_width;
};

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr)
        : QWidget(parent), currentIndex(0) {
        setupUI();
        //loadImages(QDir::homePath() + "/Pictures");
        loadMainFolders("D:\\imgs");
        loadCurrentFolder();
    }

private:
    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QGraphicsPixmapItem *imageItem;
    PixelRuler *ruler;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;

    QPushButton *prevDayBtn;
    QPushButton *nextDayBtn;
    QStringList imagePaths;
    int currentIndex;
    const int VIEW_WIDTH = 1280;
    const int RULER_HEIGHT = 30;
    const int IMAGE_WIDTH = 2560;

    // UI组件
    QLabel *folderLabel;
    QLabel *imageStatusLabel;

    // 数据存储
    QStringList folderList;
    //QStringList imageFiles;
    int currentFolderIndex;
    //int currentImageIndex;



    void setupUI() {
        // 创建控件
        prevDayBtn = new QPushButton("前一天");
        nextDayBtn = new QPushButton("后一天");
        folderLabel = new QLabel("1/9 20250425");
        // 创建控件
        openFolderBtn = new QPushButton("Open Folder", this);

        // 按钮布局
        QHBoxLayout *btnLayout_top = new QHBoxLayout;
        btnLayout_top->addWidget(prevDayBtn);
        btnLayout_top->addWidget(folderLabel);
        btnLayout_top->addWidget(nextDayBtn);
        btnLayout_top->addWidget(openFolderBtn);


        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");
        imageStatusLabel = new QLabel("0/0");

        // 按钮布局
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(imageStatusLabel);
        btnLayout->addWidget(nextBtn);


        // 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new WheelZoomView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);
        graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
        graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

        // 主布局
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addLayout(btnLayout_top);
        mainLayout->addWidget(graphicsView);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);

        // 信号槽连接
        connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
        connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);

        // 初始场景设置
        imageItem = new QGraphicsPixmapItem;
        scene->addItem(imageItem);
        ruler = new PixelRuler(IMAGE_WIDTH);
        scene->addItem(ruler);
    }

    //加载图片文件夹
    void loadImages(const QString &directory) {
        qDebug()<<"directory:"+directory;
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        QStringList allFiles = dir.entryList(filters, QDir::Files | QDir::Readable);

        imagePaths.clear();
        for (const QString &file : allFiles) {
            QString filePath = dir.absoluteFilePath(file);
            QPixmap pixmap(filePath);

            if (pixmap.width() == IMAGE_WIDTH &&
                (pixmap.height() == 60 ||pixmap.height() == 100 || pixmap.height() == 200)) {
                imagePaths.append(filePath);
            }
        }

        if (imagePaths.isEmpty()) {
            QMessageBox::warning(this, "Error",
                                 "No valid images found (2560x100/200 required)");
            return;
        }



        std::sort(imagePaths.begin(), imagePaths.end());
        currentIndex = 0;



        updateDisplay();
    }

    //更新刻度尺
    void updateRuler(){
        QStringList str =  imagePaths[currentIndex].split("=")[1].split("_")[0].split("-");
        qDebug()<<str;
        int a = str[0].toInt();
        int b = str[1].toInt();
        ruler->start = (a/10)*10;
        ruler->end = (b/10+1)*10;
        qDebug()<<ruler->end;
        if(ruler->end>2560){
            ruler->end = 2560;
        }
        ruler->update();

        int stroll = a;
        if(stroll==0){

        }else{
            stroll= stroll/2;
        }
        this->graphicsView->horizontalScrollBar()->setValue(stroll);
    }

    //更新图片
    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            qDebug() << "Failed to load image:" << imagePaths[currentIndex];
            QMessageBox::warning(this, "Error", "Failed to load image");
            return;
        }

        // 保持原始尺寸
        imageItem->setPixmap(pixmap);
        imageItem->setPos(0, 0);
        ruler->setPos(0, pixmap.height());

        // 设置场景大小
        scene->setSceneRect(0, 0, IMAGE_WIDTH,
                            pixmap.height() + RULER_HEIGHT);

        // 设置视图变换矩阵(禁用自动缩放)
        graphicsView->setTransform(QTransform());
        graphicsView->scale(1.0, 1.0);
        graphicsView->setSceneRect(scene->sceneRect());

        // 更新滚动条范围
        //graphicsView->horizontalScrollBar()->setRange(0, IMAGE_WIDTH - VIEW_WIDTH);
        //graphicsView->horizontalScrollBar()->setPageStep(VIEW_WIDTH);

        // 更新按钮状态
        prevBtn->setEnabled(currentIndex > 0);
        nextBtn->setEnabled(currentIndex < imagePaths.size() - 1);

        //更新刻度尺
        updateRuler();
    }

    //加载主目录
    void loadMainFolders(const QString &mainDir) {
        qDebug()<<"loadMainFolders";
        QDir dir(mainDir);
        QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);

        for (const QString &folder : folders) {
            if (QDate::fromString(folder, "yyyyMMdd").isValid()) {
                folderList.append(dir.filePath(folder));
            }
        }

        std::sort(folderList.begin(), folderList.end(), [](const QString &a, const QString &b) {
            return QFileInfo(a).fileName() < QFileInfo(b).fileName();
        });

        currentFolderIndex = folderList.size() - 1;
        qDebug()<<"loadMainFolders end";
    }

    //加载当前文件夹
    void loadCurrentFolder() {
        if (folderList.isEmpty()) return;

        //QDir dir(folderList[currentFolderIndex]);
        //QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
        //imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
        currentIndex = 0;

        loadImages(folderList[currentFolderIndex]);

        //if (!imageFiles.isEmpty()) loadImage();
        updateFolderInfo();
        updateImageStatus();
    }

    //更换文件夹
    void navigateFolder(int step) {
        int newIndex = currentFolderIndex + step;
        if (newIndex >= 0 && newIndex < folderList.size()) {
            currentFolderIndex = newIndex;
            loadCurrentFolder();
        }
    }

    //更新文件夹名称
    void updateFolderInfo() {
        if (folderList.isEmpty()) return;

        QString info = QString("%1/%2 %3")
                           .arg(currentFolderIndex + 1)
                           .arg(folderList.size())
                           .arg(QFileInfo(folderList[currentFolderIndex]).fileName());
        folderLabel->setText(info);
        //ruler->repaint();  // 强制立即重绘
    }

    //更新文件名称
    void updateImageStatus() {
        if (imagePaths.isEmpty()) {
            imageStatusLabel->setText("0/0");
            return;
        }

        QString fileName = QFileInfo(imagePaths[currentIndex]).fileName();
        QString status = QString("%1/%2 %3")
                             .arg(currentIndex + 1)
                             .arg(imagePaths.size())
                             .arg(fileName);
        imageStatusLabel->setText(status);
    }

private slots:
    void prevDay() { navigateFolder(-1); }
    void nextDay() { navigateFolder(1); }

    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            //loadImages(dir);
            loadMainFolders(dir);
        }
    }

    void showPrevious() {
        if (currentIndex > 0) {
            --currentIndex;
            updateDisplay();
        }
    }

    void showNext() {
        if (currentIndex < imagePaths.size() - 1) {
            ++currentIndex;
            updateDisplay();
        }
    }
};

#endif // IMAGEVIEWER_H
#include "ImageViewer.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    ImageViewer viewer;
    viewer.setWindowTitle("Pixel-Perfect Image Viewer");
    viewer.resize(1280, 600);
    viewer.show();

    return app.exec();
}

 运行结果

3.关联链接

4.关联知识

1.QGraphicsScene 详解

QGraphicsScene 是 Qt 框架中 图形视图(Graphics View)架构 的核心组件,用于管理大量 2D 图形项(QGraphicsItem)的交互、渲染和布局。它提供了一个虚拟画布,开发者可以在其中动态添加、删除或修改图形项,并通过多个视图(QGraphicsView)进行展示。


核心功能

  1. 项管理
    作为容器,管理所有 QGraphicsItem 及其子类(如 QGraphicsRectItemQGraphicsTextItem 等)。

  2. 场景坐标系
    定义全局坐标系,所有项的位置和尺寸均基于此坐标系,支持缩放、旋转等变换。

  3. 事件传播
    自动将鼠标、键盘事件传递给场景中的项,支持复杂的交互逻辑。

  4. 索引优化
    使用 BSP树(Binary Space Partitioning Tree) 对项进行空间索引,加速渲染和碰撞检测。

  5. 多视图支持
    允许一个场景被多个 QGraphicsView 同时观察,每个视图可独立设置缩放、视口等属性。


关键概念详解

1. 场景坐标系
  • 原点位置:默认在场景左上角,但可通过 setSceneRect() 调整。
  • 项的位置与变换
    每个项的位置(pos())和变换(setTransform())均基于场景坐标系。
    示例:item->setPos(100, 50) 将项移动到场景坐标 (100,50)。
2. 项的添加与管理
  • 添加项

    QGraphicsRectItem *rect = new QGraphicsRectItem(0, 0, 100, 50);
    scene->addItem(rect);
  • 查找项
    通过 items() 或 itemAt() 获取特定位置的项。
  • 项的父子关系
    支持父子项结构,子项的位置和变换相对于父项。
3. 事件处理
  • 事件传播机制
    事件从视图传递到场景,再由场景分发给最顶层的项。若项未处理事件,则传递给父项或场景。
  • 自定义事件
    重写 QGraphicsItem 的 mousePressEvent()keyPressEvent() 等方法。
4. 性能优化
  • BSP树索引
    自动管理项的渲染顺序和碰撞检测,尤其适合动态场景。可通过 setItemIndexMethod() 调整索引策略。
  • 项的可视性
    使用 setVisible(false) 隐藏项,减少渲染开销。
  • 批量操作
    在批量修改项属性时,先调用 scene->setSceneRect(QRectF()) 禁用索引更新,操作完成后恢复。
5. 多视图支持
  • 共享场景
    多个 QGraphicsView 可观察同一场景,每个视图独立设置视口、缩放比例等。

    QGraphicsView *view1 = new QGraphicsView(scene);
    QGraphicsView *view2 = new QGraphicsView(scene);
    view1->setViewport(new QOpenGLWidget); // 使用OpenGL加速渲染

高级用法

  1. 场景序列化
    通过 QGraphicsScene::itemIndex 遍历所有项,保存其属性和位置到文件,实现场景状态的持久化。

  2. 撤销/重做操作
    结合 QUndoStack,记录项的修改历史,支持复杂编辑功能的撤销。

  3. OpenGL加速
    启用 QGraphicsView::setViewport(QOpenGLWidget*),利用硬件加速提升大规模场景的渲染性能。

  4. 自定义项
    继承 QGraphicsItem 或 QGraphicsObject,实现复杂绘制逻辑(如自定义形状、动画)。


示例代码

// 创建场景和视图
QGraphicsScene *scene = new QGraphicsScene;
QGraphicsView *view = new QGraphicsView(scene);
view->setRenderHint(QPainter::Antialiasing); // 抗锯齿

// 添加矩形和椭圆
QGraphicsRectItem *rect = scene->addRect(50, 50, 100, 80, QPen(Qt::blue), QBrush(Qt::cyan));
QGraphicsEllipseItem *ellipse = scene->addEllipse(200, 150, 80, 60, QPen(Qt::red), QBrush(Qt::yellow));

// 处理鼠标事件(在自定义项中)
class MyItem : public QGraphicsEllipseItem {
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override {
        qDebug() << "Item clicked at:" << event->pos();
    }
};

// 设置场景范围
scene->setSceneRect(0, 0, 400, 300);

常见问题

  1. 项不显示
    • 检查项的位置是否在视图的可见区域内。
    • 确认项的 zValue() 未被遮挡。
  2. 性能低下
    • 对静态项调用 setCacheMode(QGraphicsItem::DeviceCoordinateCache) 启用缓存。
    • 减少频繁更新项的几何属性(如 setPos()),合并多次操作为一次。
  3. 坐标转换
    • 使用 mapToScene() 和 mapFromScene() 在项和场景坐标系间转换坐标。

通过合理利用 QGraphicsScene 的功能,可以高效实现复杂的图形交互应用,如绘图工具、游戏地图编辑器或数据可视化界面。

 2.代码解析

// 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new QGraphicsView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);
        graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

以下是您提供的 QGraphicsView 设置代码的逐行详解,帮助您理解每个配置的作用和优化方向:


1. 场景与视图的关联

scene = new QGraphicsScene(this);
graphicsView = new QGraphicsView(scene, this);
  • 作用
    创建 QGraphicsScene 作为图形项的容器,并将 QGraphicsView 与场景绑定,视图成为观察场景的窗口。
  • 关键点
    一个场景可被多个视图共享,但每个视图有独立属性(如缩放、视口位置)。

2. 视口更新模式

graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
  • 作用
    指定视口更新方式。FullViewportUpdate 表示每次更新整个视口区域(默认行为),确保画面一致性,但可能降低性能。
  • 替代方案
    • BoundingRectViewportUpdate:仅更新项边界区域,适合频繁局部更新的场景。
    • SmartViewportUpdate:自动选择更新策略,平衡性能与一致性。

3. 拖拽模式

graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
  • 作用
    设置用户拖拽行为。ScrollHandDrag 允许用户按住鼠标中键或右键拖动视图(模拟“手形工具”)。
  • 其他模式
    • NoDrag:禁用拖拽。
    • RubberBandDrag:框选多个项(需配合 setRubberBandSelectionMode)。

4. 变换锚点

graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
  • 作用
    定义缩放或旋转时的锚点位置。AnchorUnderMouse 表示以鼠标位置为基准进行变换,保持用户操作的直观性。
  • 替代选项
    • AnchorViewCenter:以视口中心为锚点。
    • NoAnchor:无固定锚点,可能导致意外偏移。

5. 调整大小锚点

graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
  • 作用
    指定视口大小变化时的锚点。AnchorViewCenter 确保视口缩放或调整大小时,内容以视口中心为基准,避免内容偏移。

6. 固定宽度与滚动条

graphicsView->setFixedWidth(VIEW_WIDTH);
graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
  • 作用
    • 固定视图宽度,避免布局变化。
    • 始终显示水平滚动条,即使内容未超出视口(适合需要精确导航的场景)。
  • 优化建议
    • 若内容动态变化,可改用 ScrollBarAlwaysOff 并结合 ensureVisible() 自动滚动。

潜在优化方向

  1. 性能提升
    • 对静态项启用缓存:setItemCacheMode(QGraphicsItem::DeviceCoordinateCache)
    • 使用 QOpenGLWidget 作为视口:graphicsView->setViewport(new QOpenGLWidget) 加速渲染。
  2. 交互增强
    • 结合 QGraphicsItem::ItemIsMovable 允许用户拖动项。
    • 实现 wheelEvent 支持鼠标滚轮缩放。
  3. 布局适配
    • 使用 QGraphicsWidget 构建复杂UI,替代手动布局。
    • 通过 sceneRect 动态调整场景范围,优化导航体验。

完整配置示例(增强版)

// 创建场景与视图
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 2000, 2000); // 设置场景范围

graphicsView = new QGraphicsView(scene, this);
graphicsView->setViewport(new QOpenGLWidget); // 启用OpenGL加速
graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
graphicsView->setRenderHint(QPainter::Antialiasing); // 抗锯齿

// 优化滚动条策略
graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

// 自动滚动到场景边缘
graphicsView->setAlignment(Qt::AlignCenter);

通过上述配置,您可以构建一个高性能、交互友好的图形视图应用,适用于地图浏览、图表编辑等场景。

3.QPainter

QPainter 是 Qt 框架中用于 2D 图形绘制的核心类,它提供了一套高度优化的绘图工具,支持在窗口部件、图像(QPixmap)、打印机等绘图设备上进行矢量图形、文本和图像的绘制。以下是其关键点详解:


核心功能

  1. 基本图形绘制
    支持直线、矩形、椭圆、多边形、贝塞尔曲线等矢量图形的绘制。

  2. 文本渲染
    支持多字体、颜色、对齐方式的文本输出,支持复杂文本布局。

  3. 图像操作
    支持图像(QImageQPixmap)的缩放、旋转、合成等操作。

  4. 坐标变换
    通过平移、旋转、缩放等变换实现复杂绘图逻辑。

  5. 抗锯齿与合成模式
    提供多种抗锯齿算法和合成模式(如透明叠加、颜色混合)。


使用流程

  1. 创建绘图设备
    如 QWidget(在 paintEvent 中)、QPixmapQImage 等。

  2. 创建 QPainter 对象
    绑定到绘图设备:

    QPainter painter(this); // 在 QWidget 的 paintEvent 中
    // 或
    QPainter painter(&pixmap); // 在 QPixmap 上绘制
  3. 设置绘制属性
    配置画笔(QPen)、画刷(QBrush)、字体(QFont)等。

  4. 执行绘制操作
    调用 drawLine()drawRect()drawText() 等方法。

  5. 释放资源
    QPainter 对象超出作用域时自动释放,无需手动销毁。


关键类与配置

1. QPen(画笔)
  • 控制线条样式(颜色、宽度、虚线等):

    QPen pen(Qt::red);
    pen.setWidth(2);
    pen.setStyle(Qt::DashLine);
    painter.setPen(pen);
2. QBrush(画刷)
  • 控制填充样式(纯色、渐变、纹理等):

    QBrush brush(Qt::blue);
    // 渐变填充
    QLinearGradient gradient(0, 0, 100, 100);
    gradient.setColorAt(0, Qt::yellow);
    gradient.setColorAt(1, Qt::green);
    painter.setBrush(gradient);
3. 字体与文本
  • 设置字体属性并绘制文本:

    QFont font("Arial", 12);
    font.setBold(true);
    painter.setFont(font);
    painter.drawText(QRect(10, 10, 200, 30), Qt::AlignCenter, "Hello Qt!");
4. 坐标变换
  • 平移、旋转、缩放:

    painter.translate(100, 50); // 平移坐标系
    painter.rotate(45); // 旋转45度
    painter.scale(1.5, 1.5); // 放大1.5倍
    painter.drawRect(0, 0, 100, 50); // 在变换后的坐标系中绘制矩形

高级功能

  1. 路径(QPainterPath)
    创建复杂矢量路径,支持布尔运算:

    QPainterPath path;
    path.addRect(50, 50, 40, 40);
    path.addEllipse(50, 50, 80, 80);
    path.setFillRule(Qt::OddEvenFill);
    painter.drawPath(path);
  2. 合成模式(Composition Modes)
    控制图形叠加方式:

    painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    // 其他模式如 CompositionMode_Multiply、CompositionMode_Screen 等
  3. 性能优化

    • 对静态内容启用缓存:setCacheMode(DeviceCoordinateCache)
    • 避免在 paintEvent 中频繁创建/销毁对象。

示例代码(在 QWidget 中绘制)

void MyWidget::paintEvent(QPaintEvent *event) {
    Q_UNUSED(event);
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿

    // 绘制背景渐变
    QLinearGradient gradient(0, 0, width(), height());
    gradient.setColorAt(0, Qt::white);
    gradient.setColorAt(1, Qt::gray);
    painter.fillRect(rect(), gradient);

    // 绘制红色矩形
    painter.setPen(QPen(Qt::red, 2));
    painter.setBrush(Qt::NoBrush);
    painter.drawRect(50, 50, 100, 80);

    // 绘制旋转文本
    painter.save();
    painter.translate(200, 100);
    painter.rotate(30);
    painter.drawText(0, 0, "Rotated Text");
    painter.restore();
}

常见问题

  1. 绘图不更新
    • 确保在 paintEvent 中调用绘制代码,并通过 update() 触发重绘。
  2. 坐标错位
    • 检查是否误用局部坐标系或未正确恢复变换(painter.save()/painter.restore())。
  3. 性能问题
    • 对复杂场景使用 QGraphicsView 框架,而非直接操作 QPainter

与 QGraphicsView 的对比

特性QPainterQGraphicsView
适用场景简单静态图形、自定义绘制动态交互场景、大量图形项
性能直接绘制,适合简单图形通过场景管理,适合复杂交互
坐标变换手动管理自动管理项的位置和变换
项管理无内置项管理通过 QGraphicsItem 管理

选择依据:简单绘图用 QPainter,复杂交互场景用 QGraphicsView

4.QRectF 详解

QRectF 是 Qt 框架中用于表示浮点数精度矩形的类,常用于图形编程、几何计算等需要高精度坐标的场景。以下是其详解:


1. 核心概念

  • 坐标类型:使用 qreal(通常为 float 或 double,取决于编译配置)存储坐标,支持小数精度。
  • 不可变性QRectF 对象不可直接修改,所有操作会返回新对象,原对象保持不变。
  • 标准化(Normalized):确保左上角坐标 ≤ 右下角坐标,避免宽高为负数。

2. 创建 QRectF

构造函数

// 通过左上角坐标和宽高创建
QRectF(qreal x, qreal y, qreal width, qreal height);


// 通过左上角和右下角坐标创建
QRectF(const QPointF &topLeft, const QPointF &bottomRight);


// 直接指定坐标值
QRectF(qreal x1, qreal y1, qreal x2, qreal y2);
静态方法

// 合并两个矩形(返回包含两者的最小矩形)
QRectF QRectF::united(const QRectF &r1, const QRectF &r2);


// 计算两个矩形的交集
QRectF QRectF::intersected(const QRectF &r1, const QRectF &r2);

3. 常用成员函数

方法功能
x()y()返回左上角 x/y 坐标
width()height()返回宽高
left()right()返回左/右边界坐标
top()bottom()返回上/下边界坐标
topLeft()bottomRight()返回左上角和右下角坐标的 QPointF
contains(const QPointF &point)判断点是否在矩形内
intersects(const QRectF &rect)判断是否与另一个矩形相交
adjusted(qreal dx1, qreal dy1, qreal dx2, qreal dy2)调整矩形边界(返回新对象)
translated(qreal dx, qreal dy)平移矩形(返回新对象)
toRect()转换为整数精度的 QRect(可能丢失精度)
isNull()判断矩形是否无效(宽或高为负数或零)

4. 与 QRect 的区别

特性QRectFQRect
坐标类型qreal(浮点)int(整数)
精度高精度,适合图形渲染低精度,适合像素操作
转换方法toRect() 转为 QRecttoRectF() 转为 QRectF

5. 使用场景

  • 图形渲染:在 QPainter 中绘制矢量图形(如抗锯齿路径)。
  • 几何计算:需要高精度坐标的碰撞检测、变换操作。
  • 图形视图框架:与 QGraphicsItem 等组件配合使用。

6. 示例代码

// 创建矩形:左上角 (10.5, 20.3),宽 80.0,高 60.0
QRectF rect1(10.5, 20.3, 80.0, 60.0);

// 平移矩形
QRectF rect2 = rect1.translated(5.0, -3.0);

// 调整边界(左/上/右/下各扩展 2.0)
QRectF rect3 = rect1.adjusted(-2.0, -2.0, 2.0, 2.0);

// 判断点是否在矩形内
QPointF point(30.0, 40.0);
if (rect1.contains(point)) {
    qDebug() << "点在矩形内";
}

// 转换为 QRect(可能丢失精度)
QRect rectInt = rect1.toRect();

7. 注意事项

  • 不可变性:频繁修改需创建新对象,可能影响性能,建议复用对象或预计算。
  • 标准化检查:使用 normalized() 确保左上角 ≤ 右下角。
  • 精度丢失:转换为 QRect 时,坐标会被截断为整数。

通过合理使用 QRectF,可以在 Qt 中高效处理高精度的几何运算和图形渲染任务。

5.QGraphicsView

QGraphicsView 是 Qt 框架中 图形视图框架(Graphics View Framework) 的核心组件之一,用于高效显示和管理大型可滚动的二维图形场景(QGraphicsScene)。以下是其详解:


1. 核心概念

  • 作用:作为视口(Viewport),显示 QGraphicsScene 的内容,支持缩放、平移、旋转等操作。
  • 坐标系:默认以场景坐标(Scene Coordinates)为中心,可通过变换矩阵调整视图坐标系。
  • 性能优化:仅渲染可见区域内的图元(QGraphicsItem),适合处理复杂或大规模场景。

2. 主要功能

功能说明
场景浏览通过滚动条查看超出视口范围的场景部分
视图变换支持缩放(scale())、旋转(rotate())、平移(translate()
坐标映射场景坐标 ↔ 视图坐标 ↔ 部件坐标的转换
渲染控制设置抗锯齿、平滑变换等渲染质量参数
多视图支持多个 QGraphicsView 可观察同一场景,独立变换

3. 常用成员函数

场景管理

// 设置关联的场景
void setScene(QGraphicsScene *scene);


// 获取当前场景
QGraphicsScene *scene() const;
视图变换

// 缩放视图(以视图中心为基准)
void scale(qreal sx, qreal sy);


// 旋转视图(以视图中心为基准)
void rotate(qreal angle);


// 平移视图
void translate(qreal dx, qreal dy);


// 重置所有变换(恢复原始状态)
void resetTransform();
坐标映射

// 将视图坐标转换为场景坐标
QPointF mapToScene(const QPointF &viewPos) const;


// 将场景坐标转换为视图坐标
QPointF mapFromScene(const QPointF &scenePos) const;


// 将部件坐标转换为场景坐标(需考虑视口偏移)
QPointF mapToScene(const QPoint &widgetPos) const;
渲染设置

// 设置渲染质量(如抗锯齿)
void setRenderHints(QPainter::RenderHints hints);


// 设置视口更新模式(如是否保留绘制内容)
void setViewportUpdateMode(ViewportUpdateMode mode);
滚动条控制

// 设置水平和垂直滚动条策略
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy);
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy);

4. 信号与槽

  • 场景变化信号:当场景内容或结构变化时触发。
    void sceneRectChanged(const QRectF &rect);
  • 视图变换信号:视图发生缩放、旋转等操作时触发。
    void transformChanged();

5. 使用场景

  1. 大规模图形显示:如地图、电路图、流程图等需要滚动和缩放的内容。
  2. 交互式应用:结合鼠标和键盘事件实现拖拽、选择、编辑图元。
  3. 多视图同步:多个 QGraphicsView 观察同一场景,独立操作但数据共享。

6. 示例代码

// 创建场景和视图
QGraphicsScene *scene = new QGraphicsScene;
QGraphicsView *view = new QGraphicsView(scene);


// 添加矩形图元
QGraphicsRectItem *rect = scene->addRect(0, 0, 100, 100);
rect->setBrush(Qt::blue);


// 设置视图属性
view->setRenderHint(QPainter::Antialiasing); // 抗锯齿
view->setDragMode(QGraphicsView::ScrollHandDrag); // 手型工具拖拽


// 显示视图
view->show();

7. 高级特性

  • 项选择:通过 setSelectionMode() 实现单选/多选图元。
  • 锚点布局:使用 QGraphicsAnchorLayout 实现图元的自动布局。
  • 嵌入部件:通过 QGraphicsProxyWidget 将 QWidget 嵌入场景。
  • OpenGL 加速:启用 setViewport(new QOpenGLWidget) 提升渲染性能。

8. 注意事项

  • 性能优化:避免频繁修改场景,批量操作使用 QGraphicsScene::setSceneRect()
  • 坐标转换:注意场景、视图、部件坐标系的转换关系。
  • 线程安全:图形操作需在主线程执行。

通过合理使用 QGraphicsView,可以实现复杂、高性能的图形交互界面。

5.过程项目

1.基本功能

qt c++ 做一个图片流量功能的窗口,图片从一个文件夹内读取,将文件夹的呢图片文件生成一个列表。窗口有两个按钮是上一页和下一页,用于图片的切换 

以下是使用Qt C++实现图片浏览功能的完整代码示例:

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QFileDialog>
#include <QDir>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPixmap>

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr) : QWidget(parent), currentIndex(0) {
        setupUI();
        loadImages(QDir::homePath() + "/Pictures"); // 默认加载用户图片目录
    }

private:
    QLabel *imageLabel;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;
    QStringList imagePaths;
    int currentIndex;
    QString currentDirectory;

    void setupUI() {
        // 创建控件
        imageLabel = new QLabel("No images loaded", this);
        imageLabel->setAlignment(Qt::AlignCenter);
        imageLabel->setMinimumSize(400, 300);
        imageLabel->setScaledContents(true);

        openFolderBtn = new QPushButton("Open Folder", this);
        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");

        // 布局设置
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(nextBtn);
        btnLayout->addWidget(openFolderBtn);

        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(imageLabel);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);
    }

    void loadImages(const QString &directory) {
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        imagePaths = dir.entryList(filters, QDir::Files | QDir::Readable);

        // 转换为绝对路径并排序
        for (int i = 0; i < imagePaths.size(); ++i) {
            imagePaths[i] = dir.absoluteFilePath(imagePaths[i]);
        }
        std::sort(imagePaths.begin(), imagePaths.end());

        currentDirectory = directory;
        currentIndex = 0;
        updateDisplay();
    }

    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageLabel->setText("No images found");
            prevBtn->setEnabled(false);
            nextBtn->setEnabled(false);
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            imageLabel->setText("Failed to load image");
            return;
        }

        // 保持宽高比缩放图片
        QSize labelSize = imageLabel->size();
        pixmap = pixmap.scaled(labelSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        imageLabel->setPixmap(pixmap);

        // 更新按钮状态
        prevBtn->setEnabled(imagePaths.size() > 1);
        nextBtn->setEnabled(imagePaths.size() > 1);
    }

private slots:
    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            loadImages(dir);
        }
    }

    void showPrevious() {
        if (imagePaths.isEmpty()) return;
        
        currentIndex = (currentIndex - 1 + imagePaths.size()) % imagePaths.size();
        updateDisplay();
    }

    void showNext() {
        if (imagePaths.isEmpty()) return;
        
        currentIndex = (currentIndex + 1) % imagePaths.size();
        updateDisplay();
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    ImageViewer viewer;
    viewer.setWindowTitle("Image Viewer");
    viewer.resize(800, 600);
    viewer.show();

    return app.exec();
}

#include "main.moc"

代码说明:

  1. 界面布局
    • 使用垂直布局管理主窗口
    • 顶部为图片显示区域(QLabel)
    • 底部为三个按钮:打开文件夹、上一张、下一张
  2. 核心功能
    • 打开文件夹:通过文件对话框选择图片目录
    • 图片加载:支持常见图片格式(jpg/jpeg/png/bmp/gif)
    • 自动排序:按文件名排序图片
    • 循环浏览:到达首尾时自动循环
    • 自动缩放:保持图片比例适应窗口大小
  3. 特性
    • 智能按钮状态管理(无图片时禁用导航按钮)
    • 错误处理(加载失败提示)
    • 支持拖放窗口调整大小
    • 图片平滑缩放处理

使用说明:

  1. 编译运行程序
  2. 点击"Open Folder"选择包含图片的目录
  3. 使用"Previous"和"Next"按钮浏览图片
  4. 调整窗口大小时图片会自动缩放

扩展建议:

  • 添加幻灯片播放功能
  • 支持图片旋转/缩放操作
  • 添加缩略图导航栏
  • 支持键盘快捷键(←/→键切换)
  • 添加图片信息展示(尺寸、格式等)

这个实现使用了Qt的核心模块,无需额外依赖,可以在支持Qt5/Qt6的环境下直接编译运行。

2.QDir::homePath() 指向哪里

QDir::homePath() 是 Qt 中用于获取当前用户家目录路径的跨平台方法,具体指向位置取决于操作系统:

  1. Windows
    C:\Users\<用户名> 或 C:\Documents and Settings\<用户名>(旧系统)

  2. Linux/Unix
    /home/<用户名> 或通过 ~ 符号解析的路径

  3. macOS
    /Users/<用户名>

特点:

  • 自动适配不同操作系统
  • 无需硬编码路径(如 C:\ 或 /home/
  • 返回路径末尾带路径分隔符(如 / 或 \
  • 受系统环境变量影响(如 HOME 环境变量)

使用场景示例:

// 获取用户文档目录(跨平台安全)
QString docPath = QDir::homePath() + "Documents/MyApp/";


// 创建配置文件路径
QString configPath = QDir::homePath() + ".config/myapp.ini";

注意事项:

  1. 需要读写权限时,应检查目录是否存在并处理权限问题
  2. 多用户系统下,返回的是当前运行程序用户的家目录
  3. 部分系统可能通过环境变量覆盖默认路径(如 HOME

如果需要获取其他特殊目录(如桌面、下载目录),建议使用 QStandardPaths 类,它提供更丰富的标准路径获取方式。

3.添加刻度 

qt c++ 做一个图片流量功能的窗口,图片从一个文件夹内读取,将文件夹的呢图片文件生成一个列表。窗口有两个按钮是上一页和下一页,用于图片的切换;图片的下面有一个刻度的标识,从0~2560,刻度值显示100的整倍数的刻度值;这个文件夹的图片都是2560宽的,高度是100或者200;窗口的宽度是1280,所以图形的显示区域要有一个滚动条。

以下是实现图片流量查看功能的Qt C++完整代码,包含刻度标识和滚动条支持:

#include <QApplication>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QDir>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>

class RulerItem : public QGraphicsItem {
public:
    RulerItem(qreal width, qreal height)
        : m_width(width), m_height(height) {}

    QRectF boundingRect() const override {
        return QRectF(0, 0, m_width, m_height);
    }

    void paint(QPainter *painter, 
               const QStyleOptionGraphicsItem *option,
               QWidget *widget) override {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        painter->setRenderHint(QPainter::Antialiasing);
        painter->fillRect(boundingRect(), Qt::white);

        // 绘制横轴
        painter->drawLine(0, m_height/2, m_width, m_height/2);

        // 绘制刻度和数字
        for (int i = 0; i <= 2560; i += 100) {
            qreal x = i;
            painter->drawLine(x, 0, x, m_height);
            QString text = QString::number(i);
            QRectF textRect(x - 15, m_height - 20, 30, 20);
            painter->drawText(textRect, Qt::AlignCenter, text);
        }
    }

private:
    qreal m_width;
    qreal m_height;
};

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr)
        : QWidget(parent), currentIndex(0) {
        setupUI();
        loadImages(QDir::homePath() + "/Pictures");
    }

private:
    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QGraphicsPixmapItem *imageItem;
    RulerItem *rulerItem;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;
    QStringList imagePaths;
    int currentIndex;
    const int VIEW_WIDTH = 1280;
    const int RULER_HEIGHT = 50;

    void setupUI() {
        // 创建控件
        openFolderBtn = new QPushButton("Open Folder", this);
        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");

        // 按钮布局
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(nextBtn);
        btnLayout->addWidget(openFolderBtn);

        // 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new QGraphicsView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);

        // 主布局
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(graphicsView);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);

        // 初始场景设置
        imageItem = new QGraphicsPixmapItem;
        scene->addItem(imageItem);
        rulerItem = new RulerItem(2560, RULER_HEIGHT);
        scene->addItem(rulerItem);
    }

    void loadImages(const QString &directory) {
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        imagePaths = dir.entryList(filters, QDir::Files | QDir::Readable);

        // 转换为绝对路径并排序
        for (int i = 0; i < imagePaths.size(); ++i) {
            imagePaths[i] = dir.absoluteFilePath(imagePaths[i]);
        }
        std::sort(imagePaths.begin(), imagePaths.end());

        currentIndex = 0;
        updateDisplay();
    }

    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        // 计算缩放比例
        qreal targetHeight = 100.0;
        qreal scaleFactor = targetHeight / pixmap.height();
        qreal targetWidth = pixmap.width() * scaleFactor;

        // 缩放图片并保持宽高比
        QPixmap scaledPixmap = pixmap.scaledToWidth(targetWidth, Qt::SmoothTransformation);

        // 更新场景内容
        imageItem->setPixmap(scaledPixmap);
        imageItem->setPos(0, 0);
        rulerItem->setPos(0, scaledPixmap.height());

        // 设置场景大小
        scene->setSceneRect(0, 0, targetWidth, 
                           scaledPixmap.height() + RULER_HEIGHT);
        graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);

        // 更新按钮状态
        prevBtn->setEnabled(currentIndex > 0);
        nextBtn->setEnabled(currentIndex < imagePaths.size() - 1);
    }

private slots:
    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            loadImages(dir);
        }
    }

    void showPrevious() {
        if (currentIndex > 0) {
            --currentIndex;
            updateDisplay();
        }
    }

    void showNext() {
        if (currentIndex < imagePaths.size() - 1) {
            ++currentIndex;
            updateDisplay();
        }
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    ImageViewer viewer;
    viewer.setWindowTitle("Image Flow Viewer");
    viewer.resize(1280, 600);
    viewer.show();

    return app.exec();
}

#include "main.moc"

功能特点:

  1. 图片浏览
    • 支持常见图片格式(jpg/jpeg/png/bmp/gif)
    • 自动按文件名排序
    • 固定窗口宽度1280,自动高度
  2. 刻度标识
    • 显示0-2560范围
    • 每100单位一个刻度线
    • 底部固定位置显示
  3. 滚动支持
    • 水平滚动条自动启用
    • 支持鼠标拖拽滚动
    • 保持图片宽高比缩放
  4. 界面控制
    • 打开文件夹按钮
    • 上一页/下一页导航按钮
    • 智能按钮状态管理

使用说明:

  1. 编译运行程序
  2. 点击"Open Folder"选择包含2560宽度图片的文件夹
  3. 使用"Previous"和"Next"按钮切换图片
  4. 拖动滚动条查看图片不同部分
  5. 刻度始终显示在图片底部,随滚动移动

注意事项:

  • 图片宽度必须为2560像素
  • 图片高度会自动缩放至100像素(保持宽高比)
  • 刻度区域高度固定为50像素
  • 支持任意数量的图片文件(需符合格式要求)

4.添加滚动条 

qt c++ 做一个图片流量功能的窗口,图片从一个文件夹内读取,将文件夹的呢图片文件生成一个列表。窗口有两个按钮是上一页和下一页,用于图片的切换;图片的下面有一个刻度的标识,从0~2560,刻度值显示100的整倍数的刻度值;这个文件夹的图片都是2560宽的,高度是100或者200;窗口的宽度是1280,所以图形的显示区域要有一个滚动条,一个刻度值,等于一个 像素,这个刻度的目的就是为了显示图片的像素位置

以下是实现精确像素位置显示的图片流量查看器完整代码:

#include <QApplication>
#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QDir>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QScrollBar>

class PixelRuler : public QGraphicsItem {
public:
    PixelRuler(qreal width) : m_width(width) {}

    QRectF boundingRect() const override {
        return QRectF(0, 0, m_width, 30);
    }

    void paint(QPainter *painter, 
               const QStyleOptionGraphicsItem *option,
               QWidget *widget) override {
        Q_UNUSED(option);
        Q_UNUSED(widget);

        painter->setRenderHint(QPainter::Antialiasing);
        painter->fillRect(boundingRect(), Qt::white);
        painter->setPen(Qt::black);

        // 绘制主刻度线
        painter->drawLine(0, 15, m_width, 15);

        // 绘制次要刻度线(每10像素)
        for (int i = 0; i <= m_width; i += 10) {
            if (i % 100 == 0) continue; // 主刻度线已绘制
            painter->drawLine(i, 10, i, 15);
        }

        // 绘制主刻度值和标签
        painter->setFont(QFont("Arial", 8));
        for (int i = 0; i <= m_width; i += 100) {
            painter->drawLine(i, 10, i, 20);
            QString text = QString::number(i);
            QRectF textRect(i - 15, 5, 30, 20);
            painter->drawText(textRect, Qt::AlignCenter, text);
        }
    }

private:
    qreal m_width;
};

class ImageViewer : public QWidget {
    Q_OBJECT

public:
    ImageViewer(QWidget *parent = nullptr)
        : QWidget(parent), currentIndex(0) {
        setupUI();
        loadImages(QDir::homePath() + "/Pictures");
    }

private:
    QGraphicsView *graphicsView;
    QGraphicsScene *scene;
    QGraphicsPixmapItem *imageItem;
    PixelRuler *ruler;
    QPushButton *openFolderBtn;
    QPushButton *prevBtn;
    QPushButton *nextBtn;
    QStringList imagePaths;
    int currentIndex;
    const int VIEW_WIDTH = 1280;
    const int RULER_HEIGHT = 30;
    const int IMAGE_WIDTH = 2560;

    void setupUI() {
        // 创建控件
        openFolderBtn = new QPushButton("Open Folder", this);
        prevBtn = new QPushButton("Previous");
        nextBtn = new QPushButton("Next");

        // 按钮布局
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addWidget(prevBtn);
        btnLayout->addWidget(nextBtn);
        btnLayout->addWidget(openFolderBtn);

        // 图形视图设置
        scene = new QGraphicsScene(this);
        graphicsView = new QGraphicsView(scene, this);
        graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
        graphicsView->setDragMode(QGraphicsView::ScrollHandDrag);
        graphicsView->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
        graphicsView->setFixedWidth(VIEW_WIDTH);
        graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

        // 主布局
        QVBoxLayout *mainLayout = new QVBoxLayout(this);
        mainLayout->addWidget(graphicsView);
        mainLayout->addLayout(btnLayout);

        // 连接信号槽
        connect(openFolderBtn, &QPushButton::clicked, this, &ImageViewer::browseFolder);
        connect(prevBtn, &QPushButton::clicked, this, &ImageViewer::showPrevious);
        connect(nextBtn, &QPushButton::clicked, this, &ImageViewer::showNext);
        connect(graphicsView->horizontalScrollBar(), &QScrollBar::valueChanged,
                this, &ImageViewer::handleScroll);

        // 初始场景设置
        imageItem = new QGraphicsPixmapItem;
        scene->addItem(imageItem);
        ruler = new PixelRuler(IMAGE_WIDTH);
        scene->addItem(ruler);
    }

    void loadImages(const QString &directory) {
        QDir dir(directory);
        QStringList filters = {"*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif"};
        imagePaths = dir.entryList(filters, QDir::Files | QDir::Readable);

        // 转换为绝对路径并排序
        for (int i = 0; i < imagePaths.size(); ++i) {
            imagePaths[i] = dir.absoluteFilePath(imagePaths[i]);
        }
        std::sort(imagePaths.begin(), imagePaths.end());

        currentIndex = 0;
        updateDisplay();
    }

    void updateDisplay() {
        if (imagePaths.isEmpty()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        QPixmap pixmap(imagePaths[currentIndex]);
        if (pixmap.isNull()) {
            imageItem->setPixmap(QPixmap());
            return;
        }

        // 计算缩放比例
        qreal scaleFactor = static_cast<qreal>(VIEW_WIDTH) / IMAGE_WIDTH;
        qreal scaledHeight = pixmap.height() * scaleFactor;

        // 缩放图片并保持宽高比
        QPixmap scaledPixmap = pixmap.scaledToWidth(VIEW_WIDTH, Qt::SmoothTransformation);

        // 更新场景内容
        imageItem->setPixmap(scaledPixmap);
        imageItem->setPos(0, 0);
        ruler->setPos(0, scaledHeight);

        // 设置场景大小
        scene->setSceneRect(0, 0, VIEW_WIDTH, 
                           scaledHeight + RULER_HEIGHT);
        graphicsView->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);

        // 更新滚动条位置
        graphicsView->horizontalScrollBar()->setValue(0);

        // 更新按钮状态
        prevBtn->setEnabled(currentIndex > 0);
        nextBtn->setEnabled(currentIndex < imagePaths.size() - 1);
    }

private slots:
    void browseFolder() {
        QString dir = QFileDialog::getExistingDirectory(this, "Select Image Folder");
        if (!dir.isEmpty()) {
            loadImages(dir);
        }
    }

    void showPrevious() {
        if (currentIndex > 0) {
            --currentIndex;
            updateDisplay();
        }
    }

    void showNext() {
        if (currentIndex < imagePaths.size() - 1) {
            ++currentIndex;
            updateDisplay();
        }
    }

    void handleScroll(int value) {
        // 将滚动条值转换为实际像素位置
        qreal pixelPos = static_cast<qreal>(value) / 
                        graphicsView->horizontalScrollBar()->singleStep() * 
                        graphicsView->horizontalScrollBar()->singleStep();
        // 这里可以添加显示当前像素位置的功能
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    ImageViewer viewer;
    viewer.setWindowTitle("Pixel-Precise Image Viewer");
    viewer.resize(1280, 600);
    viewer.show();

    return app.exec();
}

#include "main.moc"

功能增强说明:

  1. 精确像素刻度
    • 主刻度线每100像素一个,带数字标签
    • 次要刻度线每10像素一个
    • 刻度值与实际像素位置完全对应
  2. 滚动同步
    • 水平滚动条始终可见
    • 滚动时保持图片与刻度的同步移动
    • 支持鼠标拖拽滚动
  3. 图片缩放
    • 固定窗口宽度1280像素
    • 图片按宽度自动缩放,保持原始宽高比
    • 自动计算并显示实际高度
  4. 用户体验优化
    • 智能导航按钮状态管理
    • 平滑的图片缩放效果
    • 清晰的刻度标识

使用说明:

  1. 编译运行程序
  2. 点击"Open Folder"选择包含2560宽度图片的文件夹
  3. 使用"Previous"和"Next"按钮切换图片
  4. 拖动滚动条查看图片不同部分
  5. 观察底部刻度与图片像素位置的对应关系

注意事项:

  • 图片宽度必须为2560像素
  • 图片高度会自动缩放至窗口宽度比例(保持宽高比)
  • 刻度区域高度固定为30像素
  • 滚动条位置直接对应图片像素位置(需根据缩放比例计算)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值