282 lines
10 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ImageGridWidget.h"
#include "ImageTileWidget.h"
#include <QGridLayout>
#include <QtMath>
#include <QTimer>
#include <QResizeEvent>
#include <QLabel>
ImageGridWidget::ImageGridWidget(QWidget* parent)
: QWidget(parent) {
m_layout = new QGridLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setHorizontalSpacing(4); // 左右保留间距
m_layout->setVerticalSpacing(0); // 上下无间距
setLayout(m_layout);
// 设置控件大小策略为可扩展
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 添加一个默认标签,当没有图像时显示
m_noImageLabel = new QLabel("暂无图像数据", this);
m_noImageLabel->setAlignment(Qt::AlignCenter);
m_noImageLabel->setStyleSheet("color: gray; font-size: 18px;");
m_noImageLabel->hide();
}
void ImageGridWidget::setImages(int index, const QImage& image) {
if (index < 0 || index >= m_tiles.size()) return;
// 直接调用ImageTileWidget的setImage方法
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::initImages(int count) {
m_paths.clear();
m_selectedIndex = -1; // 默认无选中,所有格子等大
m_expandedIndex = -1; // 默认无展开
m_aliasMap.clear(); // 清空别名映射
// 清空现有布局
QLayoutItem* child;
while ((child = m_layout->takeAt(0)) != nullptr) {
if (child->widget()) {
child->widget()->deleteLater();
}
delete child;
}
m_tiles.clear();
// 如果count为0显示提示信息
if (count <= 0) {
m_layout->addWidget(m_noImageLabel, 0, 0, Qt::AlignCenter);
m_noImageLabel->show();
return;
}
m_noImageLabel->hide();
// 初始化指定数量的格子改为竖向布局最多2行
m_rows = qMax(1, qMin(2, count));
m_columns = (count + m_rows - 1) / m_rows;
for (int i = 0; i < count; ++i) {
ImageTileWidget* tile = new ImageTileWidget(this);
// 列优先布局:先填满一列再填下一列
int c = i / m_rows;
int r = i % m_rows;
// 奇数排(行号为偶数)图片靠下显示,偶数排(行号为奇数)图片靠上显示
Qt::Alignment imageAlign = (r % 2 == 0) ? Qt::AlignBottom : Qt::AlignTop;
tile->setImageAlignment(imageAlign);
// 设置固定尺寸
tile->setFixedSize(m_sizeNormal);
tile->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// 控件在网格中居中显示
m_layout->addWidget(tile, r, c, Qt::AlignCenter);
m_tiles.append(tile);
connect(tile, &ImageTileWidget::clicked, this, [this, i]() {
if (m_expandedIndex == i) {
// 如果点击的是已展开的格子,不触发选中,只触发点击事件
emit tileClicked(i);
} else if (m_expandedIndex == -1) {
// 如果没有展开的格子,设置选中并展开
setExpandedIndex(i);
emit tileClicked(i);
}
});
connect(tile, &ImageTileWidget::shrinkRequested, this, [this, i]() {
if (m_expandedIndex == i) {
setExpandedIndex(-1);
}
});
}
updateTileSizes();
}
// 设置别名
void ImageGridWidget::setTileAlias(int index, const QString& alias) {
if (index < 0 || index >= m_tiles.size()) return;
// 设置tile的别名
m_tiles[index]->setAlias(alias);
// 更新别名到索引的映射
m_aliasMap[alias] = index;
}
void ImageGridWidget::setSelectedIndex(int index) {
if (index < -1 || index >= m_tiles.size()) return;
m_selectedIndex = index;
updateTileSizes();
}
void ImageGridWidget::setExpandedIndex(int index) {
if (index < -1 || index >= m_tiles.size()) return;
m_expandedIndex = index;
updateTileSizes();
}
void ImageGridWidget::rebuildGrid() {
// Clear existing
QLayoutItem* child;
while ((child = m_layout->takeAt(0)) != nullptr) {
if (child->widget()) child->widget()->deleteLater();
delete child;
}
m_tiles.clear();
// Determine grid size: up to 2 rows (vertical layout)
int n = m_paths.size();
m_rows = qMax(1, qMin(2, n));
m_columns = (n + m_rows - 1) / m_rows;
// 如果没有路径,显示提示信息
if (n <= 0) {
m_layout->addWidget(m_noImageLabel, 0, 0, Qt::AlignCenter);
m_noImageLabel->show();
return;
}
m_noImageLabel->hide();
for (int i = 0; i < n; ++i) {
ImageTileWidget* tile = new ImageTileWidget(this);
tile->setImagePath(m_paths.at(i));
tile->setSelected(i == m_selectedIndex);
tile->setExpanded(i == m_expandedIndex);
// 列优先布局:先填满一列再填下一列
int c = i / m_rows;
int r = i % m_rows;
// 奇数排(行号为偶数)图片靠下显示,偶数排(行号为奇数)图片靠上显示
Qt::Alignment imageAlign = (r % 2 == 0) ? Qt::AlignBottom : Qt::AlignTop;
tile->setImageAlignment(imageAlign);
// 设置固定尺寸,确保控件大小一致
tile->setFixedSize(m_sizeNormal);
tile->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
// 控件在网格中居中显示
m_layout->addWidget(tile, r, c, Qt::AlignCenter);
m_tiles.append(tile);
connect(tile, &ImageTileWidget::clicked, this, [this, i]() {
if (m_expandedIndex == i) {
// 如果点击的是已展开的格子,不触发选中,只触发点击事件
emit tileClicked(i);
} else if (m_expandedIndex == -1) {
// 如果没有展开的格子,设置选中并展开
setExpandedIndex(i);
emit tileClicked(i);
}
});
connect(tile, &ImageTileWidget::shrinkRequested, this, [this, i]() {
if (m_expandedIndex == i) {
setExpandedIndex(-1);
}
});
}
updateTileSizes();
}
void ImageGridWidget::resizeEvent(QResizeEvent* event) {
QWidget::resizeEvent(event);
// 防止递归调用,只在尺寸真正变化时更新
static QSize lastSize;
if (lastSize != event->size()) {
lastSize = event->size();
updateTileSizes();
}
}
void ImageGridWidget::updateTileSizes() {
// 如果没有瓦片,直接返回
if (m_tiles.isEmpty()) {
return;
}
const bool anyExpanded = (m_expandedIndex >= 0 && m_expandedIndex < m_tiles.size());
const bool anySelected = (m_selectedIndex >= 0 && m_selectedIndex < m_tiles.size());
// 计算可用空间(减去边距和间距)
int availableWidth = width() - m_layout->contentsMargins().left() - m_layout->contentsMargins().right();
int availableHeight = height() - m_layout->contentsMargins().top() - m_layout->contentsMargins().bottom();
// 计算每个格子的基础尺寸(根据窗口大小动态调整)
// 确保减去的间距不会导致负数
int horizontalSpacingTotal = m_layout->horizontalSpacing() * (m_columns - 1);
int verticalSpacingTotal = m_layout->verticalSpacing() * (m_rows - 1);
// 对于竖向布局优先保证高度分配合理因为最多只有2行
int baseTileWidth = qMax(100, (availableWidth - horizontalSpacingTotal) / m_columns);
int baseTileHeight = qMax(100, (availableHeight - verticalSpacingTotal) / m_rows);
// 根据窗口大小动态调整尺寸
m_sizeNormal = QSize(baseTileWidth, baseTileHeight);
m_sizeExpanded = QSize(availableWidth, availableHeight);
for (int i = 0; i < m_tiles.size(); ++i) {
ImageTileWidget* t = m_tiles[i];
bool expanded = anyExpanded && (i == m_expandedIndex);
bool sel = anySelected && (i == m_selectedIndex);
t->setSelected(sel);
t->setExpanded(expanded);
if (expanded) {
// 展开的格子占据整个九宫格空间并支持缩放
t->setMinimumSize(100, 100); // 设置最小尺寸
t->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); // 允许最大尺寸
t->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// 重新布局,让展开的格子占据整个九宫格空间
m_layout->removeWidget(t);
m_layout->addWidget(t, 0, 0, m_rows, m_columns);
t->raise(); // 确保展开的格子在最前面
// 强制更新布局,确保展开的瓦片正确缩放
m_layout->invalidate();
updateGeometry();
} else {
// 恢复正常布局
m_layout->removeWidget(t);
// 列优先布局:先填满一列再填下一列
int c = i / m_rows;
int r = i % m_rows;
// 奇数排(行号为偶数)图片靠下显示,偶数排(行号为奇数)图片靠上显示
Qt::Alignment imageAlign = (r % 2 == 0) ? Qt::AlignBottom : Qt::AlignTop;
t->setImageAlignment(imageAlign);
// 控件在网格中居中显示
m_layout->addWidget(t, r, c, Qt::AlignCenter);
// 设置固定尺寸
t->setFixedSize(m_sizeNormal);
t->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
}
}
// 显示所有格子,让它们都能随窗口放大
for (int i = 0; i < m_tiles.size(); ++i) {
m_tiles[i]->show();
}
}