refactor: delete batch loading part

This commit is contained in:
2025-08-07 00:29:07 +02:00
parent 567baeeeab
commit 44f2753ae9
3 changed files with 47 additions and 59 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
/* /*
* @Author: Uyanide pywang0608@foxmail.com * @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:34:52 * @Date: 2025-08-05 01:34:52
* @LastEditTime: 2025-08-06 02:34:10 * @LastEditTime: 2025-08-07 00:09:51
* @Description: Configuration manager. * @Description: Configuration manager.
*/ */
#include "config.h" #include "config.h"
@@ -211,6 +211,7 @@ bool Config::isValidImageFile(const QString &filePath) {
static const QStringList validExtensions = { static const QStringList validExtensions = {
".jpg", ".jpg",
".jpeg", ".jpeg",
".jfif",
".png", ".png",
".bmp", ".bmp",
".gif", ".gif",
+37 -50
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-08-06 22:44:49 * @LastEditTime: 2025-08-07 00:22:29
* @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"
@@ -9,7 +9,9 @@
#include <pthread.h> #include <pthread.h>
#include <qboxlayout.h> #include <qboxlayout.h>
#include <qdebug.h> #include <qdebug.h>
#include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qpixmap.h>
#include <QLabel> #include <QLabel>
#include <QMetaObject> #include <QMetaObject>
@@ -31,7 +33,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
QWidget* parent) QWidget* parent)
: QWidget(parent), : QWidget(parent),
ui(new Ui::ImagesCarousel), ui(new Ui::ImagesCarousel),
m_updateTimer(new QTimer(this)),
m_itemWidth(itemWidth), m_itemWidth(itemWidth),
m_itemHeight(static_cast<int>(itemWidth / itemAspectRatio)), m_itemHeight(static_cast<int>(itemWidth / itemAspectRatio)),
m_itemFocusWidth(itemFocusWidth), m_itemFocusWidth(itemFocusWidth),
@@ -44,23 +45,19 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout()); m_imagesLayout = dynamic_cast<QHBoxLayout*>(ui->scrollAreaWidgetContents->layout());
// Load initial images // Load initial images
connect(m_updateTimer,
&QTimer::timeout,
this,
&ImagesCarousel::_updateImages);
connect(this, connect(this,
&ImagesCarousel::imagesLoaded, &ImagesCarousel::imagesLoaded,
this, this,
[this]() { [this]() {
_focusCurrImage(); _focusCurrImage();
// exit(0); // for speed test
disconnect(this, &ImagesCarousel::imagesLoaded, this, nullptr); disconnect(this, &ImagesCarousel::imagesLoaded, this, nullptr);
}); });
m_updateTimer->start(100);
// Auto focus when scrolling // Auto focus when scrolling
m_scrollDebounceTimer = new QTimer(this); m_scrollDebounceTimer = new QTimer(this);
m_scrollDebounceTimer->setSingleShot(true); m_scrollDebounceTimer->setSingleShot(true);
m_scrollDebounceTimer->setInterval(200); m_scrollDebounceTimer->setInterval(s_debounceInterval);
connect(m_scrollDebounceTimer, connect(m_scrollDebounceTimer,
&QTimer::timeout, &QTimer::timeout,
this, this,
@@ -81,9 +78,6 @@ ImagesCarousel::ImagesCarousel(const double itemAspectRatio,
ImagesCarousel::~ImagesCarousel() { ImagesCarousel::~ImagesCarousel() {
delete ui; delete ui;
for (auto item : std::as_const(m_imageQueue)) {
delete item;
}
// memory of items in m_loadedImages managed by Qt parent-child system // memory of items in m_loadedImages managed by Qt parent-child system
// ... // ...
if (m_scrollAnimation) { if (m_scrollAnimation) {
@@ -111,20 +105,14 @@ ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel)
setAutoDelete(true); setAutoDelete(true);
} }
void ImagesCarousel::_addImageToQueue(const ImageData* data) { void ImagesCarousel::_insertImage(const ImageData* data) {
QMutexLocker locker(&m_queueMutex); auto item = new ImageItem(
auto imageItem = new ImageItem(
data, data,
m_itemWidth, m_itemWidth,
m_itemHeight, m_itemHeight,
m_itemFocusWidth, m_itemFocusWidth,
m_itemFocusHeight, m_itemFocusHeight,
this); this);
m_imageQueue.enqueue(imageItem);
}
void ImagesCarousel::_updateImages() {
QMutexLocker locker(&m_queueMutex);
static const QVector<std::function<bool(const ImageItem*, const ImageItem*)>> cmpFuncs = { static const QVector<std::function<bool(const ImageItem*, const ImageItem*)>> cmpFuncs = {
[](auto, auto) { [](auto, auto) {
@@ -141,30 +129,22 @@ void ImagesCarousel::_updateImages() {
}, },
}; };
int processCount = 0; // insert into correct position based on sort type and direction
while (!m_imageQueue.isEmpty() && processCount < 5) { // currently O(n^2), but better as O(n * (n + log(n))) with vector and binary search
ImageItem* item = m_imageQueue.dequeue(); qint64 inserPos = m_loadedImages.size();
if (m_sortType != Config::SortType::None) {
// insert into correct position based on sort type and direction for (auto it = m_loadedImages.rbegin();
// currently O(n^2), but better as O(n * (n + log(n))) with vector and binary search it != m_loadedImages.rend() &&
qint64 inserPos = m_loadedImages.size(); cmpFuncs[static_cast<int>(m_sortType)](*it, item) == m_sortReverse;
if (m_sortType != Config::SortType::None) { (*it)->m_index++, ++it, --inserPos);
for (auto it = m_loadedImages.rbegin();
it != m_loadedImages.rend() &&
cmpFuncs[static_cast<int>(m_sortType)](*it, item) == m_sortReverse;
(*it)->m_index++, ++it, --inserPos);
}
item->m_index = inserPos;
connect(item, &ImageItem::clicked, this, [this](int index) {
// if (m_suppressAutoFocus) return;
_unfocusCurrImage();
m_currentIndex = index;
_focusCurrImage();
});
m_loadedImages.insert(inserPos, item);
m_imagesLayout->insertWidget(inserPos, item);
processCount++;
} }
item->m_index = inserPos;
connect(item,
&ImageItem::clicked,
this,
&ImagesCarousel::_onItemClicked);
m_loadedImages.insert(inserPos, item);
m_imagesLayout->insertWidget(inserPos, item);
{ {
QMutexLocker countLocker(&m_imageCountMutex); QMutexLocker countLocker(&m_imageCountMutex);
@@ -177,23 +157,23 @@ void ImagesCarousel::_updateImages() {
void ImageLoader::run() { void ImageLoader::run() {
auto data = new ImageData(m_path, m_initWidth, m_initHeight); auto data = new ImageData(m_path, m_initWidth, m_initHeight);
QMetaObject::invokeMethod(m_carousel, QMetaObject::invokeMethod(m_carousel,
"_addImageToQueue", "_insertImage",
Qt::QueuedConnection, Qt::QueuedConnection,
Q_ARG(const ImageData*, data)); Q_ARG(const ImageData*, data));
} }
ImageData::ImageData(const QString& p, const int initWidth, const int initHeight) : file(p) { ImageData::ImageData(const QString& p, const int initWidth, const int initHeight) : file(p) {
if (!pixmap.load(p)) { if (!image.load(p)) {
warn(QString("Failed to load image from path: %1").arg(p)); warn(QString("Failed to load image from path: %1").arg(p));
} }
// resize in "cover" mode // resize in "cover" mode
const QSize targetSize(initWidth, initHeight); const QSize targetSize(initWidth, initHeight);
pixmap = pixmap.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); image = image.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Crop to center // Crop to center
int x = (pixmap.width() - targetSize.width()) / 2; int x = (image.width() - targetSize.width()) / 2;
int y = (pixmap.height() - targetSize.height()) / 2; int y = (image.height() - targetSize.height()) / 2;
pixmap = pixmap.copy(x, y, targetSize.width(), targetSize.height()); image = image.copy(x, y, targetSize.width(), targetSize.height());
} }
void ImagesCarousel::focusNextImage() { void ImagesCarousel::focusNextImage() {
@@ -242,7 +222,7 @@ void ImagesCarousel::_focusCurrImage() {
} }
m_scrollAnimation = new QPropertyAnimation(hScrollBar, "value"); m_scrollAnimation = new QPropertyAnimation(hScrollBar, "value");
m_scrollAnimation->setDuration(300); m_scrollAnimation->setDuration(s_animationDuration);
m_scrollAnimation->setStartValue(hScrollBar->value()); m_scrollAnimation->setStartValue(hScrollBar->value());
m_scrollAnimation->setEndValue(leftOffset); m_scrollAnimation->setEndValue(leftOffset);
m_scrollAnimation->setEasingCurve(QEasingCurve::OutCubic); m_scrollAnimation->setEasingCurve(QEasingCurve::OutCubic);
@@ -282,6 +262,13 @@ void ImagesCarousel::_onScrollBarValueChanged(int value) {
_focusCurrImage(); _focusCurrImage();
} }
void ImagesCarousel::_onItemClicked(int index) {
// if (m_suppressAutoFocus) return;
_unfocusCurrImage();
m_currentIndex = index;
_focusCurrImage();
}
ImageItem::ImageItem(const ImageData* data, ImageItem::ImageItem(const ImageData* data,
const int itemWidth, const int itemWidth,
const int itemHeight, const int itemHeight,
@@ -293,7 +280,7 @@ ImageItem::ImageItem(const ImageData* data,
m_itemSize(itemWidth, itemHeight), m_itemSize(itemWidth, itemHeight),
m_itemFocusSize(itemFocusWidth, itemFocusHeight) { m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
setScaledContents(true); setScaledContents(true);
setPixmap(data->pixmap); setPixmap(QPixmap::fromImage(data->image));
setFixedSize(itemWidth, itemHeight); setFixedSize(itemWidth, itemHeight);
} }
+8 -8
View File
@@ -1,12 +1,14 @@
/* /*
* @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-08-06 22:40:18 * @LastEditTime: 2025-08-07 00:27:03
* @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
#define IMAGES_CAROUSEL_H #define IMAGES_CAROUSEL_H
#include <qtmetamacros.h>
#include <QFileInfo> #include <QFileInfo>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QKeyEvent> #include <QKeyEvent>
@@ -36,7 +38,7 @@ class ImagesCarouselScrollArea;
*/ */
struct ImageData { struct ImageData {
QFileInfo file; QFileInfo file;
QPixmap pixmap; QImage image;
explicit ImageData(const QString& p, const int initWidth, const int initHeight); explicit ImageData(const QString& p, const int initWidth, const int initHeight);
}; };
@@ -63,7 +65,7 @@ class ImageItem : public QLabel {
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); } [[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); }
[[nodiscard]] const QPixmap& getPixmap() const { return m_data->pixmap; } [[nodiscard]] const QImage& getThumbnail() const { return m_data->image; }
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); } [[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); }
@@ -117,6 +119,7 @@ class ImagesCarousel : public QWidget {
QWidget* parent = nullptr); QWidget* parent = nullptr);
~ImagesCarousel(); ~ImagesCarousel();
static constexpr int s_debounceInterval = 200;
static constexpr int s_animationDuration = 300; static constexpr int s_animationDuration = 300;
[[nodiscard]] QString getCurrentImagePath() const { [[nodiscard]] QString getCurrentImagePath() const {
@@ -140,22 +143,19 @@ class ImagesCarousel : public QWidget {
private slots: private slots:
void _unfocusCurrImage(); void _unfocusCurrImage();
void _onScrollBarValueChanged(int value); void _onScrollBarValueChanged(int value);
void _onItemClicked(int index);
public: public:
void void
appendImages(const QStringList& paths); appendImages(const QStringList& paths);
private: private:
Q_INVOKABLE void _addImageToQueue(const ImageData* data);
void _focusCurrImage(); void _focusCurrImage();
void _updateImages(); Q_INVOKABLE void _insertImage(const ImageData* item);
private: private:
Ui::ImagesCarousel* ui; Ui::ImagesCarousel* ui;
QMutex m_queueMutex;
QQueue<ImageItem*> m_imageQueue;
QList<ImageItem*> m_loadedImages; QList<ImageItem*> m_loadedImages;
QTimer* m_updateTimer;
int m_currentIndex = 0; int m_currentIndex = 0;
QPropertyAnimation* m_scrollAnimation = nullptr; QPropertyAnimation* m_scrollAnimation = nullptr;
QHBoxLayout* m_imagesLayout = nullptr; QHBoxLayout* m_imagesLayout = nullptr;