492 lines
15 KiB
C++
492 lines
15 KiB
C++
#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);
|
||
}
|