- Add a "scheduler" key to the config file that lets you define multiple image selectors and time windows they are valid for

This commit is contained in:
Alfred Reynolds
2021-08-21 15:58:16 +12:00
parent de7af88e53
commit 66d619f49e
5 changed files with 237 additions and 58 deletions

View File

@@ -150,6 +150,67 @@ QString getAppConfigFilePath(const std::string &configPath) {
return ""; return "";
} }
QVector<PathEntry> parsePathEntry(QJsonObject &jsonMainDoc, bool baseRecursive, bool baseShuffle, bool baseSorted)
{
QVector<PathEntry> 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) { AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) {
QString jsonFile = getAppConfigFilePath(commandLineConfig.configPath); QString jsonFile = getAppConfigFilePath(commandLineConfig.configPath);
QDir directory; QDir directory;
@@ -169,9 +230,10 @@ AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) {
QJsonDocument d = QJsonDocument::fromJson(val.toUtf8()); QJsonDocument d = QJsonDocument::fromJson(val.toUtf8());
QJsonObject jsonDoc = d.object(); QJsonObject jsonDoc = d.object();
SetJSONBool(loadedConfig.recursive, jsonDoc, "recursive"); bool baseRecursive, baseShuffle, baseSorted;
SetJSONBool(loadedConfig.shuffle, jsonDoc, "shuffle"); SetJSONBool(baseRecursive, jsonDoc, "recursive");
SetJSONBool(loadedConfig.sorted, jsonDoc, "sorted"); SetJSONBool(baseShuffle, jsonDoc, "shuffle");
SetJSONBool(baseSorted, jsonDoc, "sorted");
SetJSONBool(loadedConfig.debugMode, jsonDoc, "debug"); SetJSONBool(loadedConfig.debugMode, jsonDoc, "debug");
std::string overlayString = ParseJSONString(jsonDoc, "overlay"); std::string overlayString = ParseJSONString(jsonDoc, "overlay");
@@ -179,17 +241,26 @@ AppConfig loadAppConfiguration(const AppConfig &commandLineConfig) {
{ {
loadedConfig.overlay = overlayString; 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; loadedConfig.configPath = commandLineConfig.configPath;
return loadedConfig; return loadedConfig;
} }

View File

@@ -3,6 +3,7 @@
#define APPCONFIG_H #define APPCONFIG_H
#include <QDateTime> #include <QDateTime>
#include "imagestructs.h" #include "imagestructs.h"
#include <QVector>
// configuration options that apply to an image/folder of images // configuration options that apply to an image/folder of images
struct Config { struct Config {
@@ -14,34 +15,44 @@ struct Config {
QDateTime loadTime; QDateTime loadTime;
}; };
// app level configuration struct PathEntry {
struct AppConfig : public Config QVector<DisplayTimeWindow> timeWindows;
{ std::string path = "";
AppConfig() {} std::string imageList = "";
AppConfig( const Config &inConfig ) : Config(inConfig) {} std::string type = "";
std::string path = ""; bool exclusive = false;
std::string configPath = "";
std::string overlay = ""; bool recursive = false;
std::string imageList = ""; // comma delimited list of images to show bool shuffle = false;
bool sorted = false;
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;
}
}; };
// app level configuration
struct AppConfig : public Config {
AppConfig() {}
AppConfig( const Config &inConfig ) : Config(inConfig) {}
std::string configPath = "";
std::string overlay = "";
QVector<PathEntry> 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); AppConfig loadAppConfiguration(const AppConfig &commandLineConfig);
Config getConfigurationForFolder(const std::string &folderPath, const Config &currentConfig, bool debugMode); Config getConfigurationForFolder(const std::string &folderPath, const Config &currentConfig, bool debugMode);

View File

@@ -17,6 +17,8 @@ ImageSelector::ImageSelector(std::unique_ptr<PathTraverser>& pathTraverserIn):
{ {
} }
ImageSelector::ImageSelector() {}
ImageSelector::~ImageSelector(){} ImageSelector::~ImageSelector(){}
void ImageSelector::setDebugMode(bool debugModeIn) void ImageSelector::setDebugMode(bool debugModeIn)
@@ -134,7 +136,7 @@ bool ImageSelector::imageInsideTimeWindow(const QVector<DisplayTimeWindow> &time
if(debugMode && timeWindows.count() > 0) if(debugMode && timeWindows.count() > 0)
{ {
std::cout << "image display time outside windows: " << std::endl; 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; 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<ImageSelector>& selector, const QVector<DisplayTimeWindow> &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);
}

View File

@@ -4,6 +4,7 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <QStringList> #include <QStringList>
#include <QVector>
#include "imagestructs.h" #include "imagestructs.h"
class MainWindow; class MainWindow;
@@ -13,6 +14,7 @@ class ImageSelector
{ {
public: public:
ImageSelector(std::unique_ptr<PathTraverser>& pathTraverser); ImageSelector(std::unique_ptr<PathTraverser>& pathTraverser);
ImageSelector(); // use case for when you don't own your own traverser
virtual ~ImageSelector(); virtual ~ImageSelector();
virtual const ImageDetails getNextImage(const ImageDisplayOptions &baseOptions) = 0; virtual const ImageDetails getNextImage(const ImageDisplayOptions &baseOptions) = 0;
void setDebugMode(bool debugModeIn); void setDebugMode(bool debugModeIn);
@@ -61,4 +63,22 @@ private:
void reloadImagesIfEmpty(); void reloadImagesIfEmpty();
QStringList images; QStringList images;
}; };
class ListImageSelector : public ImageSelector
{
public:
ListImageSelector();
virtual ~ListImageSelector();
virtual const ImageDetails getNextImage(const ImageDisplayOptions &baseOptions);
void AddImageSelector(std::unique_ptr<ImageSelector>& selector, const QVector<DisplayTimeWindow> &displayTimeWindows, const bool exclusiveIn);
private:
struct SelectoryEntry {
std::unique_ptr<ImageSelector> selector;
QVector<DisplayTimeWindow> displayTimeWindows;
bool exclusive = false;
};
std::vector<SelectoryEntry> imageSelectors;
std::vector<SelectoryEntry>::iterator currentSelector;
};
#endif // IMAGESELECTOR_H #endif // IMAGESELECTOR_H

View File

@@ -39,7 +39,9 @@ bool parseCommandLine(AppConfig &appConfig, int argc, char *argv[]) {
return false; return false;
break; break;
case 'p': case 'p':
appConfig.path = optarg; if(appConfig.paths.count() == 0)
appConfig.paths.append(PathEntry());
appConfig.paths[0].path = optarg;
break; break;
case 'a': case 'a':
if (appConfig.valid_aspects.find(optarg[0]) == std::string::npos) 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); appConfig.backgroundOpacity = atoi(optarg);
break; break;
case 'r': case 'r':
appConfig.recursive = true; if(appConfig.paths.count() == 0)
appConfig.paths.append(PathEntry());
appConfig.paths[0].recursive = true;
break; break;
case 's': 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; std::cout << "Shuffle mode is on." << std::endl;
break; break;
case 'S': case 'S':
appConfig.sorted = true; if(appConfig.paths.count() == 0)
appConfig.paths.append(PathEntry());
appConfig.paths[0].sorted = true;
break; break;
case 'O': case 'O':
appConfig.overlay = optarg; appConfig.overlay = optarg;
@@ -78,7 +86,9 @@ bool parseCommandLine(AppConfig &appConfig, int argc, char *argv[]) {
appConfig.debugMode = true; appConfig.debugMode = true;
break; break;
case 'i': case 'i':
appConfig.imageList = optarg; if(appConfig.paths.count() == 0)
appConfig.paths.append(PathEntry());
appConfig.paths[0].imageList = optarg;
break; break;
case 'c': case 'c':
appConfig.configPath = optarg; appConfig.configPath = optarg;
@@ -118,28 +128,28 @@ void ConfigureWindowFromSettings(MainWindow &w, const AppConfig &appConfig)
w.setBaseOptions(appConfig.baseDisplayOptions); w.setBaseOptions(appConfig.baseDisplayOptions);
} }
std::unique_ptr<ImageSelector> GetSelectorForConfig(const AppConfig&appConfig) std::unique_ptr<ImageSelector> GetSelectorForConfig(const PathEntry& path, const bool debugMode)
{ {
std::unique_ptr<PathTraverser> pathTraverser; std::unique_ptr<PathTraverser> pathTraverser;
if (!appConfig.imageList.empty()) if (!path.imageList.empty())
{ {
pathTraverser = std::unique_ptr<PathTraverser>(new ImageListPathTraverser(appConfig.imageList, appConfig.debugMode)); pathTraverser = std::unique_ptr<PathTraverser>(new ImageListPathTraverser(path.imageList, debugMode));
} }
else if (appConfig.recursive) else if (path.recursive)
{ {
pathTraverser = std::unique_ptr<PathTraverser>(new RecursivePathTraverser(appConfig.path, appConfig.debugMode)); pathTraverser = std::unique_ptr<PathTraverser>(new RecursivePathTraverser(path.path, debugMode));
} }
else else
{ {
pathTraverser = std::unique_ptr<PathTraverser>(new DefaultPathTraverser(appConfig.path, appConfig.debugMode)); pathTraverser = std::unique_ptr<PathTraverser>(new DefaultPathTraverser(path.path, debugMode));
} }
std::unique_ptr<ImageSelector> selector; std::unique_ptr<ImageSelector> selector;
if (appConfig.sorted) if (path.sorted)
{ {
selector = std::unique_ptr<ImageSelector>(new SortedImageSelector(pathTraverser)); selector = std::unique_ptr<ImageSelector>(new SortedImageSelector(pathTraverser));
} }
else if (appConfig.shuffle) else if (path.shuffle)
{ {
selector = std::unique_ptr<ImageSelector>(new ShuffleImageSelector(pathTraverser)); selector = std::unique_ptr<ImageSelector>(new ShuffleImageSelector(pathTraverser));
} }
@@ -151,6 +161,26 @@ std::unique_ptr<ImageSelector> GetSelectorForConfig(const AppConfig&appConfig)
return selector; return selector;
} }
std::unique_ptr<ImageSelector> GetSelectorForApp(const AppConfig& appConfig)
{
if(appConfig.paths.count()==1)
{
return GetSelectorForConfig(appConfig.paths[0], appConfig.debugMode);
}
else
{
std::unique_ptr<ListImageSelector> 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) void ReloadConfigIfNeeded(AppConfig &appConfig, MainWindow &w, ImageSwitcher *switcher, ImageSelector *selector)
{ {
QString jsonFile = getAppConfigFilePath(appConfig.configPath); QString jsonFile = getAppConfigFilePath(appConfig.configPath);
@@ -162,16 +192,13 @@ void ReloadConfigIfNeeded(AppConfig &appConfig, MainWindow &w, ImageSwitcher *sw
if(appConfig.loadTime < QFileInfo(jsonFile).lastModified()) if(appConfig.loadTime < QFileInfo(jsonFile).lastModified())
{ {
const std::string oldPath = appConfig.path;
const std::string oldImageList = appConfig.imageList;
AppConfig oldConfig = appConfig; AppConfig oldConfig = appConfig;
appConfig = loadAppConfiguration(appConfig); appConfig = loadAppConfiguration(appConfig);
ConfigureWindowFromSettings(w, appConfig); ConfigureWindowFromSettings(w, appConfig);
if(appConfig.PathOptionsChanged(oldConfig)) if(appConfig.PathOptionsChanged(oldConfig))
{ {
std::unique_ptr<ImageSelector> selector = GetSelectorForConfig(appConfig); std::unique_ptr<ImageSelector> selector = GetSelectorForApp(appConfig);
switcher->setImageSelector(selector); switcher->setImageSelector(selector);
} }
@@ -193,7 +220,7 @@ int main(int argc, char *argv[])
AppConfig appConfig = loadAppConfiguration(commandLineAppConfig); AppConfig appConfig = loadAppConfiguration(commandLineAppConfig);
if (appConfig.path.empty() && appConfig.imageList.empty()) if (appConfig.paths.empty())
{ {
std::cout << "Error: Path expected." << std::endl; std::cout << "Error: Path expected." << std::endl;
usage(argv[0]); usage(argv[0]);
@@ -210,7 +237,7 @@ int main(int argc, char *argv[])
ConfigureWindowFromSettings(w, appConfig); ConfigureWindowFromSettings(w, appConfig);
w.show(); w.show();
std::unique_ptr<ImageSelector> selector = GetSelectorForConfig(appConfig); std::unique_ptr<ImageSelector> selector = GetSelectorForApp(appConfig);
selector->setDebugMode(appConfig.debugMode); selector->setDebugMode(appConfig.debugMode);
ImageSwitcher switcher(w, appConfig.rotationSeconds * 1000, selector); ImageSwitcher switcher(w, appConfig.rotationSeconds * 1000, selector);