From 9e3b0fed1aa11fa651ea1a27416a26e7e05e2b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= <d_muel20@uni-muenster.de> Date: Wed, 28 Aug 2019 16:05:47 +0200 Subject: [PATCH] * Spawned tasks now offer less user interaction and aren't saved anymore * Tags port moved from ML method to learning block --- Grinder/Grinder.pro | 6 - Grinder/Version.h | 4 +- Grinder/engine/ProcessorBase.impl.h | 14 +- Grinder/engine/data/DataBlob.cpp | 3 + Grinder/ml/MachineLearningConfiguration.cpp | 7 - Grinder/ml/MachineLearningConfiguration.h | 9 +- .../BaristaClassifierTaskSpawner.impl.h | 2 - .../ml/barista/tasks/BaristaInferenceTask.cpp | 6 - .../ml/barista/tasks/BaristaInferenceTask.h | 3 - Grinder/ml/barista/tasks/BaristaTask.h | 4 +- Grinder/ml/barista/tasks/BaristaTask.impl.h | 9 + .../ml/barista/tasks/BaristaTrainingTask.cpp | 6 - .../ml/barista/tasks/BaristaTrainingTask.h | 3 - Grinder/ml/blocks/MachineLearningBlock.cpp | 3 + Grinder/ml/blocks/MachineLearningBlock.h | 3 + .../ml/blocks/MachineLearningMethodBlock.h | 8 +- .../blocks/MachineLearningMethodBlock.impl.h | 18 +- .../MachineLearningMethodProcessor.impl.h | 1 - .../MachineLearningProcessor.impl.h | 16 +- Grinder/ml/tasks/MachineLearningTask.h | 2 + .../cmd/tasks/CommandInterfaceTask.cpp | 2 +- .../cmd/tasks/CommandInterfaceTestTask.cpp | 2 +- Grinder/task/Task.cpp | 3 + Grinder/task/Task.h | 9 +- Grinder/task/TaskCatalog.cpp | 42 +- Grinder/task/TaskCatalog.h | 11 +- Grinder/task/TaskPool.cpp | 2 +- Grinder/task/tasks/GenericTask.cpp | 2 +- .../tasks/BaristaInferenceTaskWidget.cpp | 131 ---- .../tasks/BaristaInferenceTaskWidget.h | 52 -- .../tasks/BaristaInferenceTaskWidget.ui | 298 -------- .../tasks/BaristaTrainingTaskWidget.cpp | 71 -- .../barista/tasks/BaristaTrainingTaskWidget.h | 43 -- .../tasks/BaristaTrainingTaskWidget.ui | 263 ------- Grinder/ui/task/TaskPoolWidget.cpp | 231 +++---- Grinder/ui/task/TaskWidget.cpp | 365 +++++----- Grinder/ui/task/TaskWidget.ui | 640 +++++++++--------- 37 files changed, 727 insertions(+), 1567 deletions(-) delete mode 100644 Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.cpp delete mode 100644 Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.h delete mode 100644 Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.ui delete mode 100644 Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.cpp delete mode 100644 Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.h delete mode 100644 Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.ui diff --git a/Grinder/Grinder.pro b/Grinder/Grinder.pro index 4b70225..1c9170d 100644 --- a/Grinder/Grinder.pro +++ b/Grinder/Grinder.pro @@ -346,9 +346,7 @@ SOURCES += \ ui/task/tasks/GenericTaskWidget.cpp \ ml/barista/BaristaMessage.cpp \ ml/barista/tasks/BaristaTrainingTask.cpp \ - ui/barista/tasks/BaristaTrainingTaskWidget.cpp \ ml/barista/tasks/BaristaInferenceTask.cpp \ - ui/barista/tasks/BaristaInferenceTaskWidget.cpp \ ui/widgets/project/LabelsComboBox.cpp \ ui/widgets/project/ImageReferencesCheckListWidget.cpp \ network/cmd/tasks/CommandInterfaceTask.cpp \ @@ -849,9 +847,7 @@ HEADERS += \ ui/task/tasks/GenericTaskWidget.h \ ml/barista/BaristaMessage.h \ ml/barista/tasks/BaristaTrainingTask.h \ - ui/barista/tasks/BaristaTrainingTaskWidget.h \ ml/barista/tasks/BaristaInferenceTask.h \ - ui/barista/tasks/BaristaInferenceTaskWidget.h \ ui/widgets/project/LabelsComboBox.h \ ui/widgets/project/ImageReferencesCheckListWidget.h \ network/cmd/tasks/CommandInterfaceTask.h \ @@ -1006,8 +1002,6 @@ FORMS += \ ui/dlg/TextViewerDialog.ui \ ui/task/ConfigureTaskDialog.ui \ ui/task/tasks/GenericTaskWidget.ui \ - ui/barista/tasks/BaristaTrainingTaskWidget.ui \ - ui/barista/tasks/BaristaInferenceTaskWidget.ui \ ui/cmd/tasks/CommandInterfaceTaskWidget.ui \ ui/cmd/tasks/CommandInterfaceTestTaskWidget.ui \ ui/dlg/BrowseDialog.ui diff --git a/Grinder/Version.h b/Grinder/Version.h index 12a1b6a..5dc0163 100644 --- a/Grinder/Version.h +++ b/Grinder/Version.h @@ -10,14 +10,14 @@ #define GRNDR_INFO_TITLE "Grinder" #define GRNDR_INFO_COPYRIGHT "Copyright (c) WWU Muenster" -#define GRNDR_INFO_DATE "27.8.2019" +#define GRNDR_INFO_DATE "28.8.2019" #define GRNDR_INFO_COMPANY "WWU Muenster" #define GRNDR_INFO_WEBSITE "http://www.uni-muenster.de" #define GRNDR_VERSION_MAJOR 0 #define GRNDR_VERSION_MINOR 15 #define GRNDR_VERSION_REVISION 0 -#define GRNDR_VERSION_BUILD 380 +#define GRNDR_VERSION_BUILD 382 namespace grndr { diff --git a/Grinder/engine/ProcessorBase.impl.h b/Grinder/engine/ProcessorBase.impl.h index fa7f8ae..d7fc501 100644 --- a/Grinder/engine/ProcessorBase.impl.h +++ b/Grinder/engine/ProcessorBase.impl.h @@ -37,15 +37,13 @@ ValueType ProcessorBase::portData(EngineExecutionContext& ctx, const Port* port, template<typename PropType> PropType* ProcessorBase::portProperty(const Port* port, PropertyID propertyID, const Connection* connection, bool required) const { + PropType* property = nullptr; + if (auto dataPort = resolveDataPort(port, connection, required)) - { - auto property = _block->portProperty<PropType>(dataPort, propertyID); + property = dataPort->block()->portProperty<PropType>(dataPort, propertyID); - if (!property && required) - throwProcessorException(QString{"No property with ID '%2' could be retrieved for port '%1'"}.arg(port->getName()).arg(propertyID)); + if (!property && required) + throwProcessorException(QString{"No property with ID '%2' could be retrieved for port '%1'"}.arg(port->getName()).arg(propertyID)); - return property; - } - else - return nullptr; + return property; } diff --git a/Grinder/engine/data/DataBlob.cpp b/Grinder/engine/data/DataBlob.cpp index 5711a23..1dc41b2 100644 --- a/Grinder/engine/data/DataBlob.cpp +++ b/Grinder/engine/data/DataBlob.cpp @@ -126,6 +126,9 @@ void DataBlob::mergeMetaData(const std::vector<MetaData>& metaData) { for (auto data : metaData) { + if (data.empty()) + continue; + if (_metaData.isEmpty()) { _metaData = data; diff --git a/Grinder/ml/MachineLearningConfiguration.cpp b/Grinder/ml/MachineLearningConfiguration.cpp index 0e0b7c8..8e8585d 100644 --- a/Grinder/ml/MachineLearningConfiguration.cpp +++ b/Grinder/ml/MachineLearningConfiguration.cpp @@ -6,10 +6,3 @@ #include "Grinder.h" #include "MachineLearningConfiguration.h" #include "MachineLearningExceptions.h" - -void MachineLearningConfiguration::verifyConfiguration() const -{ - // Verify general settings - if (!_imageTags) - throw MachineLearningException{_EXCPT("Image tags may not be null")}; -} diff --git a/Grinder/ml/MachineLearningConfiguration.h b/Grinder/ml/MachineLearningConfiguration.h index 89afff1..0bd8b89 100644 --- a/Grinder/ml/MachineLearningConfiguration.h +++ b/Grinder/ml/MachineLearningConfiguration.h @@ -17,11 +17,7 @@ namespace grndr Q_OBJECT public: - virtual void verifyConfiguration() const; - - public: - const ImageTags* imageTags() const { return _imageTags; } - void setImageTags(const ImageTags* imageTags) { setValue(_imageTags, imageTags); } + virtual void verifyConfiguration() const { } protected: template<typename ValueType> @@ -29,9 +25,6 @@ namespace grndr signals: void configurationChanged(); - - private: - const ImageTags* _imageTags{nullptr}; }; } diff --git a/Grinder/ml/barista/BaristaClassifierTaskSpawner.impl.h b/Grinder/ml/barista/BaristaClassifierTaskSpawner.impl.h index fb5a6e1..2295da8 100644 --- a/Grinder/ml/barista/BaristaClassifierTaskSpawner.impl.h +++ b/Grinder/ml/barista/BaristaClassifierTaskSpawner.impl.h @@ -16,6 +16,4 @@ void BaristaClassifierTaskSpawner::configureTask(TaskType* task) const task->setNetwork(_config.network()); task->setOutputDirectory(_config.getOutputDirectory()); task->setRemoteDirectory(_config.getRemoteDirectory()); - - task->setInputImageTags(_config.imageTags()); } diff --git a/Grinder/ml/barista/tasks/BaristaInferenceTask.cpp b/Grinder/ml/barista/tasks/BaristaInferenceTask.cpp index 1174f39..fc1b45b 100644 --- a/Grinder/ml/barista/tasks/BaristaInferenceTask.cpp +++ b/Grinder/ml/barista/tasks/BaristaInferenceTask.cpp @@ -13,7 +13,6 @@ #include "image/draftitems/PixelsDraftItem.h" #include "task/TaskExceptions.h" #include "project/ImageReference.h" -#include "ui/barista/tasks/BaristaInferenceTaskWidget.h" #include "ml/barista/msgs/BaristaInferImageMessage.h" @@ -38,11 +37,6 @@ void BaristaInferenceTask::registerMessageHandlers() registerMessageHandler<BaristaInferImageMessage>(BaristaInferImageMessage::Key, &BaristaInferenceTask::handleInferImageMessage); } -ConfigureTaskWidgetBase* BaristaInferenceTask::createEditor(bool newTask, QWidget* parent) -{ - return new BaristaInferenceTaskWidget{this, newTask, parent}; -} - void BaristaInferenceTask::serialize(SerializationContext& ctx) const { BaristaTask::serialize(ctx); diff --git a/Grinder/ml/barista/tasks/BaristaInferenceTask.h b/Grinder/ml/barista/tasks/BaristaInferenceTask.h index acab89c..f712651 100644 --- a/Grinder/ml/barista/tasks/BaristaInferenceTask.h +++ b/Grinder/ml/barista/tasks/BaristaInferenceTask.h @@ -49,9 +49,6 @@ namespace grndr public: virtual void registerMessageHandlers() override; - public: - virtual ConfigureTaskWidgetBase* createEditor(bool newTask, QWidget* parent) override; - public: QString getNetworkStateFile() const { return _networkStateFile; } void setNetworkStateFile(QString path) { _networkStateFile = path; } diff --git a/Grinder/ml/barista/tasks/BaristaTask.h b/Grinder/ml/barista/tasks/BaristaTask.h index 759e887..915a10c 100644 --- a/Grinder/ml/barista/tasks/BaristaTask.h +++ b/Grinder/ml/barista/tasks/BaristaTask.h @@ -35,6 +35,9 @@ namespace grndr public: virtual void registerMessageHandlers() override; + public: + virtual void processEngineStart(EngineExecutionContext& ctx, const data_type& data) override; + public: unsigned int getBaristaPort() const { return _baristaPort; } void setBaristaPort(unsigned int port) { _baristaPort = port; } @@ -51,7 +54,6 @@ namespace grndr public: const ImageTags* inputImageTags() const { return _inputImageTags; } - void setInputImageTags(const ImageTags* imageTags) { _inputImageTags = imageTags; } public: virtual void serialize(SerializationContext& ctx) const override; diff --git a/Grinder/ml/barista/tasks/BaristaTask.impl.h b/Grinder/ml/barista/tasks/BaristaTask.impl.h index 7f4d1ff..7453d8b 100644 --- a/Grinder/ml/barista/tasks/BaristaTask.impl.h +++ b/Grinder/ml/barista/tasks/BaristaTask.impl.h @@ -42,6 +42,15 @@ void BaristaTask<ClassType>::registerMessageHandlers() } +template<typename ClassType> +void BaristaTask<ClassType>::processEngineStart(EngineExecutionContext& ctx, const PipelineTask::data_type& data) +{ + MachineLearningTask::processEngineStart(ctx, data); + + // Store the image tags, as they will be used during context preparation + _inputImageTags = data.imageTags; +} + template<typename ClassType> void BaristaTask<ClassType>::setNetwork(BaristaNetwork* network) { diff --git a/Grinder/ml/barista/tasks/BaristaTrainingTask.cpp b/Grinder/ml/barista/tasks/BaristaTrainingTask.cpp index 24b9a1c..af0b95c 100644 --- a/Grinder/ml/barista/tasks/BaristaTrainingTask.cpp +++ b/Grinder/ml/barista/tasks/BaristaTrainingTask.cpp @@ -10,7 +10,6 @@ #include "pipeline/Block.h" #include "engine/EngineExecutionContext.h" #include "image/properties/ImageTagsProperty.h" -#include "ui/barista/tasks/BaristaTrainingTaskWidget.h" #include "res/Filenames.h" #include "ml/barista/msgs/BaristaStartTrainingMessage.h" @@ -37,11 +36,6 @@ void BaristaTrainingTask::registerMessageHandlers() registerMessageHandler<BaristaFinishTrainingMessage>(BaristaFinishTrainingMessage::Key, &BaristaTrainingTask::handleFinishTrainingMessage); } -ConfigureTaskWidgetBase* BaristaTrainingTask::createEditor(bool newTask, QWidget* parent) -{ - return new BaristaTrainingTaskWidget{this, newTask, parent}; -} - void BaristaTrainingTask::processEngineStart(EngineExecutionContext& ctx, const MachineLearningTaskData& data) { BaristaTask::processEngineStart(ctx, data); diff --git a/Grinder/ml/barista/tasks/BaristaTrainingTask.h b/Grinder/ml/barista/tasks/BaristaTrainingTask.h index 3fda464..1de0109 100644 --- a/Grinder/ml/barista/tasks/BaristaTrainingTask.h +++ b/Grinder/ml/barista/tasks/BaristaTrainingTask.h @@ -28,9 +28,6 @@ namespace grndr public: virtual void registerMessageHandlers() override; - public: - virtual ConfigureTaskWidgetBase* createEditor(bool newTask, QWidget* parent) override; - public: virtual void processEngineStart(EngineExecutionContext& ctx, const MachineLearningTaskData& data) override; virtual void processEnginePass(EngineExecutionContext& ctx, const MachineLearningTaskData& data) override; diff --git a/Grinder/ml/blocks/MachineLearningBlock.cpp b/Grinder/ml/blocks/MachineLearningBlock.cpp index 1841dda..b94d333 100644 --- a/Grinder/ml/blocks/MachineLearningBlock.cpp +++ b/Grinder/ml/blocks/MachineLearningBlock.cpp @@ -14,6 +14,9 @@ void MachineLearningBlock::createPorts() DataDescriptors statePortDataDescs = {DataDescriptor::customDescriptor("Model state", DataType::MachineLearningState)}; _statePort = createPort(PortType::State, Port::Direction::In, statePortDataDescs, "State"); + DataDescriptors imageTagsPortDataDescs = {DataDescriptor::customDescriptor("Image tags", DataType::ImageTags)}; + _imageTagsPort = createPort(PortType::ImageTagsIn, Port::Direction::In, imageTagsPortDataDescs, "Tags"); + DataDescriptors inPortDataDescs = {DataDescriptor::imageDescriptor(true, DataDescriptor::ValueType::Any), DataDescriptor::imageDescriptor(false, DataDescriptor::ValueType::Any)}; _inPort = createPort(PortType::ImageIn, Port::Direction::In, inPortDataDescs, "In"); } diff --git a/Grinder/ml/blocks/MachineLearningBlock.h b/Grinder/ml/blocks/MachineLearningBlock.h index e790f04..62df5c3 100644 --- a/Grinder/ml/blocks/MachineLearningBlock.h +++ b/Grinder/ml/blocks/MachineLearningBlock.h @@ -22,6 +22,8 @@ namespace grndr const Port* methodPort() const { return _methodPort.get(); } Port* statePort() { return _statePort.get(); } const Port* statePort() const { return _statePort.get(); } + Port* imageTagsPort() { return _imageTagsPort.get(); } + const Port* imageTagsPort() const { return _imageTagsPort.get(); } Port* inPort() { return _inPort.get(); } const Port* inPort() const { return _inPort.get(); } @@ -31,6 +33,7 @@ namespace grndr private: std::shared_ptr<Port> _methodPort; std::shared_ptr<Port> _statePort; + std::shared_ptr<Port> _imageTagsPort; std::shared_ptr<Port> _inPort; }; } diff --git a/Grinder/ml/blocks/MachineLearningMethodBlock.h b/Grinder/ml/blocks/MachineLearningMethodBlock.h index e826047..3399a17 100644 --- a/Grinder/ml/blocks/MachineLearningMethodBlock.h +++ b/Grinder/ml/blocks/MachineLearningMethodBlock.h @@ -38,8 +38,6 @@ namespace grndr auto state() { return dynamic_cast<MachineLearningStateProperty*>(_state.get()); } auto state() const { return dynamic_cast<const MachineLearningStateProperty*>(_state.get()); } - Port* imageTagsPort() { return _imageTagsPort.get(); } - const Port* imageTagsPort() const { return _imageTagsPort.get(); } Port* methodPort() { return _methodPort.get(); } const Port* methodPort() const { return _methodPort.get(); } Port* statePort() { return _statePort.get(); } @@ -51,21 +49,17 @@ namespace grndr virtual void createPorts() override; protected: - virtual void updateConfiguration(); + virtual void updateConfiguration() { } protected: bool checkStateAvailability(); - private: - void imageTagsPortConnectionChanged(const Connection* connection) { Q_UNUSED(connection); updateConfiguration(); } - protected: method_type _method; protected: std::shared_ptr<PropertyBase> _state; - std::shared_ptr<Port> _imageTagsPort; std::shared_ptr<Port> _methodPort; std::shared_ptr<Port> _statePort; }; diff --git a/Grinder/ml/blocks/MachineLearningMethodBlock.impl.h b/Grinder/ml/blocks/MachineLearningMethodBlock.impl.h index e4393ea..5837ac8 100644 --- a/Grinder/ml/blocks/MachineLearningMethodBlock.impl.h +++ b/Grinder/ml/blocks/MachineLearningMethodBlock.impl.h @@ -18,10 +18,6 @@ void MachineLearningMethodBlock<MethodType>::initBlock() { Block::initBlock(); - // Listen to connection changes on the image tags port - connect(_imageTagsPort.get(), &Port::portConnected, this, &MachineLearningMethodBlock<MethodType>::imageTagsPortConnectionChanged); - connect(_imageTagsPort.get(), &Port::portDisconnected, this, &MachineLearningMethodBlock<MethodType>::imageTagsPortConnectionChanged); - updateConfiguration(); } @@ -56,10 +52,7 @@ bool MachineLearningMethodBlock<MethodType>::updateProperties(PropertyBase* upda template<typename MethodType> void MachineLearningMethodBlock<MethodType>::createPorts() -{ - DataDescriptors imageTagsPortDataDescs = {DataDescriptor::customDescriptor("Image tags", DataType::ImageTags)}; - _imageTagsPort = createPort(PortType::ImageTagsIn, Port::Direction::In, imageTagsPortDataDescs, "Tags"); - +{ DataDescriptors methodPortDataDescs = {DataDescriptor::customDescriptor("Machine learning method", DataType::MachineLearningMethod)}; _methodPort = createPort(PortType::Method, Port::Direction::Out, methodPortDataDescs, "Method"); @@ -67,15 +60,6 @@ void MachineLearningMethodBlock<MethodType>::createPorts() _statePort = createPort(PortType::State, Port::Direction::Out, statePortDataDescs, "State"); } -template<typename MethodType> -void MachineLearningMethodBlock<MethodType>::updateConfiguration() -{ - if (auto imageTagsProperty = this->template portProperty<ImageTagsProperty>(PortType::ImageTagsIn, PropertyID::ImageTags)) - _method.config().setImageTags(&imageTagsProperty->object()); - else - _method.config().setImageTags(nullptr); -} - template<typename MethodType> bool MachineLearningMethodBlock<MethodType>::checkStateAvailability() { diff --git a/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h b/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h index 94a7df7..9c88e62 100644 --- a/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h +++ b/Grinder/ml/processors/MachineLearningMethodProcessor.impl.h @@ -6,7 +6,6 @@ #include "Grinder.h" #include "MachineLearningMethodProcessor.h" #include "ml/MachineLearningMethodBase.h" -#include "image/properties/ImageTagsProperty.h" Q_DECLARE_METATYPE(const MachineLearningMethodBase*) diff --git a/Grinder/ml/processors/MachineLearningProcessor.impl.h b/Grinder/ml/processors/MachineLearningProcessor.impl.h index 2fa4f3e..bda120a 100644 --- a/Grinder/ml/processors/MachineLearningProcessor.impl.h +++ b/Grinder/ml/processors/MachineLearningProcessor.impl.h @@ -8,7 +8,7 @@ #include "ml/MachineLearningMethodBase.h" #include "ml/MachineLearningTaskSpawnerBase.h" #include "ml/MachineLearningConfiguration.h" -#include "image/ImageTags.h" +#include "image/properties/ImageTagsProperty.h" template<typename BlockType> const char* MachineLearningProcessor<BlockType>::Data_Value_Method = "Method"; @@ -37,6 +37,11 @@ void MachineLearningProcessor<BlockType>::execute(EngineExecutionContext& ctx) // Get the used machine learning method and model state const MachineLearningMethodBase* method = this->template portData<const MachineLearningMethodBase*>(ctx, this->_block->methodPort(), Data_Value_Method); QString state = this->template portData<QString>(ctx, this->_block->statePort(), Data_Value_State, false); + const ImageTags* imageTags = nullptr; + + //if (auto imageTagsProperty = this->_block->template portProperty<ImageTagsProperty>(PortType::ImageTagsIn, PropertyID::ImageTags)) + if (auto imageTagsProperty = this->template portProperty<ImageTagsProperty>(this->_block->imageTagsPort(), PropertyID::ImageTags)) + imageTags = &imageTagsProperty->object(); if (!execute(ctx, method, state)) { @@ -58,8 +63,9 @@ void MachineLearningProcessor<BlockType>::execute(EngineExecutionContext& ctx) } // Get the task data for machine learning - MachineLearningTaskData taskData; - fillTaskData(ctx, method, taskData); + MachineLearningTaskData taskData; + taskData.imageTags = imageTags; + fillTaskData(ctx, method, taskData); if (ctx.isFirstImage()) _spawnedTask->processEngineStart(ctx, taskData); @@ -100,7 +106,7 @@ void MachineLearningProcessor<BlockType>::spawnTask(const MachineLearningMethodB if (auto spawner = method->createTaskSpawner()) { - QString taskName = QString{"%1 %2 (%3)"}.arg(method->getMethodName()).arg(getSpawnTypeName(_spawnType)).arg(this->_block->getFormattedName()); + QString taskName = QString{"%1 %2"}.arg(method->getMethodName()).arg(getSpawnTypeName(_spawnType)); switch (_spawnType) { @@ -115,6 +121,8 @@ void MachineLearningProcessor<BlockType>::spawnTask(const MachineLearningMethodB if (!_spawnedTask) this->throwProcessorException("Unable to spawn the machine learning task"); + + _spawnedTask->setInfo(this->_block->getFormattedName()); } else this->throwProcessorException("Unable to create a task spawner"); diff --git a/Grinder/ml/tasks/MachineLearningTask.h b/Grinder/ml/tasks/MachineLearningTask.h index c3ea9b6..7eb7a9c 100644 --- a/Grinder/ml/tasks/MachineLearningTask.h +++ b/Grinder/ml/tasks/MachineLearningTask.h @@ -16,6 +16,8 @@ namespace grndr struct MachineLearningTaskData { + const ImageTags* imageTags{nullptr}; + cv::Mat imageData; cv::Mat imageTagsData; }; diff --git a/Grinder/network/cmd/tasks/CommandInterfaceTask.cpp b/Grinder/network/cmd/tasks/CommandInterfaceTask.cpp index 3c339a8..9bcd248 100644 --- a/Grinder/network/cmd/tasks/CommandInterfaceTask.cpp +++ b/Grinder/network/cmd/tasks/CommandInterfaceTask.cpp @@ -26,7 +26,7 @@ const char* CommandInterfaceTask::Serialization_Value_Label = "Label"; const char* CommandInterfaceTask::Serialization_Value_CanvasBlock = "CanvasBlock"; const char* CommandInterfaceTask::Serialization_Value_UserData = "UserData"; -CommandInterfaceTask::CommandInterfaceTask(TaskPool* taskPool, QString name) : CommandInterfaceTaskBase(this, CommandInterface::CoreType::Server, taskPool, type_value, Capability::CanBeStopped|Capability::HasProgress|Capability::CanBeRefreshed, name) +CommandInterfaceTask::CommandInterfaceTask(TaskPool* taskPool, QString name) : CommandInterfaceTaskBase(this, CommandInterface::CoreType::Server, taskPool, type_value, Capability::CanBeStopped|Capability::CanBeRefreshed|Capability::HasProgress|Capability::UserControllable|Capability::Persistent, name) { } diff --git a/Grinder/network/cmd/tasks/CommandInterfaceTestTask.cpp b/Grinder/network/cmd/tasks/CommandInterfaceTestTask.cpp index 5abd34e..00441c9 100644 --- a/Grinder/network/cmd/tasks/CommandInterfaceTestTask.cpp +++ b/Grinder/network/cmd/tasks/CommandInterfaceTestTask.cpp @@ -41,7 +41,7 @@ const char* CommandInterfaceTestTask::Serialization_Value_Address = "Address"; const char* CommandInterfaceTestTask::Serialization_Value_RenderItems = "RenderItems"; const char* CommandInterfaceTestTask::Serialization_Value_BackgroundColor = "BackgroundColor"; -CommandInterfaceTestTask::CommandInterfaceTestTask(TaskPool* taskPool, QString name) : CommandInterfaceTaskBase(this, CommandInterface::CoreType::Client, taskPool, type_value, Capability::CanBeStopped|Capability::HasProgress, name) +CommandInterfaceTestTask::CommandInterfaceTestTask(TaskPool* taskPool, QString name) : CommandInterfaceTaskBase(this, CommandInterface::CoreType::Client, taskPool, type_value, Capability::CanBeStopped|Capability::HasProgress|Capability::UserControllable|Capability::Persistent, name) { } diff --git a/Grinder/task/Task.cpp b/Grinder/task/Task.cpp index 1fc32d8..2e05d32 100644 --- a/Grinder/task/Task.cpp +++ b/Grinder/task/Task.cpp @@ -8,6 +8,7 @@ const char* Task::Serialization_Value_Type = "Type"; const char* Task::Serialization_Value_Name = "Name"; +const char* Task::Serialization_Value_Info = "Info"; Task::Task(TaskPool* taskPool, TaskType type, Capabilities caps, QString name) : _taskPool{taskPool}, _type{type}, _capabilities{caps}, _name{name} @@ -153,12 +154,14 @@ void Task::serialize(SerializationContext& ctx) const // Serialize values ctx.settings()(Serialization_Value_Type) = _type; ctx.settings()(Serialization_Value_Name) = _name; + ctx.settings()(Serialization_Value_Info) = _info; } void Task::deserialize(DeserializationContext& ctx) { // Deserialize values _name = ctx.settings()(Serialization_Value_Name).toString(); + _info = ctx.settings()(Serialization_Value_Info).toString(); } void Task::reportStatus(QString status, bool preLine, bool postLine) diff --git a/Grinder/task/Task.h b/Grinder/task/Task.h index 102eba0..deeab34 100644 --- a/Grinder/task/Task.h +++ b/Grinder/task/Task.h @@ -22,6 +22,7 @@ namespace grndr public: static const char* Serialization_Value_Type; static const char* Serialization_Value_Name; + static const char* Serialization_Value_Info; public: enum class Status @@ -47,6 +48,9 @@ namespace grndr HasProgress = 0x0008, + UserControllable = 0x0010, + Persistent = 0x0020, + All = 0xFFFF, }; @@ -78,6 +82,8 @@ namespace grndr Capabilities getCapabilities() const { return _capabilities; } QString getName() const { return _name; } void setName(QString name) { if (_name != name) { _name = name; emit taskUpdated(); } } + QString getInfo() const { return _info; } + void setInfo(QString info) { if (_info != info) { _info = info; emit taskUpdated(); } } Status getStatus() const { return _status; } void setStatus(Status status) { if (_status != status) { _status = status; emit taskUpdated(); } } @@ -126,7 +132,8 @@ namespace grndr TaskType _type{TaskType::Undefined}; Capabilities _capabilities{Capability::None}; - QString _name; + QString _name{""}; + QString _info{""}; Status _status{Status::Pending}; Result _result{Result::None}; diff --git a/Grinder/task/TaskCatalog.cpp b/Grinder/task/TaskCatalog.cpp index 17529a7..f0a413d 100644 --- a/Grinder/task/TaskCatalog.cpp +++ b/Grinder/task/TaskCatalog.cpp @@ -13,29 +13,40 @@ #include "ml/barista/tasks/BaristaTrainingTask.h" #include "ml/barista/tasks/BaristaInferenceTask.h" -#define REGISTER_TASK_TYPE(cls) registerTaskType(cls::type_value, [](TaskPool* taskPool, QString name) { return std::make_unique<cls>(taskPool, name); }) +#define REGISTER_TASK_TYPE(cls, userCreatable) registerTaskType(cls::type_value, {[](TaskPool* taskPool, QString name) { return std::make_unique<cls>(taskPool, name); }, userCreatable}) -std::map<TaskType, TaskCatalog::task_creator_type> TaskCatalog::s_creators; +std::map<TaskType, TaskCatalog::CatalogEntry> TaskCatalog::s_entries; std::set<TaskType> TaskCatalog::getTypes() { std::set<TaskType> types; - for (const auto& creator : s_creators) - types.insert(creator.first); + for (const auto& entry : s_entries) + types.insert(entry.first); return types; } -void TaskCatalog::registerTaskType(TaskType type, task_creator_type creator) +bool TaskCatalog::isUserCreatable(TaskType type) +{ + if (s_entries.find(type) != s_entries.end()) + { + auto entry = s_entries.at(type); + return entry.isUserCreatable; + } + else + throw std::invalid_argument{_EXCPT("The given task type has not been registered")}; +} + +void TaskCatalog::registerTaskType(TaskType type, CatalogEntry entry) { if (type == TaskType::Undefined) throw std::invalid_argument{_EXCPT("type may not be TaskType::Undefined")}; - if (!creator) + if (!entry.creator) throw std::invalid_argument{_EXCPT("creator may not be null")}; - s_creators[type] = creator; + s_entries[type] = entry; } std::unique_ptr<Task> TaskCatalog::createTask(TaskPool* taskPool, TaskType type, QString name) @@ -43,10 +54,10 @@ std::unique_ptr<Task> TaskCatalog::createTask(TaskPool* taskPool, TaskType type, if (type == TaskType::Undefined) throw std::invalid_argument{_EXCPT("type may not be TaskType::Undefined")}; - if (s_creators.find(type) != s_creators.end()) + if (s_entries.find(type) != s_entries.end()) { - auto creator = s_creators.at(type); - auto task = creator(taskPool, name); + auto entry = s_entries.at(type); + auto task = entry.creator(taskPool, name); if (!task) throw std::runtime_error{_EXCPT(QString{"Failed to create a task of type %1"}.arg(type))}; @@ -59,9 +70,10 @@ std::unique_ptr<Task> TaskCatalog::createTask(TaskPool* taskPool, TaskType type, void TaskCatalog::registerStandardTasks() { - REGISTER_TASK_TYPE(GenericTask); - REGISTER_TASK_TYPE(CommandInterfaceTask); - REGISTER_TASK_TYPE(CommandInterfaceTestTask); - REGISTER_TASK_TYPE(BaristaTrainingTask); - REGISTER_TASK_TYPE(BaristaInferenceTask); + REGISTER_TASK_TYPE(GenericTask, true); + REGISTER_TASK_TYPE(CommandInterfaceTask, true); + REGISTER_TASK_TYPE(CommandInterfaceTestTask, true); + + REGISTER_TASK_TYPE(BaristaTrainingTask, false); + REGISTER_TASK_TYPE(BaristaInferenceTask, false); } diff --git a/Grinder/task/TaskCatalog.h b/Grinder/task/TaskCatalog.h index 3e5de14..957e0de 100644 --- a/Grinder/task/TaskCatalog.h +++ b/Grinder/task/TaskCatalog.h @@ -24,6 +24,12 @@ namespace grndr private: using task_creator_type = std::function<std::unique_ptr<Task>(TaskPool*, QString)>; + struct CatalogEntry + { + task_creator_type creator{nullptr}; + bool isUserCreatable{true}; + }; + private: TaskCatalog() { } @@ -34,12 +40,13 @@ namespace grndr public: static std::set<TaskType> getTypes(); + static bool isUserCreatable(TaskType type); private: - static void registerTaskType(TaskType type, task_creator_type creator); + static void registerTaskType(TaskType type, CatalogEntry entry); private: - static std::map<TaskType, task_creator_type> s_creators; + static std::map<TaskType, CatalogEntry> s_entries; }; } diff --git a/Grinder/task/TaskPool.cpp b/Grinder/task/TaskPool.cpp index 8e789e1..43336f1 100644 --- a/Grinder/task/TaskPool.cpp +++ b/Grinder/task/TaskPool.cpp @@ -81,7 +81,7 @@ void TaskPool::serialize(SerializationContext& ctx) const { // Serialize all tasks ctx.beginGroup(TaskVector::Serialization_Group, true); - _tasks.serialize(TaskVector::Serialization_Element, ctx); + _tasks.serialize(TaskVector::Serialization_Element, ctx, [](auto task) { return task->getCapabilities().testFlag(Task::Capability::Persistent); }); ctx.endGroup(); } diff --git a/Grinder/task/tasks/GenericTask.cpp b/Grinder/task/tasks/GenericTask.cpp index 8f57977..aa4f97d 100644 --- a/Grinder/task/tasks/GenericTask.cpp +++ b/Grinder/task/tasks/GenericTask.cpp @@ -13,7 +13,7 @@ const TaskType GenericTask::type_value = TaskType::Generic; const char* GenericTask::Serialization_Value_Command = "Command"; const char* GenericTask::Serialization_Value_Arguments = "Arguments"; -GenericTask::GenericTask(TaskPool* taskPool, QString name) : Task(taskPool, type_value, Task::Capability::CanBeStopped, name) +GenericTask::GenericTask(TaskPool* taskPool, QString name) : Task(taskPool, type_value, Capability::CanBeStopped|Capability::UserControllable|Capability::Persistent, name) { } diff --git a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.cpp b/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.cpp deleted file mode 100644 index f7f9a4e..0000000 --- a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/****************************************************************************** - * File: BaristaInferenceTaskWidget.cpp - * Date: 07.12.2018 - *****************************************************************************/ - -#include "Grinder.h" -#include "BaristaInferenceTaskWidget.h" -#include "ui_BaristaInferenceTaskWidget.h" -#include "core/GrinderApplication.h" - -BaristaInferenceTaskWidget::BaristaInferenceTaskWidget(BaristaInferenceTask* task, bool newTask, QWidget* parent) : ConfigureTaskWidget(task, newTask, parent), - ui{new Ui::BaristaInferenceTaskWidget} -{ - setupUi(); -} - -BaristaInferenceTaskWidget::~BaristaInferenceTaskWidget() -{ - delete ui; -} - -void BaristaInferenceTaskWidget::verifySettings() -{ - if (ui->txtLibraryPath->text().isEmpty()) - showError("Please enter a library path.", ui->txtLibraryPath); - - if (!ui->lstNetworks->getSelectedNetwork()) - showError("Please select a network.", ui->lstNetworks); - - if (ui->txtOutputDir->text().isEmpty()) - showError("Please enter an output directory.", ui->txtOutputDir); - - if (ui->lstNetworkState->currentText().isEmpty()) - showError("Please select a network state.", ui->lstNetworkState); - - if (!ui->chkGenerateItems->isChecked() && !ui->chkGenerateMaps->isChecked()) - showError("Please select at least one result type.", ui->chkGenerateItems); -} - -void BaristaInferenceTaskWidget::applySettings(bool save) -{ - if (save) - { - _task->setBaristaPort(ui->txtWorkerPort->value()); - _task->setLibraryPath(ui->txtLibraryPath->text()); - - _task->setNetwork(ui->lstNetworks->getSelectedNetwork()); - _task->setOutputDirectory(ui->txtOutputDir->text()); - _task->setRemoteDirectory(ui->txtRemoteDir->text()); - _task->setNetworkStateFile(ui->lstNetworkState->currentText()); - - _task->setProbabilityResultTypes(getProbabilityResultTypes()); - _task->setProbabilityThreshold(ui->txtThreshold->value()); - } - else - { - ui->txtWorkerPort->setValue(_task->getBaristaPort()); - ui->txtLibraryPath->setText(_task->getLibraryPath()); - - ui->lstNetworks->setSelectedNetwork(_task->getNetwork()); - ui->txtOutputDir->setText(_task->getOutputDirectory()); - ui->txtRemoteDir->setText(_task->getRemoteDirectory()); - - setProbabilityResultTypes(_task->getProbabilityResultTypes()); - ui->txtThreshold->setValue(_task->getProbabilityThreshold()); - - fillNetworkStateFiles(); - } -} - -void BaristaInferenceTaskWidget::on_txtOutputDir_editingFinished() -{ - fillNetworkStateFiles(); -} - -void BaristaInferenceTaskWidget::setupUi() -{ - ui->setupUi(this); - - ui->lstNetworks->populate(); -} - -void BaristaInferenceTaskWidget::fillNetworkStateFiles() -{ - ui->lstNetworkState->clear(); - - QDir dir{ui->txtOutputDir->text()}; - auto modelFiles = dir.entryList({"*.caffemodel"}, QDir::Files); - - // Sort the model filenames naturally - QCollator collator; - collator.setNumericMode(true); - std::sort(modelFiles.begin(), modelFiles.end(), collator); - - int curIndex = 0; - int selIndex = -1; - - for (auto modelFile : modelFiles) - { - ui->lstNetworkState->addItem(modelFile); - - if (modelFile.compare(_task->getNetworkStateFile(), Qt::CaseInsensitive) == 0) - selIndex = curIndex; - - curIndex += 1; - } - - if (selIndex != -1) - ui->lstNetworkState->setCurrentIndex(selIndex); - else - ui->lstNetworkState->setCurrentIndex(ui->lstNetworkState->count() - 1); -} - -BaristaInferenceTask::ProbabilityResultTypes BaristaInferenceTaskWidget::getProbabilityResultTypes() const -{ - BaristaInferenceTask::ProbabilityResultTypes types = BaristaInferenceTask::ProbabilityResultType::None; - - if (ui->chkGenerateItems->isChecked()) - types |= BaristaInferenceTask::ProbabilityResultType::Items; - - if (ui->chkGenerateMaps->isChecked()) - types |= BaristaInferenceTask::ProbabilityResultType::Maps; - - return types; -} - -void BaristaInferenceTaskWidget::setProbabilityResultTypes(BaristaInferenceTask::ProbabilityResultTypes types) -{ - ui->chkGenerateItems->setChecked(types.testFlag(BaristaInferenceTask::ProbabilityResultType::Items)); - ui->chkGenerateMaps->setChecked(types.testFlag(BaristaInferenceTask::ProbabilityResultType::Maps)); -} diff --git a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.h b/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.h deleted file mode 100644 index 3ec6668..0000000 --- a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.h +++ /dev/null @@ -1,52 +0,0 @@ -/****************************************************************************** - * File: BaristaInferenceTaskWidget.h - * Date: 07.12.2018 - *****************************************************************************/ - -#ifndef BARISTAINFERENCETASKWIDGET_H -#define BARISTAINFERENCETASKWIDGET_H - -#include "ui/task/ConfigureTaskWidget.h" -#include "ml/barista/tasks/BaristaInferenceTask.h" - -namespace Ui -{ - class BaristaInferenceTaskWidget; -} - -namespace grndr -{ - class Label; - class LabelVector; - class ImageReference; - class ImageReferenceVector; - class Block; - - class BaristaInferenceTaskWidget : public ConfigureTaskWidget<BaristaInferenceTask> - { - Q_OBJECT - - public: - explicit BaristaInferenceTaskWidget(BaristaInferenceTask* task, bool newTask, QWidget *parent = nullptr); - virtual ~BaristaInferenceTaskWidget(); - - public: - virtual void verifySettings() override; - virtual void applySettings(bool save) override; - - private slots: - void on_txtOutputDir_editingFinished(); - - private: - Ui::BaristaInferenceTaskWidget *ui; - void setupUi(); - - private: - void fillNetworkStateFiles(); - - BaristaInferenceTask::ProbabilityResultTypes getProbabilityResultTypes() const; - void setProbabilityResultTypes(BaristaInferenceTask::ProbabilityResultTypes types); - }; -} - -#endif diff --git a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.ui b/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.ui deleted file mode 100644 index 9c059b0..0000000 --- a/Grinder/ui/barista/tasks/BaristaInferenceTaskWidget.ui +++ /dev/null @@ -1,298 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>BaristaInferenceTaskWidget</class> - <widget class="QWidget" name="BaristaInferenceTaskWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>404</width> - <height>367</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QGridLayout" name="gridLayout_6"> - <item row="0" column="0"> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>Barista settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="1"> - <widget class="QSpinBox" name="txtWorkerPort"> - <property name="minimum"> - <number>1024</number> - </property> - <property name="maximum"> - <number>65536</number> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Library &path:</string> - </property> - <property name="buddy"> - <cstring>txtLibraryPath</cstring> - </property> - </widget> - </item> - <item row="1" column="1" colspan="2"> - <widget class="QLineEdit" name="txtLibraryPath"/> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>&Worker port:</string> - </property> - <property name="buddy"> - <cstring>txtWorkerPort</cstring> - </property> - </widget> - </item> - <item row="0" column="2"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>185</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - </item> - <item row="1" column="0"> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Network settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <item row="2" column="1"> - <widget class="QLineEdit" name="txtRemoteDir"/> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="label_10"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Leave empty to use the network directory</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="txtOutputDir"/> - </item> - <item row="0" column="1"> - <widget class="BaristaNetworksComboBox" name="lstNetworks"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_9"> - <property name="text"> - <string>&Remote directory:</string> - </property> - <property name="buddy"> - <cstring>txtRemoteDir</cstring> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>&Network:</string> - </property> - <property name="buddy"> - <cstring>lstNetworks</cstring> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_8"> - <property name="text"> - <string>&Output directory:</string> - </property> - <property name="buddy"> - <cstring>txtOutputDir</cstring> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>Network &state:</string> - </property> - <property name="buddy"> - <cstring>lstNetworkState</cstring> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QComboBox" name="lstNetworkState"/> - </item> - </layout> - </widget> - </item> - <item row="2" column="0"> - <widget class="QGroupBox" name="groupBox_4"> - <property name="title"> - <string>Result settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_5"> - <item row="1" column="0"> - <widget class="QFrame" name="frame"> - <property name="frameShape"> - <enum>QFrame::StyledPanel</enum> - </property> - <property name="frameShadow"> - <enum>QFrame::Raised</enum> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <property name="leftMargin"> - <number>18</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item row="0" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>&Threshold:</string> - </property> - <property name="buddy"> - <cstring>sldThreshold</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSlider" name="sldThreshold"> - <property name="maximum"> - <number>100</number> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="tickPosition"> - <enum>QSlider::TicksBelow</enum> - </property> - <property name="tickInterval"> - <number>10</number> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QSpinBox" name="txtThreshold"> - <property name="suffix"> - <string>%</string> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="value"> - <number>0</number> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="0" column="0"> - <widget class="QCheckBox" name="chkGenerateItems"> - <property name="text"> - <string>Generate &items</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QCheckBox" name="chkGenerateMaps"> - <property name="text"> - <string>Generate probability &maps</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>BaristaNetworksComboBox</class> - <extends>QComboBox</extends> - <header>ui/barista/widgets/BaristaNetworksComboBox.h</header> - </customwidget> - </customwidgets> - <tabstops> - <tabstop>txtWorkerPort</tabstop> - <tabstop>txtLibraryPath</tabstop> - <tabstop>lstNetworks</tabstop> - <tabstop>txtOutputDir</tabstop> - <tabstop>txtRemoteDir</tabstop> - <tabstop>lstNetworkState</tabstop> - <tabstop>chkGenerateItems</tabstop> - <tabstop>sldThreshold</tabstop> - <tabstop>txtThreshold</tabstop> - <tabstop>chkGenerateMaps</tabstop> - </tabstops> - <resources/> - <connections> - <connection> - <sender>txtThreshold</sender> - <signal>valueChanged(int)</signal> - <receiver>sldThreshold</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>335</x> - <y>602</y> - </hint> - <hint type="destinationlabel"> - <x>201</x> - <y>606</y> - </hint> - </hints> - </connection> - <connection> - <sender>chkGenerateItems</sender> - <signal>toggled(bool)</signal> - <receiver>frame</receiver> - <slot>setEnabled(bool)</slot> - <hints> - <hint type="sourcelabel"> - <x>48</x> - <y>579</y> - </hint> - <hint type="destinationlabel"> - <x>29</x> - <y>601</y> - </hint> - </hints> - </connection> - <connection> - <sender>sldThreshold</sender> - <signal>valueChanged(int)</signal> - <receiver>txtThreshold</receiver> - <slot>setValue(int)</slot> - <hints> - <hint type="sourcelabel"> - <x>177</x> - <y>608</y> - </hint> - <hint type="destinationlabel"> - <x>343</x> - <y>615</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.cpp b/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.cpp deleted file mode 100644 index e036d08..0000000 --- a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/****************************************************************************** - * File: BaristaTrainingTaskWidget.cpp - * Date: 20.11.2018 - *****************************************************************************/ - -#include "Grinder.h" -#include "BaristaTrainingTaskWidget.h" -#include "ui_BaristaTrainingTaskWidget.h" -#include "core/GrinderApplication.h" -#include "image/properties/ImageTagsProperty.h" -#include "ml/barista/tasks/BaristaTrainingTask.h" - -BaristaTrainingTaskWidget::BaristaTrainingTaskWidget(BaristaTrainingTask* task, bool newTask, QWidget *parent) : ConfigureTaskWidget(task, newTask, parent), - ui{new Ui::BaristaTrainingTaskWidget} -{ - setupUi(); -} - -BaristaTrainingTaskWidget::~BaristaTrainingTaskWidget() -{ - delete ui; -} - -void BaristaTrainingTaskWidget::verifySettings() -{ - if (ui->txtLibraryPath->text().isEmpty()) - showError("Please enter a library path.", ui->txtLibraryPath); - - if (!ui->lstNetworks->getSelectedNetwork()) - showError("Please select a network.", ui->lstNetworks); - - if (ui->txtOutputDir->text().isEmpty()) - showError("Please enter an output directory.", ui->txtOutputDir); -} - -void BaristaTrainingTaskWidget::applySettings(bool save) -{ - if (save) - { - _task->setBaristaPort(ui->txtWorkerPort->value()); - _task->setLibraryPath(ui->txtLibraryPath->text()); - - _task->setNetwork(ui->lstNetworks->getSelectedNetwork()); - _task->setOutputDirectory(ui->txtOutputDir->text()); - _task->setRemoteDirectory(ui->txtRemoteDir->text()); - - _task->setMaxIterations(ui->txtMaxIterations->value()); - _task->setDisplayInterval(ui->txtDisplayInterval->value()); - _task->setSnapshotInterval(ui->txtSnapshotInterval->value()); - } - else - { - ui->txtWorkerPort->setValue(_task->getBaristaPort()); - ui->txtLibraryPath->setText(_task->getLibraryPath()); - - ui->lstNetworks->setSelectedNetwork(_task->getNetwork()); - ui->txtOutputDir->setText(_task->getOutputDirectory()); - ui->txtRemoteDir->setText(_task->getRemoteDirectory()); - - ui->txtMaxIterations->setValue(_task->getMaxIterations()); - ui->txtDisplayInterval->setValue(_task->getDisplayInterval()); - ui->txtSnapshotInterval->setValue(_task->getSnapshotInterval()); - } -} - -void BaristaTrainingTaskWidget::setupUi() -{ - ui->setupUi(this); - - ui->lstNetworks->populate(); -} diff --git a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.h b/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.h deleted file mode 100644 index 818b80e..0000000 --- a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.h +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * File: BaristaTrainingTaskWidget.h - * Date: 20.11.2018 - *****************************************************************************/ - -#ifndef BARISTATRAININGTASKWIDGET_H -#define BARISTATRAININGTASKWIDGET_H - -#include "ui/task/ConfigureTaskWidget.h" - -namespace Ui -{ - class BaristaTrainingTaskWidget; -} - -namespace grndr -{ - class BaristaTrainingTask; - class LabelVector; - class Label; - class ImageReferenceVector; - class ImageReference; - class Block; - - class BaristaTrainingTaskWidget : public ConfigureTaskWidget<BaristaTrainingTask> - { - Q_OBJECT - - public: - BaristaTrainingTaskWidget(BaristaTrainingTask* task, bool newTask, QWidget* parent = nullptr); - virtual ~BaristaTrainingTaskWidget(); - - public: - virtual void verifySettings() override; - virtual void applySettings(bool save) override; - - private: - Ui::BaristaTrainingTaskWidget *ui; - void setupUi(); - }; -} - -#endif diff --git a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.ui b/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.ui deleted file mode 100644 index 9845ce8..0000000 --- a/Grinder/ui/barista/tasks/BaristaTrainingTaskWidget.ui +++ /dev/null @@ -1,263 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>BaristaTrainingTaskWidget</class> - <widget class="QWidget" name="BaristaTrainingTaskWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>386</width> - <height>320</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item row="0" column="0"> - <widget class="QGroupBox" name="groupBox"> - <property name="title"> - <string>Barista settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>&Worker port:</string> - </property> - <property name="buddy"> - <cstring>txtWorkerPort</cstring> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Library &path:</string> - </property> - <property name="buddy"> - <cstring>txtLibraryPath</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSpinBox" name="txtWorkerPort"> - <property name="minimum"> - <number>1024</number> - </property> - <property name="maximum"> - <number>65536</number> - </property> - </widget> - </item> - <item row="0" column="2"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>205</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="1" column="1" colspan="2"> - <widget class="QLineEdit" name="txtLibraryPath"/> - </item> - </layout> - </widget> - </item> - <item row="1" column="0"> - <widget class="QGroupBox" name="groupBox_2"> - <property name="title"> - <string>Network settings</string> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="0"> - <widget class="QLabel" name="label_9"> - <property name="text"> - <string>&Remote directory:</string> - </property> - <property name="buddy"> - <cstring>txtRemoteDir</cstring> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="txtRemoteDir"/> - </item> - <item row="0" column="1"> - <widget class="BaristaNetworksComboBox" name="lstNetworks"/> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="label_10"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Leave empty to use the network directory</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_8"> - <property name="text"> - <string>&Output directory:</string> - </property> - <property name="buddy"> - <cstring>txtOutputDir</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="txtOutputDir"/> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>&Network:</string> - </property> - <property name="buddy"> - <cstring>lstNetworks</cstring> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="2" column="0"> - <widget class="QGroupBox" name="groupBox_4"> - <property name="title"> - <string>Training settings</string> - </property> - <layout class="QGridLayout" name="gridLayout_5"> - <item row="0" column="0"> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>Max. &iterations:</string> - </property> - <property name="buddy"> - <cstring>txtMaxIterations</cstring> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSpinBox" name="txtMaxIterations"> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>1000000000</number> - </property> - <property name="singleStep"> - <number>100</number> - </property> - <property name="value"> - <number>5000</number> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>&Display interval:</string> - </property> - <property name="buddy"> - <cstring>txtDisplayInterval</cstring> - </property> - </widget> - </item> - <item row="0" column="2"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>&Snapshot interval:</string> - </property> - <property name="buddy"> - <cstring>txtSnapshotInterval</cstring> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QSpinBox" name="txtDisplayInterval"> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>1000000000</number> - </property> - <property name="singleStep"> - <number>10</number> - </property> - <property name="value"> - <number>100</number> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QSpinBox" name="txtSnapshotInterval"> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>1000000000</number> - </property> - <property name="singleStep"> - <number>100</number> - </property> - <property name="value"> - <number>1000</number> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - <customwidgets> - <customwidget> - <class>BaristaNetworksComboBox</class> - <extends>QComboBox</extends> - <header>ui/barista/widgets/BaristaNetworksComboBox.h</header> - </customwidget> - </customwidgets> - <tabstops> - <tabstop>txtWorkerPort</tabstop> - <tabstop>txtLibraryPath</tabstop> - <tabstop>lstNetworks</tabstop> - <tabstop>txtOutputDir</tabstop> - <tabstop>txtRemoteDir</tabstop> - <tabstop>txtMaxIterations</tabstop> - <tabstop>txtDisplayInterval</tabstop> - <tabstop>txtSnapshotInterval</tabstop> - </tabstops> - <resources/> - <connections/> -</ui> diff --git a/Grinder/ui/task/TaskPoolWidget.cpp b/Grinder/ui/task/TaskPoolWidget.cpp index 0d4f9f5..0ba85ef 100644 --- a/Grinder/ui/task/TaskPoolWidget.cpp +++ b/Grinder/ui/task/TaskPoolWidget.cpp @@ -1,114 +1,117 @@ -/****************************************************************************** - * File: TaskPoolWidget.cpp - * Date: 01.11.2018 - *****************************************************************************/ - -#include "Grinder.h" -#include "TaskPoolWidget.h" -#include "TaskWidget.h" -#include "core/GrinderApplication.h" -#include "task/TaskCatalog.h" -#include "ui/UIUtils.h" -#include "res/Resources.h" - -TaskPoolWidget::TaskPoolWidget(QWidget* parent) : MetaWidget(parent), - _layout{new QVBoxLayout{this}} -{ - // Create tasks actions - _newTaskAction = UIUtils::createAction(this, "&New task", FILE_ICON_ADD, nullptr, "Add a new task", "", Qt::WidgetShortcut, nullptr, true); - _removeAllTasksAction = UIUtils::createAction(this, "Remove all tasks", "", SLOT(removeAllTasks()), "Remove all tasks"); - - // Set up the new task menu - for (auto type : TaskCatalog::getTypes()) - { - auto action = _newTaskMenu.addAction(type + " task"); - action->setData(type); - - connect(action, SIGNAL(triggered(bool)), this, SLOT(newTask())); - } - - _newTaskAction->setMenu(&_newTaskMenu); - - // Add a spacer item to align all tasks - _layout->setContentsMargins(0, 0, 0, 0); - _layout->setSpacing(0); - _layout->addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}); - - updateActions(); -} - -void TaskPoolWidget::addTask(const std::shared_ptr<Task>& task) -{ - if (!findTaskWidget(task.get())) - { - // Create a new task widget - auto widget = new TaskWidget{task, this}; - _layout->insertWidget(_taskWidgets.size(), widget); - - _taskWidgets.push_back(widget); - - updateActions(); - } -} - -void TaskPoolWidget::removeTask(const std::shared_ptr<Task>& task) -{ - // Remove the task widget - if (auto widget = findTaskWidget(task.get())) - { - _taskWidgets.erase(std::remove(_taskWidgets.begin(), _taskWidgets.end(), widget), _taskWidgets.end()); - - widget->hide(); - delete widget; - - updateActions(); - } -} - -std::vector<QAction*> TaskPoolWidget::getActions(MetaWidget::AddActionsMode mode) const -{ - std::vector<QAction*> actions; - - actions.push_back(_newTaskAction); - - if (mode == AddActionsMode::ContextMenu) - { - actions.push_back(nullptr); - actions.push_back(_removeAllTasksAction); - } - - return actions; -} - -void TaskPoolWidget::newTask() -{ - if (auto action = dynamic_cast<QAction*>(sender())) - { - TaskType taskType = action->data().toString(); - - if (taskType != TaskType::Undefined) - grinder()->taskController().newTask(taskType); - } -} - -void TaskPoolWidget::removeAllTasks() -{ - grinder()->taskController().removeAllTasks(); - updateActions(); -} - -void TaskPoolWidget::updateActions() -{ - _removeAllTasksAction->setEnabled(!grinder()->project().taskPool().tasks().empty()); -} - -TaskWidget* TaskPoolWidget::findTaskWidget(const Task* task) const -{ - for (auto widget : _taskWidgets) - { - if (widget->task() == task) - return widget; - } - - return nullptr; -} +/****************************************************************************** + * File: TaskPoolWidget.cpp + * Date: 01.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "TaskPoolWidget.h" +#include "TaskWidget.h" +#include "core/GrinderApplication.h" +#include "task/TaskCatalog.h" +#include "ui/UIUtils.h" +#include "res/Resources.h" + +TaskPoolWidget::TaskPoolWidget(QWidget* parent) : MetaWidget(parent), + _layout{new QVBoxLayout{this}} +{ + // Create tasks actions + _newTaskAction = UIUtils::createAction(this, "&New task", FILE_ICON_ADD, nullptr, "Add a new task", "", Qt::WidgetShortcut, nullptr, true); + _removeAllTasksAction = UIUtils::createAction(this, "Remove all tasks", "", SLOT(removeAllTasks()), "Remove all tasks"); + + // Set up the new task menu + for (auto type : TaskCatalog::getTypes()) + { + if (!TaskCatalog::isUserCreatable(type)) + continue; + + auto action = _newTaskMenu.addAction(type + " task"); + action->setData(type); + + connect(action, SIGNAL(triggered(bool)), this, SLOT(newTask())); + } + + _newTaskAction->setMenu(&_newTaskMenu); + + // Add a spacer item to align all tasks + _layout->setContentsMargins(0, 0, 0, 0); + _layout->setSpacing(0); + _layout->addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}); + + updateActions(); +} + +void TaskPoolWidget::addTask(const std::shared_ptr<Task>& task) +{ + if (!findTaskWidget(task.get())) + { + // Create a new task widget + auto widget = new TaskWidget{task, this}; + _layout->insertWidget(_taskWidgets.size(), widget); + + _taskWidgets.push_back(widget); + + updateActions(); + } +} + +void TaskPoolWidget::removeTask(const std::shared_ptr<Task>& task) +{ + // Remove the task widget + if (auto widget = findTaskWidget(task.get())) + { + _taskWidgets.erase(std::remove(_taskWidgets.begin(), _taskWidgets.end(), widget), _taskWidgets.end()); + + widget->hide(); + delete widget; + + updateActions(); + } +} + +std::vector<QAction*> TaskPoolWidget::getActions(MetaWidget::AddActionsMode mode) const +{ + std::vector<QAction*> actions; + + actions.push_back(_newTaskAction); + + if (mode == AddActionsMode::ContextMenu) + { + actions.push_back(nullptr); + actions.push_back(_removeAllTasksAction); + } + + return actions; +} + +void TaskPoolWidget::newTask() +{ + if (auto action = dynamic_cast<QAction*>(sender())) + { + TaskType taskType = action->data().toString(); + + if (taskType != TaskType::Undefined) + grinder()->taskController().newTask(taskType); + } +} + +void TaskPoolWidget::removeAllTasks() +{ + grinder()->taskController().removeAllTasks(); + updateActions(); +} + +void TaskPoolWidget::updateActions() +{ + _removeAllTasksAction->setEnabled(!grinder()->project().taskPool().tasks().empty()); +} + +TaskWidget* TaskPoolWidget::findTaskWidget(const Task* task) const +{ + for (auto widget : _taskWidgets) + { + if (widget->task() == task) + return widget; + } + + return nullptr; +} diff --git a/Grinder/ui/task/TaskWidget.cpp b/Grinder/ui/task/TaskWidget.cpp index f486aa0..2756366 100644 --- a/Grinder/ui/task/TaskWidget.cpp +++ b/Grinder/ui/task/TaskWidget.cpp @@ -1,177 +1,188 @@ -/****************************************************************************** - * File: TaskWidget.cpp - * Date: 03.11.2018 - *****************************************************************************/ - -#include "Grinder.h" -#include "TaskWidget.h" -#include "ui_TaskWidget.h" -#include "core/GrinderApplication.h" -#include "task/Task.h" -#include "ui/dlg/TextViewerDialog.h" -#include "res/Resources.h" - -TaskWidget::TaskWidget(const std::shared_ptr<Task>& task, QWidget *parent) : QWidget(parent), - ui{new Ui::TaskWidget}, _task{task} -{ - if (!task) - throw std::invalid_argument{_EXCPT("task may not be null")}; - - setupUi(); - - // Update the UI whenever the task has changed - if (auto task = _task.lock()) // Make sure that the underlying task still exists - connect(task.get(), &Task::taskUpdated, this, &TaskWidget::updateUi); -} - -TaskWidget::~TaskWidget() -{ - delete ui; -} - -void TaskWidget::showEvent(QShowEvent* event) -{ - QWidget::showEvent(event); - updateUi(); -} - -void TaskWidget::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - updateUi(); -} - -void TaskWidget::setupUi() -{ - ui->setupUi(this); - - QColor clrBorder = QPalette{}.color(QPalette::Dark); - ui->line->setStyleSheet(QString{"color: %1; margin-bottom: -1px;"}.arg(clrBorder.name())); - - updateUi(); -} - -void TaskWidget::updateUi() -{ - static auto setEllidedText = [](QLabel* label, QString text) { - text.replace("\t", ""); - - QFontMetrics metrics(label->font()); - int width = label->width() - 2; - QString clippedText = metrics.elidedText(text, Qt::ElideRight, width); - - label->setText(clippedText); - label->setToolTip(text); - }; - - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - setEllidedText(ui->lblTitle, task->getName()); - - auto getStatusStyleSheet = [](QColor color) { return QString{"background: %1; border: 1px solid %2; border-radius: 10px; margin-top: 2px; margin-bottom: 2px;"}.arg(color.name()).arg(color.darker(130).name()); }; - - if (task->isRunning()) - { - setEllidedText(ui->lblMessage, task->getMessage()); - - if (task->isPaused()) - { - ui->lblStatus->setText("Paused"); - ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{255, 200, 120})); - } - else - { - ui->lblStatus->setText("Running"); - ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{140, 220, 255})); - } - } - else - { - switch (task->getResult()) - { - case Task::Result::Succeeded: - setEllidedText(ui->lblMessage, "The task succeeded"); - ui->lblStatus->setText("Succeeded"); - ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{120, 255, 120})); - break; - - case Task::Result::Failed: - setEllidedText(ui->lblMessage, "The task failed"); - ui->lblStatus->setText("Failed"); - ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{255, 120, 120})); - break; - - default: - setEllidedText(ui->lblMessage, ""); - ui->lblStatus->setText("Stopped"); - ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{190, 190, 190})); - break; - } - } - - ui->progressBar->setEnabled(task->getCapabilities().testFlag(Task::Capability::HasProgress)); - ui->progressBar->setValue(task->getProgress() * 100.0f); - - ui->btnStart->setEnabled(!task->isRunning() || task->getCapabilities().testFlag(Task::Capability::CanBePaused)); - ui->btnStart->setIcon(QIcon{task->isRunning() && !task->isPaused() ? FILE_ICON_PAUSE : FILE_ICON_START}); - ui->btnStart->setToolTip(task->isRunning() && !task->isPaused() ? "Pause task" : (task->isRunning() ? "Resume task" : "Start task")); - ui->btnStop->setEnabled(task->isRunning()); - ui->btnRefresh->setEnabled(task->isRunning() && task->getCapabilities().testFlag(Task::Capability::CanBeRefreshed)); - - ui->btnConfigure->setEnabled(!task->isRunning()); - ui->btnDelete->setEnabled(!task->isRunning()); - } -} - -void TaskWidget::on_btnStart_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - if (task->isRunning()) - grinder()->taskController().pauseTask(task.get(), !task->isPaused()); - else - grinder()->taskController().startTask(task.get()); - } -} - -void TaskWidget::on_btnStop_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - if (task->isRunning()) - grinder()->taskController().stopTask(task.get()); - } -} - -void TaskWidget::on_btnRefresh_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - if (task->isRunning()) - grinder()->taskController().refreshTask(task.get()); - } -} - -void TaskWidget::on_btnViewLog_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - TextViewerDialog::viewText(nullptr, "View task log", QString{"Log of task '%1':"}.arg(task->getName()), task->getMessageLog().join("\n"), "No log available"); -} - -void grndr::TaskWidget::on_btnConfigure_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - if (!task->isRunning()) - grinder()->taskController().configureTask(task.get()); - } -} - -void TaskWidget::on_btnDelete_clicked() -{ - if (auto task = _task.lock()) // Make sure that the underlying task still exists - { - // Use a timer to give Qt a chance to finish handling the click event before removing this widget - QTimer::singleShot(0, [task] { grinder()->taskController().removeTask(task.get()); }); - } -} +/****************************************************************************** + * File: TaskWidget.cpp + * Date: 03.11.2018 + *****************************************************************************/ + +#include "Grinder.h" +#include "TaskWidget.h" +#include "ui_TaskWidget.h" +#include "core/GrinderApplication.h" +#include "task/Task.h" +#include "ui/dlg/TextViewerDialog.h" +#include "res/Resources.h" + +TaskWidget::TaskWidget(const std::shared_ptr<Task>& task, QWidget *parent) : QWidget(parent), + ui{new Ui::TaskWidget}, _task{task} +{ + if (!task) + throw std::invalid_argument{_EXCPT("task may not be null")}; + + setupUi(); + + // Update the UI whenever the task has changed + if (auto task = _task.lock()) // Make sure that the underlying task still exists + connect(task.get(), &Task::taskUpdated, this, &TaskWidget::updateUi); +} + +TaskWidget::~TaskWidget() +{ + delete ui; +} + +void TaskWidget::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + updateUi(); +} + +void TaskWidget::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + updateUi(); +} + +void TaskWidget::setupUi() +{ + ui->setupUi(this); + + QColor clrBorder = QPalette{}.color(QPalette::Dark); + ui->line->setStyleSheet(QString{"color: %1; margin-bottom: -1px;"}.arg(clrBorder.name())); + + updateUi(); +} + +void TaskWidget::updateUi() +{ + static auto setEllidedText = [](QLabel* label, QString text) { + text.replace("\t", ""); + + QFontMetrics metrics(label->font()); + int width = label->width() - 2; + QString clippedText = metrics.elidedText(text, Qt::ElideRight, width); + + label->setText(clippedText); + label->setToolTip(text); + }; + + // Update texts, colors etc. + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + setEllidedText(ui->lblTitle, task->getName()); + + auto getStatusStyleSheet = [](QColor color) { return QString{"background: %1; border: 1px solid %2; border-radius: 10px; margin-top: 2px; margin-bottom: 2px;"}.arg(color.name()).arg(color.darker(130).name()); }; + + if (task->isRunning()) + { + setEllidedText(ui->lblMessage, task->getMessage()); + + if (task->isPaused()) + { + ui->lblStatus->setText("Paused"); + ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{255, 200, 120})); + } + else + { + ui->lblStatus->setText("Running"); + ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{140, 220, 255})); + } + } + else + { + switch (task->getResult()) + { + case Task::Result::Succeeded: + setEllidedText(ui->lblMessage, "The task succeeded"); + ui->lblStatus->setText("Succeeded"); + ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{120, 255, 120})); + break; + + case Task::Result::Failed: + setEllidedText(ui->lblMessage, "The task failed"); + ui->lblStatus->setText("Failed"); + ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{255, 120, 120})); + break; + + default: + setEllidedText(ui->lblMessage, ""); + ui->lblStatus->setText("Stopped"); + ui->lblStatus->setStyleSheet(getStatusStyleSheet(QColor{190, 190, 190})); + break; + } + } + + ui->lblTaskInfo->setText(task->getInfo()); + + // Update control states + ui->progressBar->setEnabled(task->getCapabilities().testFlag(Task::Capability::HasProgress)); + ui->progressBar->setValue(task->getProgress() * 100.0f); + + ui->btnStart->setEnabled(!task->isRunning() || task->getCapabilities().testFlag(Task::Capability::CanBePaused)); + ui->btnStart->setVisible(task->getCapabilities().testFlag(Task::Capability::UserControllable)); + ui->btnStart->setIcon(QIcon{task->isRunning() && !task->isPaused() ? FILE_ICON_PAUSE : FILE_ICON_START}); + ui->btnStart->setToolTip(task->isRunning() && !task->isPaused() ? "Pause task" : (task->isRunning() ? "Resume task" : "Start task")); + ui->btnStop->setEnabled(task->isRunning()); + ui->btnStop->setVisible(task->getCapabilities().testFlag(Task::Capability::UserControllable)); + ui->btnRefresh->setEnabled(task->isRunning() && task->getCapabilities().testFlag(Task::Capability::CanBeRefreshed)); + ui->btnRefresh->setVisible(task->getCapabilities().testFlag(Task::Capability::UserControllable)); + + ui->btnConfigure->setEnabled(!task->isRunning()); + ui->btnConfigure->setVisible(task->getCapabilities().testFlag(Task::Capability::UserControllable)); + ui->btnDelete->setEnabled(!task->isRunning()); + } +} + +void TaskWidget::on_btnStart_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + if (task->getCapabilities().testFlag(Task::Capability::UserControllable)) + { + if (task->isRunning()) + grinder()->taskController().pauseTask(task.get(), !task->isPaused()); + else + grinder()->taskController().startTask(task.get()); + } + } +} + +void TaskWidget::on_btnStop_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + if (task->isRunning() && task->getCapabilities().testFlag(Task::Capability::UserControllable)) + grinder()->taskController().stopTask(task.get()); + } +} + +void TaskWidget::on_btnRefresh_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + if (task->isRunning() && task->getCapabilities().testFlag(Task::Capability::UserControllable)) + grinder()->taskController().refreshTask(task.get()); + } +} + +void TaskWidget::on_btnViewLog_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + TextViewerDialog::viewText(nullptr, "View task log", QString{"Log of task '%1':"}.arg(task->getName()), task->getMessageLog().join("\n"), "No log available"); +} + +void grndr::TaskWidget::on_btnConfigure_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + if (!task->isRunning() && task->getCapabilities().testFlag(Task::Capability::UserControllable)) + grinder()->taskController().configureTask(task.get()); + } +} + +void TaskWidget::on_btnDelete_clicked() +{ + if (auto task = _task.lock()) // Make sure that the underlying task still exists + { + // Use a timer to give Qt a chance to finish handling the click event before removing this widget + QTimer::singleShot(0, [task] { grinder()->taskController().removeTask(task.get()); }); + } +} diff --git a/Grinder/ui/task/TaskWidget.ui b/Grinder/ui/task/TaskWidget.ui index d208c6e..419f597 100644 --- a/Grinder/ui/task/TaskWidget.ui +++ b/Grinder/ui/task/TaskWidget.ui @@ -1,315 +1,325 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>TaskWidget</class> - <widget class="QWidget" name="TaskWidget"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>418</width> - <height>126</height> - </rect> - </property> - <property name="windowTitle"> - <string>Form</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <property name="spacing"> - <number>0</number> - </property> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QWidget" name="widget" native="true"> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QWidget" name="widget_3" native="true"> - <layout class="QGridLayout" name="gridLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>3</number> - </property> - <item row="0" column="0" rowspan="2" colspan="2"> - <widget class="QLabel" name="lblTitle"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>TextLabel</string> - </property> - </widget> - </item> - <item row="0" column="2" rowspan="3"> - <widget class="QLabel" name="lblStatus"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>90</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="styleSheet"> - <string notr="true">border: 1px solid black; -border-radius: 5px;</string> - </property> - <property name="text"> - <string>TextLabel</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item row="2" column="0" colspan="2"> - <widget class="QLabel" name="lblMessage"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>TextLabel</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QProgressBar" name="progressBar"> - <property name="value"> - <number>24</number> - </property> - </widget> - </item> - <item> - <widget class="QWidget" name="widget_2" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>6</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QToolButton" name="btnViewLog"> - <property name="toolTip"> - <string>View the task log</string> - </property> - <property name="statusTip"> - <string>View the task log</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>View log</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/basic-text-format.png</normaloff>:/icons/icons/basic-text-format.png</iconset> - </property> - <property name="toolButtonStyle"> - <enum>Qt::ToolButtonIconOnly</enum> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnConfigure"> - <property name="toolTip"> - <string>Configure this task</string> - </property> - <property name="statusTip"> - <string>Configure this task</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/gear.png</normaloff>:/icons/icons/gear.png</iconset> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnDelete"> - <property name="toolTip"> - <string>Delete this task</string> - </property> - <property name="statusTip"> - <string>Delete this task</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/delete.png</normaloff>:/icons/icons/delete.png</iconset> - </property> - </widget> - </item> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>239</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QToolButton" name="btnRefresh"> - <property name="toolTip"> - <string>Refresh this task</string> - </property> - <property name="statusTip"> - <string>Refresh this task</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/arrows-circle.png</normaloff>:/icons/icons/arrows-circle.png</iconset> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnStart"> - <property name="toolTip"> - <string>Start/pause this task</string> - </property> - <property name="statusTip"> - <string>Start/pause this task</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/start.png</normaloff>:/icons/icons/start.png</iconset> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="btnStop"> - <property name="toolTip"> - <string>Stop this task</string> - </property> - <property name="statusTip"> - <string>Stop this task</string> - </property> - <property name="styleSheet"> - <string notr="true">QToolButton { - padding: 5px -} -</string> - </property> - <property name="text"> - <string>...</string> - </property> - <property name="icon"> - <iconset resource="../../res/Grinder.qrc"> - <normaloff>:/icons/icons/stop.png</normaloff>:/icons/icons/stop.png</iconset> - </property> - </widget> - </item> - </layout> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="Line" name="line"> - <property name="frameShadow"> - <enum>QFrame::Plain</enum> - </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - </layout> - </widget> - <resources> - <include location="../../res/Grinder.qrc"/> - </resources> - <connections/> -</ui> +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>TaskWidget</class> + <widget class="QWidget" name="TaskWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>418</width> + <height>126</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="widget_3" native="true"> + <layout class="QGridLayout" name="gridLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>3</number> + </property> + <item row="0" column="0" rowspan="2" colspan="2"> + <widget class="QLabel" name="lblTitle"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + <item row="0" column="2" rowspan="3"> + <widget class="QLabel" name="lblStatus"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>90</width> + <height>0</height> + </size> + </property> + <property name="font"> + <font> + <weight>75</weight> + <bold>true</bold> + </font> + </property> + <property name="styleSheet"> + <string notr="true">border: 1px solid black; +border-radius: 5px;</string> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QLabel" name="lblMessage"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QProgressBar" name="progressBar"> + <property name="value"> + <number>24</number> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="widget_2" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="btnViewLog"> + <property name="toolTip"> + <string>View the task log</string> + </property> + <property name="statusTip"> + <string>View the task log</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>View log</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/basic-text-format.png</normaloff>:/icons/icons/basic-text-format.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonIconOnly</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnConfigure"> + <property name="toolTip"> + <string>Configure this task</string> + </property> + <property name="statusTip"> + <string>Configure this task</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/gear.png</normaloff>:/icons/icons/gear.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnDelete"> + <property name="toolTip"> + <string>Delete this task</string> + </property> + <property name="statusTip"> + <string>Delete this task</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/delete.png</normaloff>:/icons/icons/delete.png</iconset> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>239</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="lblTaskInfo"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>TaskInfo</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnRefresh"> + <property name="toolTip"> + <string>Refresh this task</string> + </property> + <property name="statusTip"> + <string>Refresh this task</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/arrows-circle.png</normaloff>:/icons/icons/arrows-circle.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnStart"> + <property name="toolTip"> + <string>Start/pause this task</string> + </property> + <property name="statusTip"> + <string>Start/pause this task</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/start.png</normaloff>:/icons/icons/start.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="btnStop"> + <property name="toolTip"> + <string>Stop this task</string> + </property> + <property name="statusTip"> + <string>Stop this task</string> + </property> + <property name="styleSheet"> + <string notr="true">QToolButton { + padding: 5px +} +</string> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../../res/Grinder.qrc"> + <normaloff>:/icons/icons/stop.png</normaloff>:/icons/icons/stop.png</iconset> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../../res/Grinder.qrc"/> + </resources> + <connections/> +</ui> -- GitLab