#include "ImageGridWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include // 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(m_originalImage.width() * m_scaleFactor); int newHeight = static_cast(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(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(viewSize.width() - 20) / m_originalImage.width(); double scaleY = static_cast(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(hBar->value()) / hBar->maximum() : 0.5; double vRatio = vBar->maximum() > 0 ? static_cast(vBar->value()) / vBar->maximum() : 0.5; m_scaleFactor = newScale; updateImageDisplay(); hBar->setValue(static_cast(hRatio * hBar->maximum())); vBar->setValue(static_cast(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(std::ceil(std::sqrt(count))); m_rows = static_cast(std::ceil(static_cast(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); }