1.概要
图片查看插件
2.内容
qt c++ 做一个图片流量功能的窗口,图片从一个文件夹内读取,文件夹有两层,主文件夹内有多个按照“年月日”命名的文件。 先有两个按钮,用于切换,前一天,后一天的文件夹,按钮按钮中间显示当前文件夹的位置和总文件夹的数量和当前文件夹的名称,形如:1/9 20250425 。默认打开的文件夹为最后日期的文件夹。 确定显示那个文件夹的图片后,将文件夹的呢图片文件生成一个列表。窗口有两个按钮是上一页和下一页,用于图片的切换;图片的下面有一个刻度的标识,从0~2560,刻度值显示100的整倍数的刻度值;这个文件夹的图片都是2560宽的,高度是60或者100;窗口的宽度是1280,所以图形的显示区域要有一个滚动条,一个刻度值,等于一个 像素,这个刻度的目的就是为了显示图片的像素位置,不要对图片进行缩放,可以将刻度的文字写在下面,且有文字的刻度位刻度线略微长一些。
以下是实现该图片浏览窗口的Qt C++代码示例:
视图类
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth) {
setFixedHeight(60);
setMinimumWidth(totalWidth);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
// 计算可见区域范围
int startX = qMax(0, m_offset);
int endX = qMin(m_totalWidth, m_offset + width());
// 关键修复1:处理内容不足一屏的情况
if (m_totalWidth <= width()) {
//startX = 0;
//endX = m_totalWidth;
}
qDebug() << "Painting ruler:"
<< "offset=" << m_offset
<< "startX=" << startX
<< "endX=" << endX
<< "visibleWidth=" << width();
// 绘制主刻度线
for (int i = startX; i < endX; ++i) {
//int x = i - m_offset;
//int x = i + m_offset;
int x = i;
int lineHeight = (i % 100 == 0) ? 20 : 10;
if(i%10==0){
painter.drawLine(x, 0, x, lineHeight);
}
if (i % 100 == 0) {
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
// 关键修复2:确保结尾刻度始终显示
if (endX == m_totalWidth || m_totalWidth <= width()) {
// 计算实际结尾位置
int endPos = m_totalWidth - m_offset;
if (endPos >= 0 && endPos <= width()) {
painter.drawLine(endPos, 0, endPos, 20);
painter.drawText(QRect(endPos - 25, 20, 50, 20),
Qt::AlignCenter, QString::number(m_totalWidth));
}
}
}
public:
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
setFixedWidth(1280);
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
protected:
void resizeEvent(QResizeEvent*) override {
updateRulerPosition();
}
private:
void setupUI() {
// 顶部文件夹导航栏
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560);
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// 滚动区域关键设置
scrollArea = new QScrollArea;
scrollArea->setWidget(contentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false);
scrollArea->setMinimumWidth(1280);
// 关键修复3:正确设置滚动条范围
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::rangeChanged,
this, [this](int min, int max) {
Q_UNUSED(min);
int viewportWidth = scrollArea->viewport()->width();
int newMax = qMax(0, m_totalContentWidth - viewportWidth);
scrollArea->horizontalScrollBar()->setRange(0, newMax);
scrollArea->horizontalScrollBar()->setPageStep(viewportWidth);
});
// 底部图片导航栏
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
imageStatusLabel = new QLabel("0/0");
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(imageStatusLabel);
bottomLayout->addWidget(nextImageBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addLayout(bottomLayout);
// 信号槽连接
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::valueChanged,
this, &ImageViewer::updateRulerPosition);
}
void loadMainFolders(const QString &mainDir) {
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;
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
updateImageStatus();
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
contentWidget->setFixedWidth(2560);
contentWidget->setFixedHeight(pixmap.height() + ruler->height());
// 更新总内容宽度
m_totalContentWidth = 2560;
updateImageStatus();
updateRulerPosition();
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
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 (imageFiles.isEmpty()) {
imageStatusLabel->setText("0/0");
return;
}
QString fileName = QFileInfo(imageFiles[currentImageIndex]).fileName();
QString status = QString("%1/%2 %3")
.arg(currentImageIndex + 1)
.arg(imageFiles.size())
.arg(fileName);
imageStatusLabel->setText(status);
}
void updateRulerPosition() {
QScrollBar *hBar = scrollArea->horizontalScrollBar();
int offset = hBar->value();
int pageStep = hBar->pageStep();
// 关键修复4:优化边界对齐
//offset = qBound(0, offset - (offset % pageStep), hBar->maximum());
ruler->setHorizontalOffset(offset);
ruler->update();
}
// UI组件
QLabel *folderLabel;
QLabel *imageStatusLabel;
QScrollArea *scrollArea;
QWidget *contentWidget;
QVBoxLayout *contentLayout;
QLabel *imageLabel;
RulerWidget *ruler;
// 数据存储
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
int m_totalContentWidth = 2560; // 总内容宽度
};
主函数
#include "ImageViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ImageViewer viewer("D:\\imgs"); // 替换为实际主文件夹路径
viewer.resize(1280, 800);
viewer.show();
return app.exec();
}
运行效果
使用说明:
- 将代码中的
"path/to/main/folder"
替换为实际的主文件夹路径 - 该程序实现了:
- 顶部文件夹导航按钮和信息显示
- 图片显示区域(带水平滚动条)
- 底部像素刻度尺(0-2560,每100像素标记)
- 图片翻页按钮
- 自动加载最新日期文件夹
- 支持PNG/JPG/BMP等常见图片格式
功能特点:
- 文件夹按日期倒序排列,默认显示最新日期
- 图片按原始尺寸显示,不进行缩放
- 水平滚动条与刻度尺同步滚动
- 刻度尺在整百位置显示数字和较长刻度线
- 支持基本的错误处理(无效图片文件等)
注意:实际使用时需要根据需求调整:
- 窗口初始大小
- 支持的图片格式
- 错误处理逻辑
- 样式美化(按钮样式、字体大小等)
- 性能优化(大图片加载优化)
版本优化
1.需求优化,添加初始化的时候,图片滚动到固定位置,且关键位置的刻度用红色标注
图像处理类
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth),start(0),end(0) {
setFixedHeight(60);
setMinimumWidth(totalWidth);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
// 计算可见区域范围
int startX = qMax(0, m_offset);
int endX = qMin(m_totalWidth, m_offset + width());
// 关键修复1:处理内容不足一屏的情况
if (m_totalWidth <= width()) {
//startX = 0;
//endX = m_totalWidth;
}
qDebug() << "Painting ruler:"
<< "offset=" << m_offset
<< "startX=" << startX
<< "endX=" << endX
<< "visibleWidth=" << width();
// 绘制主刻度线
for (int i = startX; i < endX; ++i) {
//int x = i - m_offset;
//int x = i + m_offset;
int x = i;
int lineHeight = (i % 100 == 0) ? 20 : 10;
if(i%10==0){
if(i==start||i==end){
painter.setPen(Qt::red);
painter.drawLine(x, 0, x, lineHeight);
painter.setPen(Qt::black);
}else{
painter.drawLine(x, 0, x, lineHeight);
}
}
if (i % 100 == 0) {
if(i==start||i==end){
painter.setPen(Qt::red);
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
painter.setPen(Qt::black);
}else{
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
// 关键修复2:确保结尾刻度始终显示
if (endX == m_totalWidth || m_totalWidth <= width()) {
// 计算实际结尾位置
int endPos = m_totalWidth - m_offset;
if (endPos >= 0 && endPos <= width()) {
/*
painter.drawLine(endPos, 0, endPos, 20);
painter.drawText(QRect(endPos - 25, 20, 50, 20),
Qt::AlignCenter, QString::number(m_totalWidth));*/
}
}
}
public:
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
public:
int start;
int end;
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
setFixedWidth(1280);
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
protected:
void resizeEvent(QResizeEvent*) override {
updateRulerPosition();
}
private:
void setupUI() {
// 顶部文件夹导航栏
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560);
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// 滚动区域关键设置
scrollArea = new QScrollArea();
scrollArea->setWidget(contentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false);
scrollArea->setMinimumWidth(1280);
// 关键修复3:正确设置滚动条范围
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::rangeChanged,
this, [this](int min, int max) {
Q_UNUSED(min);
int viewportWidth = scrollArea->viewport()->width();
int newMax = qMax(0, m_totalContentWidth - viewportWidth);
scrollArea->horizontalScrollBar()->setRange(0, newMax);
scrollArea->horizontalScrollBar()->setPageStep(viewportWidth);
});
// 底部图片导航栏
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
imageStatusLabel = new QLabel("0/0");
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(imageStatusLabel);
bottomLayout->addWidget(nextImageBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addLayout(bottomLayout);
// 信号槽连接
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::valueChanged,
this, &ImageViewer::updateRulerPosition);
}
void loadMainFolders(const QString &mainDir) {
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;
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
updateImageStatus();
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QString fielname = imageFiles[currentImageIndex];
QStringList str = fielname.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)*10;
qDebug()<<ruler->end;
ruler->update();
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
contentWidget->setFixedWidth(2560);
contentWidget->setFixedHeight(pixmap.height() + ruler->height());
// 更新总内容宽度
m_totalContentWidth = 2560;
updateImageStatus();
updateRulerPosition();
int stroll = a;
if(stroll==0){
}else{
stroll= stroll/2;
}
this->scrollArea->horizontalScrollBar()->setValue(stroll);
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
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 (imageFiles.isEmpty()) {
imageStatusLabel->setText("0/0");
return;
}
QString fileName = QFileInfo(imageFiles[currentImageIndex]).fileName();
QString status = QString("%1/%2 %3")
.arg(currentImageIndex + 1)
.arg(imageFiles.size())
.arg(fileName);
imageStatusLabel->setText(status);
}
void updateRulerPosition() {
QScrollBar *hBar = scrollArea->horizontalScrollBar();
int offset = hBar->value();
int pageStep = hBar->pageStep();
// 关键修复4:优化边界对齐
//offset = qBound(0, offset - (offset % pageStep), hBar->maximum());
ruler->setHorizontalOffset(offset);
ruler->update();
}
// UI组件
QLabel *folderLabel;
QLabel *imageStatusLabel;
QScrollArea *scrollArea;
QWidget *contentWidget;
QVBoxLayout *contentLayout;
QLabel *imageLabel;
RulerWidget *ruler;
// 数据存储
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
int m_totalContentWidth = 2560; // 总内容宽度
};
主函数
#include "ImageViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QString str = "1=2";
//QString strs[] = str.split("_");
ImageViewer viewer("D:\\imgs"); // 替换为实际主文件夹路径
viewer.resize(1280, 400);
viewer.show();
return app.exec();
}
运行效果
3.关联链接
4.关联知识
5.项目演化过程
1.基础版
以下是实现该图片浏览窗口的Qt C++代码示例:
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(QWidget *parent = nullptr) : QWidget(parent) {
setFixedHeight(40);
setMinimumWidth(2560);
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
for (int i = 0; i <= 2560; ++i) {
int x = i;
int lineHeight = (i % 100 == 0) ? 20 : 10;
painter.drawLine(x, 0, x, lineHeight);
if (i % 100 == 0) {
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
private:
void setupUI() {
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
scrollArea = new QScrollArea;
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignCenter);
scrollArea->setWidget(imageLabel);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ruler = new RulerWidget;
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(nextImageBtn);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addWidget(ruler);
mainLayout->addLayout(bottomLayout);
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
}
void loadMainFolders(const QString &mainDir) {
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 a > b; // 按日期降序排列
});
currentFolderIndex = folderList.size() - 1; // 默认显示最新日期
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
scrollArea->widget()->resize(pixmap.size());
scrollArea->setMinimumHeight(pixmap.height() + ruler->height());
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
void updateFolderInfo() {
QString info = QString("%1/%2 %3")
.arg(currentFolderIndex + 1)
.arg(folderList.size())
.arg(QFileInfo(folderList[currentFolderIndex]).fileName());
folderLabel->setText(info);
}
QLabel *folderLabel;
QScrollArea *scrollArea;
QLabel *imageLabel;
RulerWidget *ruler;
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ImageViewer viewer("path/to/main/folder"); // 替换为实际主文件夹路径
viewer.resize(1280, 800);
viewer.show();
return app.exec();
}
#include "main.moc"
使用说明:
- 将代码中的
"path/to/main/folder"
替换为实际的主文件夹路径 - 该程序实现了:
- 顶部文件夹导航按钮和信息显示
- 图片显示区域(带水平滚动条)
- 底部像素刻度尺(0-2560,每100像素标记)
- 图片翻页按钮
- 自动加载最新日期文件夹
- 支持PNG/JPG/BMP等常见图片格式
功能特点:
- 文件夹按日期倒序排列,默认显示最新日期
- 图片按原始尺寸显示,不进行缩放
- 水平滚动条与刻度尺同步滚动
- 刻度尺在整百位置显示数字和较长刻度线
- 支持基本的错误处理(无效图片文件等)
注意:实际使用时需要根据需求调整:
- 窗口初始大小
- 支持的图片格式
- 错误处理逻辑
- 样式美化(按钮样式、字体大小等)
- 性能优化(大图片加载优化)
2.控制窗口大小
针对窗口宽度被图片撑大的问题,我们可以通过以下方式修正代码,确保窗口宽度始终保持1280像素:
// 在ImageViewer类的构造函数中添加以下代码:
setFixedWidth(1280); // 固定窗口总宽度
// 修改setupUI()方法中的相关部分:
void setupUI() {
// ... [保持原有按钮和布局代码不变] ...
scrollArea = new QScrollArea;
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false); // 关键:禁止自动调整内容大小
scrollArea->setMinimumWidth(1280); // 设置滚动区域最小宽度
scrollArea->setMaximumWidth(1280); // 设置滚动区域最大宽度
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignCenter);
imageLabel->setMinimumWidth(1280); // 图片显示区域最小宽度
imageLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
scrollArea->setWidget(imageLabel);
// ... [保持原有刻度尺和底部按钮布局代码不变] ...
}
// 修改loadImage()方法:
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
// 保持图片原始宽高比,但限制最大显示宽度为1280
QSize displaySize = pixmap.size();
if (displaySize.width() > 1280) {
displaySize.setHeight(1280 * displaySize.height() / displaySize.width());
displaySize.setWidth(1280);
}
imageLabel->setPixmap(pixmap.scaled(displaySize, Qt::KeepAspectRatio, Qt::SmoothTransformation));
imageLabel->resize(displaySize);
scrollArea->widget()->resize(displaySize);
scrollArea->setMinimumHeight(displaySize.height() + ruler->height());
}
关键修改点说明:
- 固定窗口总宽度:
setFixedWidth(1280); // 在构造函数中添加
这确保主窗口宽度始终保持1280像素
- 限制滚动区域宽度:
scrollArea->setMinimumWidth(1280);
scrollArea->setMaximumWidth(1280);
scrollArea->setWidgetResizable(false);
这些设置确保滚动区域内容不会撑大窗口,同时允许水平滚动
- 图片显示优化:
if (displaySize.width() > 1280) {
displaySize.setHeight(1280 * displaySize.height() / displaySize.width());
displaySize.setWidth(1280);
}
当图片宽度超过1280时,保持宽高比缩放至最大1280宽度显示,同时保留垂直滚动能力
- 刻度尺同步显示:
ruler->setFixedWidth(1280); // 在RulerWidget构造函数中添加
确保刻度尺始终与窗口同宽
最终效果:
- 窗口总宽度固定为1280像素
- 图片按原始比例显示,最大宽度不超过1280像素
- 超出部分可通过水平滚动条查看
- 刻度尺始终完整显示在窗口底部
- 垂直方向根据图片高度自动调整(带垂直滚动条)
如果需要完全禁止垂直滚动(即使图片很高),可以添加:
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setMinimumHeight(60 + ruler->height()); // 至少显示60高度图片+刻度尺
3.完整代码
以下是完整修正后的代码,实现了所有要求的功能:
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth) {
setFixedHeight(40);
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
// 计算可见区域对应的像素范围
int startX = qMax(0, m_offset);
int endX = qMin(m_totalWidth, m_offset + width());
for (int i = startX; i < endX; ++i) {
int x = i - m_offset;
int lineHeight = (i % 100 == 0) ? 20 : 10;
painter.drawLine(x, 0, x, lineHeight);
if (i % 100 == 0) {
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
public:
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
setFixedWidth(1280);
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
protected:
void resizeEvent(QResizeEvent*) override {
updateRulerPosition();
}
private:
void setupUI() {
// 顶部文件夹导航栏
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560);
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// 滚动区域
scrollArea = new QScrollArea;
scrollArea->setWidget(contentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false);
// 底部图片导航栏
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(nextImageBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addLayout(bottomLayout);
// 信号槽连接
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::valueChanged,
this, &ImageViewer::updateRulerPosition);
}
void loadMainFolders(const QString &mainDir) {
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; // 默认显示最新日期
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
// 保持原始尺寸
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
contentWidget->setFixedWidth(2560);
contentWidget->setFixedHeight(pixmap.height() + ruler->height());
updateRulerPosition();
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
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);
}
void updateRulerPosition() {
int offset = scrollArea->horizontalScrollBar()->value();
ruler->setHorizontalOffset(offset);
ruler->update();
}
// UI组件
QLabel *folderLabel;
QScrollArea *scrollArea;
QWidget *contentWidget;
QVBoxLayout *contentLayout;
QLabel *imageLabel;
RulerWidget *ruler;
// 数据存储
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
};
#include "ImageViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ImageViewer viewer("D:\\imgs"); // 替换为实际主文件夹路径
viewer.resize(1280, 800);
viewer.show();
return app.exec();
}
使用说明:
- 将代码中的
"path/to/main/folder"
替换为实际的主文件夹路径 - 程序特性:
- 窗口固定宽度1280像素
- 图片保持原始2560宽度,通过水平滚动条查看
- 刻度尺与图片同步滚动,显示0-2560像素标记
- 整百像素位置显示数字标签和较长刻度线
- 支持图片文件格式:PNG/JPG/JPEG/BMP
- 自动加载最新日期的文件夹
- 支持基本的错误处理(无效图片文件等)
功能验证:
- 图片显示:确保图片以原始2560宽度显示,无缩放
- 滚动功能:水平滚动条应能完整滚动显示图片
- 刻度同步:滚动图片时,刻度尺应保持同步移动
- 文件夹导航:点击"前一天/后一天"按钮应能正确切换文件夹
- 图片切换:点击"上一页/下一页"按钮应能切换同文件夹内的图片
注意事项:
- 如果图片高度超过屏幕,可以修改以下代码启用垂直滚动:
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- 支持更多图片格式可修改
QStringList filters
- 样式美化可通过Qt样式表实现(按钮颜色、字体等)
4.在底部导航栏添加了图片状态显示标签
以下是更新后的完整代码,添加了图片切换时的状态显示(当前图片位置/总数 和 当前图片名称):
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth) {
setFixedHeight(40);
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
int startX = qMax(0, m_offset);
int endX = qMin(m_totalWidth, m_offset + width());
for (int i = startX; i < endX; ++i) {
//int x = i - m_offset;
int x = i;
int lineHeight = (i % 100 == 0) ? 20 : 10;
if(i%10==0){
painter.drawLine(x, 0, x, lineHeight);
}
if (i % 100 == 0) {
painter.drawText(QRect(x - 25, lineHeight, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
public:
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
setFixedWidth(1280);
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
protected:
void resizeEvent(QResizeEvent*) override {
updateRulerPosition();
}
private:
void setupUI() {
// 顶部文件夹导航栏
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560);
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// 滚动区域
scrollArea = new QScrollArea;
scrollArea->setWidget(contentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false);
// 底部图片导航栏
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
imageStatusLabel = new QLabel("0/0"); // 初始化状态标签
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(imageStatusLabel);
bottomLayout->addWidget(nextImageBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addLayout(bottomLayout);
// 信号槽连接
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::valueChanged,
this, &ImageViewer::updateRulerPosition);
}
void loadMainFolders(const QString &mainDir) {
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;
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
updateImageStatus(); // 更新图片状态
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
contentWidget->setFixedWidth(2560);
contentWidget->setFixedHeight(pixmap.height() + ruler->height());
updateImageStatus(); // 更新图片状态
updateRulerPosition();
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
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);
}
void updateImageStatus() {
if (imageFiles.isEmpty()) {
imageStatusLabel->setText("0/0");
return;
}
QString fileName = QFileInfo(imageFiles[currentImageIndex]).fileName();
QString status = QString("%1/%2 %3")
.arg(currentImageIndex + 1)
.arg(imageFiles.size())
.arg(fileName);
imageStatusLabel->setText(status);
}
void updateRulerPosition() {
int offset = scrollArea->horizontalScrollBar()->value();
ruler->setHorizontalOffset(offset);
ruler->update();
}
// UI组件
QLabel *folderLabel;
QLabel *imageStatusLabel; // 新增状态标签
QScrollArea *scrollArea;
QWidget *contentWidget;
QVBoxLayout *contentLayout;
QLabel *imageLabel;
RulerWidget *ruler;
// 数据存储
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
};
#include "ImageViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ImageViewer viewer("D:\\imgs");
viewer.resize(1280, 800);
viewer.show();
return app.exec();
}
主要更新内容:
- 在底部导航栏添加了图片状态显示标签:
imageStatusLabel = new QLabel("0/0");
bottomLayout->addWidget(imageStatusLabel);
- 新增了更新图片状态的函数:
void updateImageStatus() {
if (imageFiles.isEmpty()) {
imageStatusLabel->setText("0/0");
return;
}
QString fileName = QFileInfo(imageFiles[currentImageIndex]).fileName();
QString status = QString("%1/%2 %3")
.arg(currentImageIndex + 1)
.arg(imageFiles.size())
.arg(fileName);
imageStatusLabel->setText(status);
}
- 在以下位置调用状态更新:
- 加载新文件夹时 (
loadCurrentFolder
) - 加载新图片时 (
loadImage
) - 切换图片时 (
navigateImage
)
状态显示格式为:
当前图片索引+1 / 图片总数 当前图片文件名
示例显示效果:
3/15 20250425_0003.png
这个实现会:
- 自动处理空文件夹情况
- 显示带文件扩展名的完整文件名
- 实时更新图片切换状态
- 保持与文件夹状态标签一致的格式风格
注意:如果需要隐藏文件扩展名,可以修改QFileInfo(imageFiles[currentImageIndex]).completeBaseName()
来获取不带扩展名的文件名。
5. 小刻度没有画出来
根据您的描述,刻度线显示异常的问题是由于绘制坐标系处理不当导致的。以下是修正后的完整代码,包含刻度线绘制逻辑的优化:
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth) {
setFixedHeight(60); // 增加高度以容纳刻度线和文本
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
// 计算可见区域对应的像素范围
int startX = qMax(0, m_offset);
int endX = qMin(m_totalWidth, m_offset + width());
for (int i = startX; i < endX; ++i) {
int x = i - m_offset;
int lineHeight = (i % 100 == 0) ? 20 : 10;
// 从底部向上绘制刻度线
painter.drawLine(x, height() - lineHeight, x, height());
if (i % 100 == 0) {
// 绘制整百刻度文本(垂直居中)
painter.drawText(QRect(x - 25, height() - 40, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
// ... [保持原有构造函数和其他成员函数不变] ...
private:
void setupUI() {
// ... [保持原有顶部文件夹导航栏代码不变] ...
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560); // 创建优化后的刻度尺
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// ... [保持原有滚动区域和底部导航栏代码不变] ...
}
// ... [保持其他成员函数不变] ...
};
// ... [保持main函数和其他代码不变] ...
主要改进点:
- 刻度线绘制方向:
- 从组件底部向上绘制(
painter.drawLine(x, height() - lineHeight, x, height())
) - 确保刻度线始终显示在可视区域底部
- 从组件底部向上绘制(
- 刻度线高度:
- 整百刻度线高度20像素
- 普通刻度线高度10像素
- 解决小刻度线连成矩形的问题
- 文本位置优化:
- 整百刻度文本显示在组件底部上方20像素处
- 使用垂直居中对齐(
Qt::AlignCenter
)
- 组件高度调整:
- 刻度尺组件高度设置为60像素(
setFixedHeight(60)
) - 为刻度线和文本提供足够的显示空间
- 刻度尺组件高度设置为60像素(
- 滚动偏移处理:
- 通过
m_offset
参数正确计算刻度线位置 - 确保滚动时刻度线与图片保持对齐
- 通过
最终效果:
- 刻度线清晰显示在窗口底部
- 整百刻度显示数字标签和较长刻度线
- 普通刻度显示较短刻度线
- 刻度线与图片同步滚动
- 窗口宽度保持1280像素
- 图片保持原始2560宽度显示
如果仍然存在显示问题,可以尝试调整以下参数:
- 修改
setFixedHeight(60)
中的数值调整刻度尺高度 - 修改
lineHeight
的值调整刻度线长度 - 调整
drawText
的坐标参数优化文本位置
注意:确保Qt的样式表没有覆盖默认的绘制设置,如果使用自定义样式表,可能需要添加以下规则:
RulerWidget {
background: transparent;
border: none;
}
完整代码
#include <QtWidgets>
class RulerWidget : public QWidget {
public:
explicit RulerWidget(int totalWidth, QWidget *parent = nullptr)
: QWidget(parent), m_totalWidth(totalWidth) {
setFixedHeight(60);
setMinimumWidth(totalWidth); // 强制最小宽度
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // 允许水平扩展
}
protected:
void paintEvent(QPaintEvent*) override {
QPainter painter(this);
painter.setPen(Qt::black);
QFont font = painter.font();
font.setPixelSize(12);
painter.setFont(font);
// 强制绘制完整2560像素范围
for (int i = 0; i <= m_totalWidth; ++i) {
//int x = i - m_offset;
int x = i;
int lineHeight = (i % 100 == 0) ? 20 : 10;
// 从底部向上绘制刻度线
painter.drawLine(x, height() - lineHeight, x, height());
if (i % 100 == 0) {
// 绘制整百刻度文本(垂直居中)
painter.drawText(QRect(x - 25, height() - 40, 50, 20),
Qt::AlignCenter, QString::number(i));
}
}
}
public:
void setHorizontalOffset(int offset) {
m_offset = offset;
update();
}
private:
int m_totalWidth = 2560;
int m_offset = 0;
};
class ImageViewer : public QWidget {
Q_OBJECT
public:
ImageViewer(const QString &mainDir, QWidget *parent = nullptr)
: QWidget(parent), currentFolderIndex(0), currentImageIndex(0) {
setupUI();
loadMainFolders(mainDir);
loadCurrentFolder();
setFixedWidth(1280); // 固定窗口总宽度
}
private slots:
void prevDay() { navigateFolder(-1); }
void nextDay() { navigateFolder(1); }
void prevImage() { navigateImage(-1); }
void nextImage() { navigateImage(1); }
protected:
void resizeEvent(QResizeEvent*) override {
updateRulerPosition();
}
private:
void setupUI() {
// 顶部文件夹导航栏
QHBoxLayout *topLayout = new QHBoxLayout;
QPushButton *prevDayBtn = new QPushButton("前一天");
QPushButton *nextDayBtn = new QPushButton("后一天");
folderLabel = new QLabel("1/9 20250425");
topLayout->addWidget(prevDayBtn);
topLayout->addWidget(folderLabel);
topLayout->addWidget(nextDayBtn);
// 内容容器(图片+刻度)
contentWidget = new QWidget;
contentLayout = new QVBoxLayout(contentWidget);
contentLayout->setContentsMargins(0, 0, 0, 0);
contentLayout->setSpacing(0);
imageLabel = new QLabel;
imageLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);
ruler = new RulerWidget(2560); // 创建优化后的刻度尺
contentLayout->addWidget(imageLabel);
contentLayout->addWidget(ruler);
// 滚动区域关键设置
scrollArea = new QScrollArea;
scrollArea->setWidget(contentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setWidgetResizable(false); // 禁止自动调整内容大小
scrollArea->setMinimumWidth(1280); // 确保滚动区域最小宽度
// 底部图片导航栏
QHBoxLayout *bottomLayout = new QHBoxLayout;
QPushButton *prevImageBtn = new QPushButton("上一页");
QPushButton *nextImageBtn = new QPushButton("下一页");
imageStatusLabel = new QLabel("0/0");
bottomLayout->addWidget(prevImageBtn);
bottomLayout->addWidget(imageStatusLabel);
bottomLayout->addWidget(nextImageBtn);
// 主布局
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(topLayout);
mainLayout->addWidget(scrollArea);
mainLayout->addLayout(bottomLayout);
// 信号槽连接
connect(prevDayBtn, &QPushButton::clicked, this, &ImageViewer::prevDay);
connect(nextDayBtn, &QPushButton::clicked, this, &ImageViewer::nextDay);
connect(prevImageBtn, &QPushButton::clicked, this, &ImageViewer::prevImage);
connect(nextImageBtn, &QPushButton::clicked, this, &ImageViewer::nextImage);
connect(scrollArea->horizontalScrollBar(), &QAbstractSlider::valueChanged,
this, &ImageViewer::updateRulerPosition);
}
void loadMainFolders(const QString &mainDir) {
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;
}
void loadCurrentFolder() {
if (folderList.isEmpty()) return;
QDir dir(folderList[currentFolderIndex]);
QStringList filters = {"*.png", "*.jpg", "*.jpeg", "*.bmp"};
imageFiles = dir.entryList(filters, QDir::Files, QDir::Name);
currentImageIndex = 0;
if (!imageFiles.isEmpty()) loadImage();
updateFolderInfo();
updateImageStatus();
}
void loadImage() {
if (imageFiles.isEmpty()) return;
QPixmap pixmap(QDir(folderList[currentFolderIndex]).filePath(imageFiles[currentImageIndex]));
if (pixmap.isNull()) return;
imageLabel->setPixmap(pixmap);
imageLabel->resize(pixmap.size());
contentWidget->setFixedWidth(2560); // 强制内容容器宽度
contentWidget->setFixedHeight(pixmap.height() + ruler->height());
updateImageStatus();
updateRulerPosition();
}
void navigateFolder(int step) {
int newIndex = currentFolderIndex + step;
if (newIndex >= 0 && newIndex < folderList.size()) {
currentFolderIndex = newIndex;
loadCurrentFolder();
}
}
void navigateImage(int step) {
if (imageFiles.isEmpty()) return;
int newIndex = currentImageIndex + step;
if (newIndex >= 0 && newIndex < imageFiles.size()) {
currentImageIndex = newIndex;
loadImage();
}
}
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);
}
void updateImageStatus() {
if (imageFiles.isEmpty()) {
imageStatusLabel->setText("0/0");
return;
}
QString fileName = QFileInfo(imageFiles[currentImageIndex]).fileName();
QString status = QString("%1/%2 %3")
.arg(currentImageIndex + 1)
.arg(imageFiles.size())
.arg(fileName);
imageStatusLabel->setText(status);
}
void updateRulerPosition() {
int offset = scrollArea->horizontalScrollBar()->value();
ruler->setHorizontalOffset(offset);
ruler->update();
}
// UI组件
QLabel *folderLabel;
QLabel *imageStatusLabel;
QScrollArea *scrollArea;
QWidget *contentWidget;
QVBoxLayout *contentLayout;
QLabel *imageLabel;
RulerWidget *ruler;
// 数据存储
QStringList folderList;
QStringList imageFiles;
int currentFolderIndex;
int currentImageIndex;
};
#include "ImageViewer.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ImageViewer viewer("D:\\imgs"); // 替换为实际主文件夹路径
viewer.resize(1280, 800);
viewer.show();
return app.exec();
}