From 66d619f49e8f06a2d830ce28aaa0f334a70be7dc Mon Sep 17 00:00:00 2001 From: Alfred Reynolds Date: Sat, 21 Aug 2021 15:58:16 +1200 Subject: [PATCH] - Add a "scheduler" key to the config file that lets you define multiple image selectors and time windows they are valid for --- src/appconfig.cpp | 97 +++++++++++++++++++++++++++++++++++++------ src/appconfig.h | 61 ++++++++++++++++----------- src/imageselector.cpp | 52 ++++++++++++++++++++++- src/imageselector.h | 20 +++++++++ src/main.cpp | 65 ++++++++++++++++++++--------- 5 files changed, 237 insertions(+), 58 deletions(-) diff --git a/src/appconfig.cpp b/src/appconfig.cpp index 022e8fc..8a18eae 100644 --- a/src/appconfig.cpp +++ b/src/appconfig.cpp @@ -150,6 +150,67 @@ QString getAppConfigFilePath(const std::string &configPath) { return ""; } +QVector parsePathEntry(QJsonObject &jsonMainDoc, bool baseRecursive, bool baseShuffle, bool baseSorted) +{ + QVector pathEntries; + + if(jsonMainDoc.contains("scheduler") && jsonMainDoc["scheduler"].isArray()) + { + QJsonArray jsonArray = jsonMainDoc["scheduler"].toArray(); + foreach (const QJsonValue & value, jsonArray) + { + PathEntry entry; + entry.recursive = baseRecursive; + entry.sorted = baseSorted; + entry.shuffle = baseShuffle; + + QJsonObject schedulerJson = value.toObject(); + + SetJSONBool(entry.recursive, schedulerJson, "recursive"); + SetJSONBool(entry.shuffle, schedulerJson, "shuffle"); + SetJSONBool(entry.sorted, schedulerJson, "sorted"); + + std::string pathString = ParseJSONString(schedulerJson, "path"); + if(!pathString.empty()) { + entry.path = pathString; + } + std::string imageListString = ParseJSONString(schedulerJson, "imageList"); + if(!imageListString.empty()) { + entry.imageList = imageListString; + } + std::string typeString = ParseJSONString(schedulerJson, "type"); + if(!pathString.empty()) { + entry.type = typeString; + } + SetJSONBool(entry.exclusive, schedulerJson, "exclusive"); + + if(schedulerJson.contains("times") && schedulerJson["times"].isArray()) + { + QJsonArray jsonTimesArray = schedulerJson["times"].toArray(); + foreach (const QJsonValue & timesValue, jsonTimesArray) + { + QJsonObject timesJson = timesValue.toObject(); + if(timesJson.contains("start") || timesJson.contains("end")) + { + DisplayTimeWindow window; + if(timesJson.contains("start")) + { + window.startDisplay = QTime::fromString(timesJson["start"].toString()); + } + if(timesJson.contains("end")) + { + window.endDisplay = QTime::fromString(timesJson["end"].toString()); + } + entry.timeWindows.append(window); + } + } + } + pathEntries.append(entry); + } + } + return pathEntries; +} + AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) { QString jsonFile = getAppConfigFilePath(commandLineConfig.configPath); QDir directory; @@ -169,9 +230,10 @@ AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) { QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); QJsonObject jsonDoc = d.object(); - SetJSONBool(loadedConfig.recursive, jsonDoc, "recursive"); - SetJSONBool(loadedConfig.shuffle, jsonDoc, "shuffle"); - SetJSONBool(loadedConfig.sorted, jsonDoc, "sorted"); + bool baseRecursive, baseShuffle, baseSorted; + SetJSONBool(baseRecursive, jsonDoc, "recursive"); + SetJSONBool(baseShuffle, jsonDoc, "shuffle"); + SetJSONBool(baseSorted, jsonDoc, "sorted"); SetJSONBool(loadedConfig.debugMode, jsonDoc, "debug"); std::string overlayString = ParseJSONString(jsonDoc, "overlay"); @@ -179,17 +241,26 @@ AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) { { loadedConfig.overlay = overlayString; } - std::string pathString = ParseJSONString(jsonDoc, "path"); - if(!pathString.empty()) - { - loadedConfig.path = pathString; - } - std::string imageListString = ParseJSONString(jsonDoc, "imageList"); - if(!imageListString.empty()) - { - loadedConfig.imageList = imageListString; - } + loadedConfig.paths = parsePathEntry(jsonDoc, baseRecursive, baseShuffle, baseSorted); + if(loadedConfig.paths.count() <= 0) + { + PathEntry entry; + entry.recursive = baseRecursive; + entry.sorted = baseSorted; + entry.shuffle = baseShuffle; + std::string pathString = ParseJSONString(jsonDoc, "path"); + if(!pathString.empty()) + { + entry.path = pathString; + } + std::string imageListString = ParseJSONString(jsonDoc, "imageList"); + if(!imageListString.empty()) + { + entry.imageList = imageListString; + } + loadedConfig.paths.append(entry); + } loadedConfig.configPath = commandLineConfig.configPath; return loadedConfig; } diff --git a/src/appconfig.h b/src/appconfig.h index 0b4398f..61fadb3 100644 --- a/src/appconfig.h +++ b/src/appconfig.h @@ -3,6 +3,7 @@ #define APPCONFIG_H #include #include "imagestructs.h" +#include // configuration options that apply to an image/folder of images struct Config { @@ -14,34 +15,44 @@ struct Config { QDateTime loadTime; }; -// app level configuration -struct AppConfig : public Config -{ - AppConfig() {} - AppConfig( const Config &inConfig ) : Config(inConfig) {} - std::string path = ""; - std::string configPath = ""; +struct PathEntry { + QVector timeWindows; + std::string path = ""; + std::string imageList = ""; + std::string type = ""; + bool exclusive = false; - std::string overlay = ""; - std::string imageList = ""; // comma delimited list of images to show - - bool recursive = false; - bool shuffle = false; - bool sorted = false; - bool debugMode = false; - static const std::string valid_aspects; - - public: - bool PathOptionsChanged(AppConfig &other) { - if ( other.recursive != recursive || other.shuffle != shuffle - || other.sorted != sorted) - return true; - if ( other.path != path || other.imageList != imageList ) - return true; - return false; - } + bool recursive = false; + bool shuffle = false; + bool sorted = false; }; +// app level configuration +struct AppConfig : public Config { + AppConfig() {} + AppConfig( const Config &inConfig ) : Config(inConfig) {} + std::string configPath = ""; + std::string overlay = ""; + QVector paths; + + bool debugMode = false; + + static const std::string valid_aspects; + public: + bool PathOptionsChanged(AppConfig &other) { + if (paths.count() != other.paths.count()) + return true; + for(int index = 0; index < paths.count(); ++index) + { + if ( other.paths[index].recursive != paths[index].recursive || other.paths[index].shuffle != paths[index].shuffle + || other.paths[index].sorted != paths[index].sorted) + return true; + if ( other.paths[index].path != paths[index].path || other.paths[index].imageList != paths[index].imageList ) + return true; + } + return false; + } +}; AppConfig loadAppConfiguration(const AppConfig &commandLineConfig); Config getConfigurationForFolder(const std::string &folderPath, const Config ¤tConfig, bool debugMode); diff --git a/src/imageselector.cpp b/src/imageselector.cpp index 22bcd75..839a919 100644 --- a/src/imageselector.cpp +++ b/src/imageselector.cpp @@ -17,6 +17,8 @@ ImageSelector::ImageSelector(std::unique_ptr& pathTraverserIn): { } +ImageSelector::ImageSelector() {} + ImageSelector::~ImageSelector(){} void ImageSelector::setDebugMode(bool debugModeIn) @@ -134,7 +136,7 @@ bool ImageSelector::imageInsideTimeWindow(const QVector &time if(debugMode && timeWindows.count() > 0) { std::cout << "image display time outside windows: " << std::endl; - for(auto timeWindow : timeWindows) + for(auto &timeWindow : timeWindows) { std::cout << "time: " << timeWindow.startDisplay.toString().toStdString() << "-" << timeWindow.endDisplay.toString().toStdString() << std::endl; } @@ -324,3 +326,51 @@ void SortedImageSelector::reloadImagesIfEmpty() } } } + + +ListImageSelector::ListImageSelector() +{ + currentSelector = imageSelectors.begin(); +} + +ListImageSelector::~ListImageSelector() +{ +} + +void ListImageSelector::AddImageSelector(std::unique_ptr& selector, const QVector &displayTimeWindows, const bool exclusiveIn) +{ + SelectoryEntry entry; + entry.selector = std::move(selector); + entry.displayTimeWindows = displayTimeWindows; + entry.exclusive = exclusiveIn; + imageSelectors.push_back(std::move(entry)); + currentSelector = imageSelectors.begin(); +} + + +const ImageDetails ListImageSelector::getNextImage(const ImageDisplayOptions& baseOptions) +{ + // check for exclusive time windows + for(auto& selector: imageSelectors) + { + if (imageInsideTimeWindow(selector.displayTimeWindows) && selector.exclusive) + { + return selector.selector->getNextImage(baseOptions); + } + } + + // fall back to the next in the list + do + { + ++currentSelector; + if(currentSelector == imageSelectors.end()) + { + currentSelector = imageSelectors.begin(); + } + if (imageInsideTimeWindow(currentSelector->displayTimeWindows)) + { + return currentSelector->selector->getNextImage(baseOptions); + } + } + while(true); +} \ No newline at end of file diff --git a/src/imageselector.h b/src/imageselector.h index fa46fd2..eeaccc3 100644 --- a/src/imageselector.h +++ b/src/imageselector.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "imagestructs.h" class MainWindow; @@ -13,6 +14,7 @@ class ImageSelector { public: ImageSelector(std::unique_ptr& pathTraverser); + ImageSelector(); // use case for when you don't own your own traverser virtual ~ImageSelector(); virtual const ImageDetails getNextImage(const ImageDisplayOptions &baseOptions) = 0; void setDebugMode(bool debugModeIn); @@ -61,4 +63,22 @@ private: void reloadImagesIfEmpty(); QStringList images; }; + +class ListImageSelector : public ImageSelector +{ +public: + ListImageSelector(); + virtual ~ListImageSelector(); + virtual const ImageDetails getNextImage(const ImageDisplayOptions &baseOptions); + void AddImageSelector(std::unique_ptr& selector, const QVector &displayTimeWindows, const bool exclusiveIn); + +private: + struct SelectoryEntry { + std::unique_ptr selector; + QVector displayTimeWindows; + bool exclusive = false; + }; + std::vector imageSelectors; + std::vector::iterator currentSelector; +}; #endif // IMAGESELECTOR_H diff --git a/src/main.cpp b/src/main.cpp index 967e3e7..f2c3d4c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,7 +39,9 @@ bool parseCommandLine(AppConfig &appConfig, int argc, char *argv[]) { return false; break; case 'p': - appConfig.path = optarg; + if(appConfig.paths.count() == 0) + appConfig.paths.append(PathEntry()); + appConfig.paths[0].path = optarg; break; case 'a': if (appConfig.valid_aspects.find(optarg[0]) == std::string::npos) @@ -62,14 +64,20 @@ bool parseCommandLine(AppConfig &appConfig, int argc, char *argv[]) { appConfig.backgroundOpacity = atoi(optarg); break; case 'r': - appConfig.recursive = true; + if(appConfig.paths.count() == 0) + appConfig.paths.append(PathEntry()); + appConfig.paths[0].recursive = true; break; case 's': - appConfig.shuffle = true; + if(appConfig.paths.count() == 0) + appConfig.paths.append(PathEntry()); + appConfig.paths[0].shuffle = true; std::cout << "Shuffle mode is on." << std::endl; break; case 'S': - appConfig.sorted = true; + if(appConfig.paths.count() == 0) + appConfig.paths.append(PathEntry()); + appConfig.paths[0].sorted = true; break; case 'O': appConfig.overlay = optarg; @@ -78,7 +86,9 @@ bool parseCommandLine(AppConfig &appConfig, int argc, char *argv[]) { appConfig.debugMode = true; break; case 'i': - appConfig.imageList = optarg; + if(appConfig.paths.count() == 0) + appConfig.paths.append(PathEntry()); + appConfig.paths[0].imageList = optarg; break; case 'c': appConfig.configPath = optarg; @@ -118,28 +128,28 @@ void ConfigureWindowFromSettings(MainWindow &w, const AppConfig &appConfig) w.setBaseOptions(appConfig.baseDisplayOptions); } -std::unique_ptr GetSelectorForConfig(const AppConfig&appConfig) +std::unique_ptr GetSelectorForConfig(const PathEntry& path, const bool debugMode) { std::unique_ptr pathTraverser; - if (!appConfig.imageList.empty()) + if (!path.imageList.empty()) { - pathTraverser = std::unique_ptr(new ImageListPathTraverser(appConfig.imageList, appConfig.debugMode)); + pathTraverser = std::unique_ptr(new ImageListPathTraverser(path.imageList, debugMode)); } - else if (appConfig.recursive) + else if (path.recursive) { - pathTraverser = std::unique_ptr(new RecursivePathTraverser(appConfig.path, appConfig.debugMode)); + pathTraverser = std::unique_ptr(new RecursivePathTraverser(path.path, debugMode)); } else { - pathTraverser = std::unique_ptr(new DefaultPathTraverser(appConfig.path, appConfig.debugMode)); + pathTraverser = std::unique_ptr(new DefaultPathTraverser(path.path, debugMode)); } std::unique_ptr selector; - if (appConfig.sorted) + if (path.sorted) { selector = std::unique_ptr(new SortedImageSelector(pathTraverser)); } - else if (appConfig.shuffle) + else if (path.shuffle) { selector = std::unique_ptr(new ShuffleImageSelector(pathTraverser)); } @@ -151,6 +161,26 @@ std::unique_ptr GetSelectorForConfig(const AppConfig&appConfig) return selector; } +std::unique_ptr GetSelectorForApp(const AppConfig& appConfig) +{ + if(appConfig.paths.count()==1) + { + return GetSelectorForConfig(appConfig.paths[0], appConfig.debugMode); + } + else + { + std::unique_ptr listSelector(new ListImageSelector()); + for(const auto &path : appConfig.paths) + { + auto selector = GetSelectorForConfig(path, appConfig.debugMode); + listSelector->AddImageSelector(selector,path.timeWindows, path.exclusive); + } + // new things + return listSelector; + } +} + + void ReloadConfigIfNeeded(AppConfig &appConfig, MainWindow &w, ImageSwitcher *switcher, ImageSelector *selector) { QString jsonFile = getAppConfigFilePath(appConfig.configPath); @@ -162,16 +192,13 @@ void ReloadConfigIfNeeded(AppConfig &appConfig, MainWindow &w, ImageSwitcher *sw if(appConfig.loadTime < QFileInfo(jsonFile).lastModified()) { - const std::string oldPath = appConfig.path; - const std::string oldImageList = appConfig.imageList; - AppConfig oldConfig = appConfig; appConfig = loadAppConfiguration(appConfig); ConfigureWindowFromSettings(w, appConfig); if(appConfig.PathOptionsChanged(oldConfig)) { - std::unique_ptr selector = GetSelectorForConfig(appConfig); + std::unique_ptr selector = GetSelectorForApp(appConfig); switcher->setImageSelector(selector); } @@ -193,7 +220,7 @@ int main(int argc, char *argv[]) AppConfig appConfig = loadAppConfiguration(commandLineAppConfig); - if (appConfig.path.empty() && appConfig.imageList.empty()) + if (appConfig.paths.empty()) { std::cout << "Error: Path expected." << std::endl; usage(argv[0]); @@ -210,7 +237,7 @@ int main(int argc, char *argv[]) ConfigureWindowFromSettings(w, appConfig); w.show(); - std::unique_ptr selector = GetSelectorForConfig(appConfig); + std::unique_ptr selector = GetSelectorForApp(appConfig); selector->setDebugMode(appConfig.debugMode); ImageSwitcher switcher(w, appConfig.rotationSeconds * 1000, selector);