diff --git a/README.md b/README.md index e4f14b5..4e47f19 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,16 @@ qmake ../src/slide.pro make sudo make install ``` + +### macOS + +Prerequisite: brew + +``` +brew install qt5 +brew install libexif +mkdir -p build +cd build +qmake ../src/slide.pro +make +``` diff --git a/src/imageselector.cpp b/src/imageselector.cpp index 5f9261b..12f68a0 100644 --- a/src/imageselector.cpp +++ b/src/imageselector.cpp @@ -1,4 +1,5 @@ #include "imageselector.h" +#include "pathtraverser.h" #include "mainwindow.h" #include #include @@ -7,59 +8,86 @@ #include #include /* srand, rand */ #include /* time */ +#include // std::shuffle +#include // std::default_random_engine -ImageSelector::ImageSelector(std::string path, bool recursive): - path(path), - recursive(recursive) +ImageSelector::ImageSelector(std::unique_ptr& pathTraverser): + pathTraverser(pathTraverser) { - srand (time(NULL)); } -std::string ImageSelector::getNextImage() const +ImageSelector::~ImageSelector(){} + +RandomImageSelector::RandomImageSelector(std::unique_ptr& pathTraverser): + ImageSelector(pathTraverser) { - QDir directory(path.c_str()); - std:: string filename; - try - { - if (recursive) - { - QStringList images = listImagesRecursive(); - unsigned int selectedImage = selectRandom(images); - filename = images.at(selectedImage).toStdString(); - } - else - { - QStringList images = directory.entryList(QStringList() << "*.jpg" << "*.JPG", QDir::Files); - unsigned int selectedImage = selectRandom(images); - filename = directory.filePath(images.at(selectedImage)).toStdString(); - } - } - catch(const std::string& err) - { - std::cerr << "Error: " << err << std::endl; - } - std::cout << "updating image: " << filename << std::endl; - return filename; + srand (time(NULL)); } -unsigned int ImageSelector::selectRandom(const QStringList& images) const +RandomImageSelector::~RandomImageSelector(){} + +std::string RandomImageSelector::getNextImage() { - std::cout << "images: " << images.size() << std::endl; - if (images.size() == 0) - { - throw std::string("No jpg images found in folder " + path); - } - return rand() % images.size(); + std:: string filename; + try + { + QStringList images = pathTraverser->getImages(); + unsigned int selectedImage = selectRandom(images); + filename = pathTraverser->getImagePath(images.at(selectedImage).toStdString()); + } + catch(const std::string& err) + { + std::cerr << "Error: " << err << std::endl; + } + std::cout << "updating image: " << filename << std::endl; + return filename; } - -QStringList ImageSelector::listImagesRecursive() const +unsigned int RandomImageSelector::selectRandom(const QStringList& images) const { - QDirIterator it(QString(path.c_str()), QStringList() << "*.jpg" << "*.JPG", QDir::Files, QDirIterator::Subdirectories); - QStringList files; - while (it.hasNext()) - { - files.append(it.next()); - } - return files; + std::cout << "images: " << images.size() << std::endl; + if (images.size() == 0) + { + throw std::string("No jpg images found in given folder"); + } + return rand() % images.size(); +} + +ShuffleImageSelector::ShuffleImageSelector(std::unique_ptr& pathTraverser): + ImageSelector(pathTraverser), + current_image_shuffle(-1), + images() +{ + srand (time(NULL)); +} + +ShuffleImageSelector::~ShuffleImageSelector() +{ +} + +std::string ShuffleImageSelector::getNextImage() +{ + if (images.size() == 0 || current_image_shuffle >= images.size()) + { + current_image_shuffle = 0; + images = pathTraverser->getImages(); + std::cout << "Shuffling " << images.size() << " images." << std::endl; + std::random_device rd; + std::mt19937 randomizer(rd()); + std::shuffle(images.begin(), images.end(), randomizer); + } + if (images.size() == 0) + { + return ""; + } + std::string filename = pathTraverser->getImagePath(images.at(current_image_shuffle).toStdString()); + if(!QFileInfo::exists(QString(filename.c_str()))) + { + std::cout << "file not found: " << filename << std::endl; + current_image_shuffle = images.size(); + return getNextImage(); + } + std::cout << "updating image: " << filename << std::endl; + current_image_shuffle = current_image_shuffle + 1; + return filename; } diff --git a/src/imageselector.h b/src/imageselector.h index 2a5b2c7..b01d662 100644 --- a/src/imageselector.h +++ b/src/imageselector.h @@ -2,20 +2,44 @@ #define IMAGESELECTOR_H #include +#include #include class MainWindow; +class PathTraverser; + class ImageSelector { public: - ImageSelector(std::string path, bool recursive); - std::string getNextImage() const; + ImageSelector(std::unique_ptr& pathTraverser); + virtual ~ImageSelector(); + virtual std::string getNextImage() = 0; + +protected: + std::unique_ptr& pathTraverser; +}; + +class RandomImageSelector : public ImageSelector +{ +public: + RandomImageSelector(std::unique_ptr& pathTraverser); + virtual ~RandomImageSelector(); + virtual std::string getNextImage(); private: - QStringList listImagesRecursive() const; unsigned int selectRandom(const QStringList& images) const; - std::string path; - bool recursive; +}; + +class ShuffleImageSelector : public ImageSelector +{ +public: + ShuffleImageSelector(std::unique_ptr& pathTraverser); + virtual ~ShuffleImageSelector(); + virtual std::string getNextImage(); + +private: + int current_image_shuffle; + QStringList images; }; #endif // IMAGESELECTOR_H diff --git a/src/imageswitcher.cpp b/src/imageswitcher.cpp index ea9753a..e64979d 100644 --- a/src/imageswitcher.cpp +++ b/src/imageswitcher.cpp @@ -4,12 +4,12 @@ #include #include #include -#include #include +#include #include /* srand, rand */ #include /* time */ -ImageSwitcher::ImageSwitcher(MainWindow& w, unsigned int timeout, const ImageSelector& selector): +ImageSwitcher::ImageSwitcher(MainWindow& w, unsigned int timeout, std::unique_ptr& selector): QObject::QObject(), window(w), timeout(timeout), @@ -20,7 +20,7 @@ ImageSwitcher::ImageSwitcher(MainWindow& w, unsigned int timeout, const ImageSel void ImageSwitcher::updateImage() { - std::string filename(selector.getNextImage()); + std::string filename(selector->getNextImage()); if (filename == "") { window.warn("No image found."); diff --git a/src/imageswitcher.h b/src/imageswitcher.h index 8c6b027..d49d3ad 100644 --- a/src/imageswitcher.h +++ b/src/imageswitcher.h @@ -4,6 +4,7 @@ #include #include #include +#include class MainWindow; class ImageSelector; @@ -11,7 +12,7 @@ class ImageSwitcher : public QObject { Q_OBJECT public: - ImageSwitcher(MainWindow& w, unsigned int timeout, const ImageSelector& selector); + ImageSwitcher(MainWindow& w, unsigned int timeout, std::unique_ptr& selector); void start(); public slots: @@ -19,7 +20,7 @@ public slots: private: MainWindow& window; unsigned int timeout; - const ImageSelector& selector; + std::unique_ptr& selector; QTimer timer; }; diff --git a/src/main.cpp b/src/main.cpp index a922eab..e7c0a7e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include "imageselector.h" #include "imageswitcher.h" +#include "pathtraverser.h" #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include void usage(std::string programName) { std::cerr << "Usage: " << programName << " [-t rotation_seconds] [-o background_opacity(0..255)] [-b blur_radius] -p image_folder -r" << std::endl; @@ -16,46 +18,72 @@ void usage(std::string programName) { int main(int argc, char *argv[]) { - unsigned int rotationSeconds = 30; - std::string path = ""; + unsigned int rotationSeconds = 30; + std::string path = ""; - QApplication a(argc, argv); + QApplication a(argc, argv); - MainWindow w; - int opt; - bool recursive = false; - while ((opt = getopt(argc, argv, "b:p:t:o:r")) != -1) { - switch (opt) { - case 'p': - path = optarg; - break; - case 't': - rotationSeconds = atoi(optarg); - break; - case 'b': - w.setBlurRadius(atoi(optarg)); - break; - case 'o': - w.setBackgroundOpacity(atoi(optarg)); - break; - case 'r': - recursive = true; - break; - default: /* '?' */ - usage(argv[0]); - return 1; - } - } - - if (path.empty()) { - std::cout << "Error: Path expected." << std::endl; + MainWindow w; + int opt; + bool recursive = false; + bool shuffle = false; + while ((opt = getopt(argc, argv, "b:p:t:o:rs")) != -1) { + switch (opt) { + case 'p': + path = optarg; + break; + case 't': + rotationSeconds = atoi(optarg); + break; + case 'b': + w.setBlurRadius(atoi(optarg)); + break; + case 'o': + w.setBackgroundOpacity(atoi(optarg)); + break; + case 'r': + recursive = true; + break; + case 's': + shuffle = true; + std::cout << "Shuffle mode is on." << std::endl; + break; + default: /* '?' */ usage(argv[0]); return 1; } - w.show(); + } - ImageSelector selector(path, recursive); - ImageSwitcher switcher(w, rotationSeconds * 1000, selector); - switcher.start(); - return a.exec(); + if (path.empty()) + { + std::cout << "Error: Path expected." << std::endl; + usage(argv[0]); + return 1; + } + + std::unique_ptr pathTraverser; + if (recursive) + { + pathTraverser = std::unique_ptr(new RecursivePathTraverser(path)); + } + else + { + pathTraverser = std::unique_ptr(new DefaultPathTraverser(path)); + } + + std::unique_ptr selector; + if (shuffle) + { + selector = std::unique_ptr(new ShuffleImageSelector(pathTraverser)); + } + else + { + selector = std::unique_ptr(new RandomImageSelector(pathTraverser)); + } + + w.show(); + + ImageSwitcher switcher(w, rotationSeconds * 1000, selector); + switcher.start(); + return a.exec(); } diff --git a/src/pathtraverser.cpp b/src/pathtraverser.cpp new file mode 100644 index 0000000..5686e25 --- /dev/null +++ b/src/pathtraverser.cpp @@ -0,0 +1,55 @@ +#include "pathtraverser.h" +#include "mainwindow.h" +#include +#include +#include +#include +#include +#include /* srand, rand */ + +PathTraverser::PathTraverser(const std::string path): + path(path) +{} + +PathTraverser::~PathTraverser() {} + +RecursivePathTraverser::RecursivePathTraverser(const std::string path): + PathTraverser(path) +{} + +RecursivePathTraverser::~RecursivePathTraverser() {} + + +QStringList RecursivePathTraverser::getImages() const +{ + QDirIterator it(QString(path.c_str()), QStringList() << "*.jpg" << "*.JPG", QDir::Files, QDirIterator::Subdirectories); + QStringList files; + while (it.hasNext()) + { + files.append(it.next()); + } + return files; +} + +const std::string RecursivePathTraverser::getImagePath(const std::string image) const +{ + return image; +} + +DefaultPathTraverser::DefaultPathTraverser(const std::string path): + PathTraverser(path), + directory(path.c_str()) +{} + +DefaultPathTraverser::~DefaultPathTraverser() {} + + +QStringList DefaultPathTraverser::getImages() const +{ + return directory.entryList(QStringList() << "*.jpg" << "*.JPG", QDir::Files); +} + +const std::string DefaultPathTraverser::getImagePath(const std::string image) const +{ + return directory.filePath(QString(image.c_str())).toStdString(); +} diff --git a/src/pathtraverser.h b/src/pathtraverser.h new file mode 100644 index 0000000..d400ce8 --- /dev/null +++ b/src/pathtraverser.h @@ -0,0 +1,41 @@ +#ifndef PATHTRAVERSER_H +#define PATHTRAVERSER_H + +#include +#include +#include + +class MainWindow; +class PathTraverser +{ + public: + PathTraverser(const std::string path); + virtual ~PathTraverser(); + virtual QStringList getImages() const = 0; + virtual const std::string getImagePath(const std::string image) const = 0; + + protected: + const std::string path; +}; + +class RecursivePathTraverser : public PathTraverser +{ + public: + RecursivePathTraverser(const std::string path); + virtual ~RecursivePathTraverser(); + QStringList getImages() const; + virtual const std::string getImagePath(const std::string image) const; +}; + +class DefaultPathTraverser : public PathTraverser +{ + public: + DefaultPathTraverser(const std::string path); + virtual ~DefaultPathTraverser(); + QStringList getImages() const; + virtual const std::string getImagePath(const std::string image) const; + private: + QDir directory; +}; + +#endif // PATHTRAVERSER_H diff --git a/src/slide.pro b/src/slide.pro index 906ba94..7f8c9ab 100644 --- a/src/slide.pro +++ b/src/slide.pro @@ -22,16 +22,22 @@ DEFINES += QT_DEPRECATED_WARNINGS # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +mac: INCLUDEPATH += $$system(brew --prefix libexif)/include/ +mac: QMAKE_LFLAGS += -L$$system(brew --prefix libexif)/lib + + SOURCES += \ main.cpp \ mainwindow.cpp \ imageswitcher.cpp \ + pathtraverser.cpp \ imageselector.cpp HEADERS += \ mainwindow.h \ imageselector.h \ + pathtraverser.h \ imageswitcher.h FORMS += \