diff --git a/Modules/Forms/include/mitkQuestionWithOptions.h b/Modules/Forms/include/mitkQuestionWithOptions.h index fef2d1ef74..4003ded473 100644 --- a/Modules/Forms/include/mitkQuestionWithOptions.h +++ b/Modules/Forms/include/mitkQuestionWithOptions.h @@ -1,50 +1,79 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkQuestionWithOptions_h #define mitkQuestionWithOptions_h #include #include #include namespace mitk::Forms { + /** \brief Base class for questions with options to choose from as response. + * + * This base class represents both, questions with mutually exclusive options as well as + * questions with multiple answer options. Derived classes should use using-declarations + * to change the visibility of the respective protected member functions to public, i.e., + * SetResponse() vs. AddResponse() and RemoveResponse(). + */ class MITKFORMS_EXPORT QuestionWithOptions : public Question { public: ~QuestionWithOptions() override; std::vector GetResponsesAsStrings() const override; void ClearResponses() override; bool IsComplete() const override; + /** \brief Add a non-exclusive option as possible answer to the question. + * + * The option is appended to the list of already existing options. + * + * \return The index of the option + */ size_t AddOption(const std::string& option); + std::vector GetOptions() const; protected: + /** \brief Add one of the possible answer options to the responses. + * + * Indexes to responses are inserted in ascending order. + * + * \sa AddOption() + */ void AddResponse(size_t i); + + /** \brief Remove one of the already given responses. + * + * \sa AddOption(), AddResponse() + */ void RemoveResponse(size_t i); + /** \brief Set one of the possible answer options as the single response. + * + * \note This will remove any responses added by AddResponse(). + */ virtual void SetResponse(size_t i); private: std::vector m_Options; std::set m_Responses; }; MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, QuestionWithOptions& q); MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const QuestionWithOptions& q); } #endif diff --git a/Modules/Forms/include/mitkQuestionWithOtherOption.h b/Modules/Forms/include/mitkQuestionWithOtherOption.h index 9c560a84e0..5850dcfb9d 100644 --- a/Modules/Forms/include/mitkQuestionWithOtherOption.h +++ b/Modules/Forms/include/mitkQuestionWithOtherOption.h @@ -1,52 +1,79 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkQuestionWithOtherOption_h #define mitkQuestionWithOtherOption_h #include #include namespace mitk::Forms { + /** \brief Base class for questions with options that may also have a free text option. + * + * This base class is an extension of QuestionWithOptions representing questions that + * may also have a free text option, typically labeled "Other" in forms/surveys. + * + * Just like with QuestionWithOptions, depending on the exclusivity of the answer + * options, use using-declarations in derived classes to change the visibility of + * the respective protected member functions to public, i.e., SetOtherResponse() + * vs. AddOtherResponse() and RemoveOtherResponse(). + */ class MITKFORMS_EXPORT QuestionWithOtherOption : public QuestionWithOptions { public: QuestionWithOtherOption(); ~QuestionWithOtherOption() override; std::vector GetResponsesAsStrings() const override; void ClearResponses() override; bool IsComplete() const override; + /** \brief Query whether this question actually has an "Other" option. + */ bool HasOtherOption() const; + + /** \brief Switch on the "Other" option. + * + * By default, a question does not have an "Other" option. + */ void EnableOtherOption(); protected: void SetResponse(size_t i) override; + /** \brief Add the free text given as "Other" option to the list of responses. + * + * \note A question can only have a single "Other" response. Consequtive calls + * to this method will override the previously set "Other" response. + */ void AddOtherResponse(const std::string& response); + + /** \brief Remove the "Other" response from the list of already given responses. + */ void RemoveOtherResponse(); + /** \brief Set the "Other" response as single exclusive response to this question. + */ void SetOtherResponse(const std::string& response); private: bool m_HasOtherOption; std::optional m_OtherResponse; }; MITKFORMS_EXPORT void from_json(const nlohmann::ordered_json& j, QuestionWithOtherOption& q); MITKFORMS_EXPORT void to_json(nlohmann::ordered_json& j, const QuestionWithOtherOption& q); } #endif diff --git a/Modules/FormsUI/include/QmitkQuestionWidget.h b/Modules/FormsUI/include/QmitkQuestionWidget.h index 2491417ab7..28b61f1500 100644 --- a/Modules/FormsUI/include/QmitkQuestionWidget.h +++ b/Modules/FormsUI/include/QmitkQuestionWidget.h @@ -1,53 +1,160 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkQuestionWidget_h #define QmitkQuestionWidget_h #include #include class QVBoxLayout; class QLabel; namespace mitk::Forms { class Question; } +/** \brief Abstract base class for all types of question widgets used in a QmitkForm. + * + * This class manages GUI elements common to all questions like labels for the question text and + * a reminder that a response might be required. All specific GUI elements of derived classes + * must be put into a layout and inserted by calling InsertLayout(). + * + * Please make sure to read the full documentation of the pure virtual functions in particular to + * fully understand implications and requirements. + */ class MITKFORMSUI_EXPORT QmitkQuestionWidget : public QFrame { Q_OBJECT public: explicit QmitkQuestionWidget(QWidget* parent = nullptr); ~QmitkQuestionWidget() override; + /** \name Pure virtual functions + * + * QmitkQuestionWidget is an abstract base class. Derive from this class to add a widget for a + * certain type of Question and override the following pure virtual functions. Please read the + * full documentation for all of these functions to fully understand implications and requirements. + * + * Do not forget to register any new question widget by calling mitk::Forms::UI::IQuestionWidgetFactory::Register() + * like it is done in this module's activator class. + * + * \sa mitk::Forms::UI::IQuestionWidgetFactory + * \{ + */ + + /** \brief Create a new instance of the derived question widget class type. + * + * This method is mainly used by mitk::Forms::UI::IQuestionWidgetFactory to create + * new instances from registered prototype instances. + * + * \code{.cpp} + * QmitkQuestionWidget* QmitkRhetoricalQuestionWidget::CreateAnother(QWidget* parent) const + * { + * return new RhetoricalQuestionWidget(parent); + * } + * \endcode + */ virtual QmitkQuestionWidget* CreateAnother(QWidget* parent = nullptr) const = 0; + + /** \brief Get the question associated with this widget. + * + * \code{.cpp} + * Question* QmitkRhetoricalQuestionWidget::GetQuestion() const + * { + * // In class declaration: mitk::Forms::RhetoricalQuestion* m_Question; + * return m_Question; + * } + * \endcode + * + * \sa SetQuestion() + */ virtual mitk::Forms::Question* GetQuestion() const = 0; + + /** \brief Initialize the widget based on the given question. + * + * This method is rarely used explicitly since it is automatically called by + * mitk::Forms::UI::IQuestionWidgetFactory::Create(). + * + * You are excepted to throw an mitk::Exception if the Question type does not match + * the expectations of the widget. + * + * \note It is required to call this base class method at the beginning of the derived + * method. + * + * \code{.cpp} + * void QmitkRhetoricalQuestionWidget::SetQuestion(Question* question) + * { + * QmitkQuestionWidget::SetQuestion(question); + * + * auto rhetoricalQuestion = dynamic_cast(question); + * + * if (rhetoricalQuestion == nullptr) + * mitkThrow() << "QmitkRhetoricalQuestionWidget only accepts RhetoricalQuestion as question type!"; + * + * m_Question = rhetoricalQuestion; + * } + * \endcode + */ virtual void SetQuestion(mitk::Forms::Question* question) = 0; + + /** \brief Reset the state of the GUI as if no interaction would have been happened yet. + * + * \note It is required to call this base class method at the end of the derived method. + * + * \code{.cpp} + * void QmitkRhetoricalQuestionWidget::Reset() + * { + * // It's a rhetorical question... nothing specific to be reset. + * QmitkQuestionWidget::Reset(); // Nevertheless, this is required at the end! + * } + * \endcode + */ virtual void Reset() = 0; + /**\}*/ + void SetRequirementVisible(bool visible); void ShowRequirement(); void HideRequirement(); protected: + /** \brief Insert a layout containing all GUI elements specific to the derived question widget type. + * + * This method is typically called from the constructor of a derived class after all GUI elements + * have been set up and organized in a layout. + * + * \code{.cpp} + * QmitkRhetoricalQuestionWidget::QmitkRhetoricalQuestionWidget(QWidget* parent) + * : QmitkQuestionWidget(parent), + * m_Question(nullptr), + * m_Layout(new QVBoxLayout), + * m_Label(new QLabel) + * { + * m_Label->setText("You know the answer... we all do."); + * m_Layout->addWidget(m_Label); + * + * this->InsertLayout(m_Layout); + * } + * \endcode + */ void InsertLayout(QLayout* layout); private: QVBoxLayout* m_Layout; QLabel* m_QuestionLabel; QLabel* m_RequiredLabel; }; #endif diff --git a/Modules/FormsUI/include/mitkIQuestionWidgetFactory.h b/Modules/FormsUI/include/mitkIQuestionWidgetFactory.h index e5f86d6659..284946de2d 100644 --- a/Modules/FormsUI/include/mitkIQuestionWidgetFactory.h +++ b/Modules/FormsUI/include/mitkIQuestionWidgetFactory.h @@ -1,42 +1,65 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkIQuestionWidgetFactory_h #define mitkIQuestionWidgetFactory_h #include #include #include namespace mitk::Forms { class Question; namespace UI { + /** \brief Register widgets for questions. + * + * This is a service interface. Obtain a pointer to its single instance via GetInstance(). + * + * Each QmitkQuestionWidget subclass must be registered by calling Register(), which is typically done + * in the module activator class. After QmitkQuestionWidget subclasses are registered, instances of them + * can be created with Create() based on their matching type of Question. + */ class MITKFORMSUI_EXPORT IQuestionWidgetFactory { public: + /** \brief Obtain a pointer to the single instance of this service. + */ static IQuestionWidgetFactory* GetInstance(); virtual ~IQuestionWidgetFactory(); + /** \brief Register a QmitkQuestionWidget subclass for a certain Question type string. + * + * The service takes over ownership of the passed QmitkQuestionWidget pointer. + * + * \sa Question::GetType() + */ virtual void Register(const std::string& questionType, QmitkQuestionWidget* widgetPrototype) = 0; + + /** \brief Create an instance of a matching QmitkQuestionWidget subclass for a certain question. + * + * The given question is passed to QmitkQuestionWidget::SetQuestion(). + * + * \sa QmitkQuestionWidget::CreateAnother() + */ virtual QmitkQuestionWidget* Create(Question* question, QWidget* parent = nullptr) const = 0; }; } } MITK_DECLARE_SERVICE_INTERFACE(mitk::Forms::UI::IQuestionWidgetFactory, "org.mitk.Forms.UI.IQuestionWidgetFactory") #endif