492 lines
15 KiB
C++
Raw Normal View History

2025-12-27 09:34:02 +08:00
#include "ImageGridWidget.h"
#include <QGridLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QResizeEvent>
#include <QShowEvent>
#include <QStackedWidget>
#include <QScrollBar>
#include <QKeyEvent>
#include <QPushButton>
#include <cmath>
// ImageTileWidget 实现
ImageTileWidget::ImageTileWidget(QWidget* parent)
: QWidget(parent)
{
setMinimumSize(100, 100);
setCursor(Qt::PointingHandCursor);
}
void ImageTileWidget::setImage(const QImage& image)
{
m_image = image;
update();
}
void ImageTileWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制背景
painter.fillRect(rect(), QColor(47, 48, 52));
if (!m_image.isNull()) {
// 缩放图像以适应控件
QImage scaled = m_image.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
int x = (width() - scaled.width()) / 2;
int y = (height() - scaled.height()) / 2;
painter.drawImage(x, y, scaled);
// 在左上角显示设备名称(无背景)
if (!m_alias.isEmpty()) {
QFont font("Arial", 14);
painter.setFont(font);
painter.setPen(Qt::white);
painter.drawText(10, 25, m_alias);
}
} else {
// 显示占位符
QFont font("Arial", 14);
painter.setFont(font);
painter.setPen(Qt::white);
painter.drawText(rect(), Qt::AlignCenter, m_alias.isEmpty() ? "无图像" : m_alias);
}
}
void ImageTileWidget::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
emit clicked();
} else if (event->button() == Qt::RightButton) {
emit rightClicked();
}
}
// ImageZoomWidget 实现
ImageZoomWidget::ImageZoomWidget(QWidget* parent)
: QWidget(parent)
{
setStyleSheet("background-color: rgb(25, 26, 28);");
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(10, 10, 10, 10);
mainLayout->setSpacing(10);
// 顶部标题栏
QHBoxLayout* topLayout = new QHBoxLayout();
m_titleLabel = new QLabel(this);
m_titleLabel->setStyleSheet("QLabel { color: rgb(221, 225, 233); font-size: 16px; font-weight: bold; background: transparent; }");
topLayout->addWidget(m_titleLabel);
topLayout->addStretch();
// 返回按钮
QPushButton* btnBack = new QPushButton(QString::fromUtf8("返回"), this);
btnBack->setFixedSize(80, 30);
btnBack->setStyleSheet(
"QPushButton { background-color: rgb(60, 62, 68); color: rgb(221, 225, 233); "
"border: 1px solid rgb(80, 82, 88); border-radius: 5px; font-size: 14px; }"
"QPushButton:hover { background-color: rgb(80, 82, 88); }"
"QPushButton:pressed { background-color: rgb(50, 52, 58); }"
);
connect(btnBack, &QPushButton::clicked, this, &ImageZoomWidget::exitRequested);
topLayout->addWidget(btnBack);
mainLayout->addLayout(topLayout);
// 滚动区域
m_scrollArea = new QScrollArea(this);
m_scrollArea->setWidgetResizable(false);
m_scrollArea->setAlignment(Qt::AlignCenter);
m_scrollArea->setStyleSheet(
"QScrollArea { background-color: rgb(38, 40, 47); border: none; }"
"QScrollBar:vertical { background-color: rgb(38, 40, 47); width: 12px; }"
"QScrollBar::handle:vertical { background-color: rgb(80, 82, 88); border-radius: 6px; min-height: 20px; }"
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }"
"QScrollBar:horizontal { background-color: rgb(38, 40, 47); height: 12px; }"
"QScrollBar::handle:horizontal { background-color: rgb(80, 82, 88); border-radius: 6px; min-width: 20px; }"
"QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0px; }"
);
m_imageLabel = new QLabel(m_scrollArea);
m_imageLabel->setAlignment(Qt::AlignCenter);
m_imageLabel->setStyleSheet("background-color: rgb(38, 40, 47);");
m_imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
m_imageLabel->setScaledContents(false);
m_scrollArea->setWidget(m_imageLabel);
mainLayout->addWidget(m_scrollArea, 1);
// 底部信息栏
m_infoLabel = new QLabel(this);
m_infoLabel->setStyleSheet("QLabel { color: rgb(180, 180, 180); font-size: 12px; background: transparent; }");
m_infoLabel->setText(QString::fromUtf8("滚轮缩放 | 拖拽平移 | 双击还原 | 点击返回按钮退出"));
mainLayout->addWidget(m_infoLabel);
setFocusPolicy(Qt::StrongFocus);
}
void ImageZoomWidget::setImage(const QImage& image, const QString& title)
{
m_originalImage = image;
m_titleLabel->setText(title);
m_needResetZoom = true;
// 先显示图像showEvent 中会重新计算缩放
updateImageDisplay();
}
void ImageZoomWidget::showEvent(QShowEvent* event)
{
QWidget::showEvent(event);
if (m_needResetZoom && !m_originalImage.isNull()) {
m_needResetZoom = false;
// 延迟一帧执行,确保布局完成
QMetaObject::invokeMethod(this, &ImageZoomWidget::resetZoom, Qt::QueuedConnection);
}
}
void ImageZoomWidget::updateImageDisplay()
{
if (m_originalImage.isNull()) {
m_imageLabel->setText(QString::fromUtf8("无图像"));
return;
}
int newWidth = static_cast<int>(m_originalImage.width() * m_scaleFactor);
int newHeight = static_cast<int>(m_originalImage.height() * m_scaleFactor);
QImage scaledImage = m_originalImage.scaled(newWidth, newHeight,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
m_imageLabel->setPixmap(QPixmap::fromImage(scaledImage));
m_imageLabel->resize(scaledImage.size());
QString info = QString::fromUtf8("缩放: %1% | 尺寸: %2x%3 | 滚轮缩放 | 拖拽平移 | 双击还原")
.arg(static_cast<int>(m_scaleFactor * 100))
.arg(m_originalImage.width())
.arg(m_originalImage.height());
m_infoLabel->setText(info);
}
void ImageZoomWidget::resetZoom()
{
if (m_originalImage.isNull()) return;
QSize viewSize = m_scrollArea->viewport()->size();
double scaleX = static_cast<double>(viewSize.width() - 20) / m_originalImage.width();
double scaleY = static_cast<double>(viewSize.height() - 20) / m_originalImage.height();
m_scaleFactor = qMin(scaleX, scaleY);
m_scaleFactor = qMax(m_scaleFactor, MIN_SCALE);
updateImageDisplay();
m_scrollArea->horizontalScrollBar()->setValue(
(m_imageLabel->width() - m_scrollArea->viewport()->width()) / 2);
m_scrollArea->verticalScrollBar()->setValue(
(m_imageLabel->height() - m_scrollArea->viewport()->height()) / 2);
}
void ImageZoomWidget::wheelEvent(QWheelEvent* event)
{
if (m_originalImage.isNull()) return;
double delta = event->angleDelta().y() > 0 ? SCALE_STEP : -SCALE_STEP;
double newScale = m_scaleFactor + delta;
newScale = qMax(newScale, MIN_SCALE);
newScale = qMin(newScale, MAX_SCALE);
if (qAbs(newScale - m_scaleFactor) > 0.001) {
QScrollBar* hBar = m_scrollArea->horizontalScrollBar();
QScrollBar* vBar = m_scrollArea->verticalScrollBar();
double hRatio = hBar->maximum() > 0 ? static_cast<double>(hBar->value()) / hBar->maximum() : 0.5;
double vRatio = vBar->maximum() > 0 ? static_cast<double>(vBar->value()) / vBar->maximum() : 0.5;
m_scaleFactor = newScale;
updateImageDisplay();
hBar->setValue(static_cast<int>(hRatio * hBar->maximum()));
vBar->setValue(static_cast<int>(vRatio * vBar->maximum()));
}
event->accept();
}
void ImageZoomWidget::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
m_dragging = true;
m_lastPos = event->pos();
setCursor(Qt::ClosedHandCursor);
}
QWidget::mousePressEvent(event);
}
void ImageZoomWidget::mouseMoveEvent(QMouseEvent* event)
{
if (m_dragging) {
QPoint delta = event->pos() - m_lastPos;
m_lastPos = event->pos();
QScrollBar* hBar = m_scrollArea->horizontalScrollBar();
QScrollBar* vBar = m_scrollArea->verticalScrollBar();
hBar->setValue(hBar->value() - delta.x());
vBar->setValue(vBar->value() - delta.y());
}
QWidget::mouseMoveEvent(event);
}
void ImageZoomWidget::mouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
m_dragging = false;
setCursor(Qt::ArrowCursor);
}
QWidget::mouseReleaseEvent(event);
}
void ImageZoomWidget::mouseDoubleClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
resetZoom();
}
QWidget::mouseDoubleClickEvent(event);
}
void ImageZoomWidget::keyPressEvent(QKeyEvent* event)
{
switch (event->key()) {
case Qt::Key_Escape:
emit exitRequested();
break;
case Qt::Key_Plus:
case Qt::Key_Equal:
m_scaleFactor = qMin(m_scaleFactor + SCALE_STEP, MAX_SCALE);
updateImageDisplay();
break;
case Qt::Key_Minus:
m_scaleFactor = qMax(m_scaleFactor - SCALE_STEP, MIN_SCALE);
updateImageDisplay();
break;
case Qt::Key_0:
m_scaleFactor = 1.0;
updateImageDisplay();
break;
case Qt::Key_F:
resetZoom();
break;
default:
QWidget::keyPressEvent(event);
}
}
void ImageZoomWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
if (!m_originalImage.isNull()) {
resetZoom();
}
}
// ImageGridWidget 实现
ImageGridWidget::ImageGridWidget(QWidget* parent)
: QWidget(parent)
{
// 使用 QStackedWidget 切换网格视图和放大视图
m_stackedWidget = new QStackedWidget(this);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->addWidget(m_stackedWidget);
// 网格容器
m_gridContainer = new QWidget(this);
m_layout = new QGridLayout(m_gridContainer);
m_layout->setSpacing(5);
m_layout->setContentsMargins(5, 5, 5, 5);
// 创建无图像提示标签
m_noImageLabel = new QLabel("暂无图像", m_gridContainer);
m_noImageLabel->setAlignment(Qt::AlignCenter);
m_noImageLabel->setStyleSheet("color: rgb(150, 150, 150); font-size: 24px;");
m_noImageLabel->hide();
m_stackedWidget->addWidget(m_gridContainer);
// 放大视图
m_zoomWidget = new ImageZoomWidget(this);
connect(m_zoomWidget, &ImageZoomWidget::exitRequested, this, &ImageGridWidget::showGridView);
m_stackedWidget->addWidget(m_zoomWidget);
// 默认显示网格视图
m_stackedWidget->setCurrentIndex(0);
}
void ImageGridWidget::initImages(int count)
{
// 清除现有的tiles
for (auto tile : m_tiles) {
m_layout->removeWidget(tile);
delete tile;
}
m_tiles.clear();
m_aliasMap.clear();
if (count <= 0) {
m_noImageLabel->show();
m_columns = 0;
m_rows = 0;
return;
}
m_noImageLabel->hide();
// 计算行列数
m_columns = static_cast<int>(std::ceil(std::sqrt(count)));
m_rows = static_cast<int>(std::ceil(static_cast<double>(count) / m_columns));
// 创建新的tiles
for (int i = 0; i < count; ++i) {
auto tile = new ImageTileWidget(m_gridContainer);
m_tiles.append(tile);
int row = i / m_columns;
int col = i % m_columns;
m_layout->addWidget(tile, row, col);
connect(tile, &ImageTileWidget::clicked, [this, i]() {
// 点击时显示放大视图
showZoomView(i);
emit tileClicked(i);
});
connect(tile, &ImageTileWidget::rightClicked, [this, i]() {
// 右键点击时发射信号,携带索引和别名
QString alias = m_tiles[i]->alias();
emit tileRightClicked(i, alias);
});
}
updateTileSizes();
}
void ImageGridWidget::setImages(int index, const QImage& image)
{
if (index >= 0 && index < m_tiles.size()) {
m_tiles[index]->setImage(image);
}
}
void ImageGridWidget::setImages(const QString& alias, const QImage& image)
{
if (m_aliasMap.contains(alias)) {
int index = m_aliasMap[alias];
setImages(index, image);
}
}
void ImageGridWidget::setTileAlias(int index, const QString& alias)
{
if (index >= 0 && index < m_tiles.size()) {
m_tiles[index]->setAlias(alias);
m_aliasMap[alias] = index;
}
}
void ImageGridWidget::setSelectedIndex(int index)
{
m_selectedIndex = index;
}
void ImageGridWidget::setExpandedIndex(int index)
{
m_expandedIndex = index;
updateTileSizes();
}
void ImageGridWidget::rebuildGrid()
{
// 重新构建网格布局
for (int i = 0; i < m_tiles.size(); ++i) {
int row = i / m_columns;
int col = i % m_columns;
m_layout->addWidget(m_tiles[i], row, col);
}
}
void ImageGridWidget::updateTileSizes()
{
if (m_tiles.isEmpty() || m_columns == 0 || m_rows == 0) {
return;
}
// 使用 stackedWidget 的尺寸,因为 gridContainer 在 stackedWidget 内
int containerWidth = m_stackedWidget->width();
int containerHeight = m_stackedWidget->height();
int availableWidth = containerWidth - m_layout->contentsMargins().left() - m_layout->contentsMargins().right()
- (m_columns - 1) * m_layout->spacing();
int availableHeight = containerHeight - m_layout->contentsMargins().top() - m_layout->contentsMargins().bottom()
- (m_rows - 1) * m_layout->spacing();
int tileWidth = availableWidth / m_columns;
int tileHeight = availableHeight / m_rows;
for (auto tile : m_tiles) {
tile->setFixedSize(tileWidth, tileHeight);
}
}
void ImageGridWidget::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
updateTileSizes();
}
QImage ImageGridWidget::getImage(int index) const
{
if (index >= 0 && index < m_tiles.size()) {
return m_tiles[index]->image();
}
return QImage();
}
QString ImageGridWidget::getAlias(int index) const
{
if (index >= 0 && index < m_tiles.size()) {
return m_tiles[index]->alias();
}
return QString();
}
void ImageGridWidget::showZoomView(int index)
{
if (index < 0 || index >= m_tiles.size()) {
return;
}
QImage image = m_tiles[index]->image();
if (image.isNull()) {
return;
}
QString alias = m_tiles[index]->alias();
m_zoomWidget->setImage(image, alias);
m_stackedWidget->setCurrentIndex(1);
m_zoomWidget->setFocus();
}
void ImageGridWidget::showGridView()
{
m_stackedWidget->setCurrentIndex(0);
}