refactor: improve image loading and handling in ImageData and ImagesCarousel

This commit is contained in:
2026-01-14 23:52:02 +01:00
parent 9e732bdf2b
commit 1f4d5dcc1d
5 changed files with 74 additions and 31 deletions
+34 -11
View File
@@ -1,34 +1,56 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:32:27 * @Date: 2025-11-30 20:32:27
* @LastEditTime: 2025-11-30 23:07:52 * @LastEditTime: 2026-01-14 23:31:41
* @Description: Image item widget for displaying an image. * @Description: Image item widget for displaying an image.
*/ */
#include "image_item.h" #include "image_item.h"
#include <QImageReader>
#include "logger.h" #include "logger.h"
using namespace GeneralLogger; using namespace GeneralLogger;
ImageData* ImageData::create(const QString& p, const int initWidth, const int initHeight) { ImageData* ImageData::create(const QString& p, const int initWidth, const int initHeight) {
ImageData* data = new ImageData(p); ImageData* data = new ImageData(p);
data->image = new QImage();
if (!data->image.load(p)) { QImageReader reader(p);
if (!reader.canRead()) {
error(QString("Failed to load image from path: %1").arg(p)); error(QString("Failed to load image from path: %1").arg(p));
delete data; delete data;
return nullptr; return nullptr;
} }
const QSize targetSize(initWidth, initHeight);
data->image = data->image.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Crop to center const QSize targetSize(initWidth, initHeight);
int x = (data->image.width() - targetSize.width()) / 2; const QSize originalSize = reader.size();
int y = (data->image.height() - targetSize.height()) / 2;
data->image = data->image.copy(x, y, targetSize.width(), targetSize.height()); if (originalSize.isValid()) {
double widthRatio = (double)targetSize.width() / originalSize.width();
double heightRatio = (double)targetSize.height() / originalSize.height();
double scaleFactor = std::max(widthRatio, heightRatio);
QSize scaledSize = originalSize * scaleFactor;
reader.setScaledSize(scaledSize);
}
if (!reader.read(data->image)) {
error(QString("Failed to load image from path: %1").arg(p));
delete data;
return nullptr;
}
if (data->image->size() != targetSize) {
int x = (data->image->width() - targetSize.width()) / 2;
int y = (data->image->height() - targetSize.height()) / 2;
*data->image = data->image->copy(x, y, targetSize.width(), targetSize.height());
}
return data; return data;
} }
ImageItem::ImageItem(const ImageData* data, ImageItem::ImageItem(ImageData* data,
const int itemWidth, const int itemWidth,
const int itemHeight, const int itemHeight,
const int itemFocusWidth, const int itemFocusWidth,
@@ -40,11 +62,12 @@ ImageItem::ImageItem(const ImageData* data,
m_itemFocusSize(itemFocusWidth, itemFocusHeight) { m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
assert(data != nullptr); assert(data != nullptr);
setScaledContents(true); setScaledContents(true);
if (data->image.isNull()) { if (!data->isValid()) {
setText(":("); setText(":(");
setAlignment(Qt::AlignCenter); setAlignment(Qt::AlignCenter);
} else { } else {
setPixmap(QPixmap::fromImage(data->image)); setPixmap(QPixmap::fromImage(data->getImage()));
data->releaseImage();
} }
setFixedSize(itemWidth, itemHeight); setFixedSize(itemWidth, itemHeight);
} }
+19 -9
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:31:15 * @Date: 2025-11-30 20:31:15
* @LastEditTime: 2025-11-30 23:08:22 * @LastEditTime: 2026-01-14 23:32:58
* @Description: Image item widget for displaying an image. * @Description: Image item widget for displaying an image.
*/ */
#ifndef IMAGE_ITEM_H #ifndef IMAGE_ITEM_H
@@ -17,13 +17,23 @@
* @brief Data structure to hold image information * @brief Data structure to hold image information
* and can be safely created and passed between threads. * and can be safely created and passed between threads.
*/ */
struct ImageData { class ImageData {
QFileInfo file; QFileInfo file;
QImage image; QImage* image;
public: public:
static ImageData* create(const QString& p, const int initWidth, const int initHeight); static ImageData* create(const QString& p, const int initWidth, const int initHeight);
~ImageData() { releaseImage(); }
void releaseImage() { delete image, image = nullptr; }
[[nodiscard]] const QImage& getImage() const { return *image; }
[[nodiscard]] bool isValid() const { return !image->isNull(); }
[[nodiscard]] const QFileInfo& getFileInfo() const { return file; }
private: private:
ImageData(const QString& path) : file(path), image() {} ImageData(const QString& path) : file(path), image() {}
}; };
@@ -38,7 +48,7 @@ class ImageItem : public QLabel {
public: public:
static constexpr int s_animationDuration = 300; static constexpr int s_animationDuration = 300;
explicit ImageItem(const ImageData* data, explicit ImageItem(ImageData* data,
const int itemWidth, const int itemWidth,
const int itemHeight, const int itemHeight,
const int itemFocusWidth, const int itemFocusWidth,
@@ -47,15 +57,15 @@ class ImageItem : public QLabel {
~ImageItem() override; ~ImageItem() override;
[[nodiscard]] QString getFileFullPath() const { return m_data->file.absoluteFilePath(); } [[nodiscard]] QString getFileFullPath() const { return m_data->getFileInfo().absoluteFilePath(); }
[[nodiscard]] QString getFileName() const { return m_data->file.fileName(); } [[nodiscard]] QString getFileName() const { return m_data->getFileInfo().fileName(); }
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); } [[nodiscard]] QDateTime getFileDate() const { return m_data->getFileInfo().lastModified(); }
[[nodiscard]] const QImage& getThumbnail() const { return m_data->image; } [[nodiscard]] const QImage& getThumbnail() const { return m_data->getImage(); }
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); } [[nodiscard]] qint64 getFileSize() const { return m_data->getFileInfo().size(); }
void setFocus(bool focus = true, bool animate = true); void setFocus(bool focus = true, bool animate = true);
+8 -5
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53 * @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-12-01 01:43:00 * @LastEditTime: 2026-01-14 23:50:49
* @Description: Animated carousel widget for displaying and selecting images. * @Description: Animated carousel widget for displaying and selecting images.
*/ */
#include "images_carousel.h" #include "images_carousel.h"
@@ -93,7 +93,10 @@ void ImagesCarousel::_onImagesLoaded() {
// Focus the first image // Focus the first image
if (m_currentIndex < 0) { if (m_currentIndex < 0) {
m_currentIndex = 0; m_currentIndex = 0;
// Ensure the layout events are processed
QTimer::singleShot(0, this, [this]() {
focusCurrImage(); focusCurrImage();
});
} }
} }
@@ -145,7 +148,7 @@ ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel)
setAutoDelete(true); setAutoDelete(true);
} }
void ImagesCarousel::_insertImageQueue(const ImageData* data) { void ImagesCarousel::_insertImageQueue(ImageData* data) {
if (!m_noLoadingScreen) { if (!m_noLoadingScreen) {
_insertImage(data); _insertImage(data);
return; return;
@@ -156,7 +159,7 @@ void ImagesCarousel::_insertImageQueue(const ImageData* data) {
} }
} }
int ImagesCarousel::_insertImage(const ImageData* data) { int ImagesCarousel::_insertImage(ImageData* data) {
// Increase loaded count regardless of success or failure // Increase loaded count regardless of success or failure
Defer defer([this]() { Defer defer([this]() {
emit imageLoaded(getLoadedImagesCount()); emit imageLoaded(getLoadedImagesCount());
@@ -270,7 +273,7 @@ void ImageLoader::run() {
QMetaObject::invokeMethod(m_carousel, QMetaObject::invokeMethod(m_carousel,
"_insertImageQueue", "_insertImageQueue",
Qt::QueuedConnection, Qt::QueuedConnection,
Q_ARG(const ImageData*, data)); Q_ARG(ImageData*, data));
}); });
{ {
QMutexLocker stopSignLocker(&m_carousel->m_stopSignMutex); QMutexLocker stopSignLocker(&m_carousel->m_stopSignMutex);
@@ -340,7 +343,7 @@ void ImagesCarousel::focusCurrImage() {
} }
auto item = getImageItemAt(m_currentIndex); auto item = getImageItemAt(m_currentIndex);
if (!item) { if (!item) {
warn(QString("Failed to get item at index: %1").arg(m_currentIndex)); error(QString("Failed to get item at index: %1").arg(m_currentIndex));
return; return;
} }
item->setFocus(true, m_animationEnabled); item->setFocus(true, m_animationEnabled);
+3 -3
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53 * @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-12-01 01:36:04 * @LastEditTime: 2026-01-14 23:26:32
* @Description: Animated carousel widget for displaying and selecting images. * @Description: Animated carousel widget for displaying and selecting images.
*/ */
#ifndef IMAGES_CAROUSEL_H #ifndef IMAGES_CAROUSEL_H
@@ -121,8 +121,8 @@ class ImagesCarousel : public QWidget {
void appendImages(const QStringList& paths); void appendImages(const QStringList& paths);
private: private:
int _insertImage(const ImageData* item); int _insertImage(ImageData* item);
Q_INVOKABLE void _insertImageQueue(const ImageData* item); Q_INVOKABLE void _insertImageQueue(ImageData* item);
void _enableUIUpdates(bool enable); void _enableUIUpdates(bool enable);
int _focusingLeftOffset(int index); int _focusingLeftOffset(int index);
+7
View File
@@ -1,3 +1,10 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:59:57
* @LastEditTime: 2025-12-07 06:08:18
* @Description: THE utils header that every project needs :)
*/
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H