From f6c2a9fa75831ac279fe1ee22d91c9db29dc98a8 Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@kit.edu> Date: Fri, 3 Nov 2023 11:07:34 +0100 Subject: [PATCH] Enable Markdown support in display of skill description in Skill Manager Gui --- .../SkillManagerPlugin/CMakeLists.txt | 10 +- .../SkillManagerMonitorWidget.ui | 199 ++++++++++++------ .../SkillManagerMonitorWidgetController.cpp | 48 ++--- .../SkillManagerMonitorWidgetController.h | 3 + .../widgets/SkillDescriptionWidget.cpp | 72 +++++++ .../widgets/SkillDescriptionWidget.h | 27 +++ 6 files changed, 255 insertions(+), 104 deletions(-) create mode 100644 source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp create mode 100644 source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt index a6d2ff63c..113ccd870 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt @@ -10,12 +10,13 @@ set(SOURCES aronTreeWidget/visitors/AronTreeWidgetSetter.cpp aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp aronTreeWidget/visitors/AronTreeWidgetContextMenu.cpp - aronTreeWidget/Data.cpp aronTreeWidget/widgets/CustomWidget.cpp aronTreeWidget/widgets/EditMatrixWidget.cpp aronTreeWidget/widgets/IntEnumWidget.cpp - aronTreeWidget/ListDictHelper.cpp aronTreeWidget/widgets/QuaternionWidget.cpp + aronTreeWidget/widgets/SkillDescriptionWidget.cpp + aronTreeWidget/Data.cpp + aronTreeWidget/ListDictHelper.cpp aronTreeWidget/AronTreeWidgetItem.cpp aronTreeWidget/AronTreeWidgetController.cpp aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.cpp @@ -34,13 +35,14 @@ set(HEADERS aronTreeWidget/visitors/AronTreeWidgetSetter.h aronTreeWidget/visitors/AronTreeWidgetModalCreator.h aronTreeWidget/visitors/AronTreeWidgetContextMenu.h - aronTreeWidget/Data.h aronTreeWidget/widgets/NDArrayHelper.h aronTreeWidget/widgets/EditMatrixWidget.h aronTreeWidget/widgets/CustomWidget.h aronTreeWidget/widgets/IntEnumWidget.h - aronTreeWidget/ListDictHelper.h aronTreeWidget/widgets/QuaternionWidget.h + aronTreeWidget/widgets/SkillDescriptionWidget.h + aronTreeWidget/Data.h + aronTreeWidget/ListDictHelper.h aronTreeWidget/AronTreeWidgetItem.h aronTreeWidget/AronTreeWidgetController.h aronTreeWidget/modal/AronTreeWidgetModal.h diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui index b96d3850e..83e522b9d 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui @@ -20,6 +20,9 @@ <string>SkillManagerMonitorWidget</string> </property> <layout class="QGridLayout" name="gridLayout_3"> + <item row="3" column="2"> + <widget class="QDoubleSpinBox" name="doubleSpinBoxUpdateFreq"/> + </item> <item row="3" column="0"> <widget class="QCheckBox" name="checkBoxAutoUpdate"> <property name="text"> @@ -27,16 +30,6 @@ </property> </widget> </item> - <item row="3" column="3"> - <widget class="QPushButton" name="pushButtonRefreshNow"> - <property name="text"> - <string>Refresh Now</string> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QDoubleSpinBox" name="doubleSpinBoxUpdateFreq"/> - </item> <item row="3" column="1"> <widget class="QLabel" name="label"> <property name="text"> @@ -47,6 +40,13 @@ </property> </widget> </item> + <item row="3" column="3"> + <widget class="QPushButton" name="pushButtonRefreshNow"> + <property name="text"> + <string>Refresh Now</string> + </property> + </widget> + </item> <item row="4" column="0" colspan="4"> <widget class="QSplitter" name="splitter_2"> <property name="enabled"> @@ -190,72 +190,137 @@ <property name="title"> <string>Skill Details</string> </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="8" column="3"> - <widget class="QPushButton" name="pushButtonExecuteSkill"> - <property name="text"> - <string>Request Execution</string> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QSplitter" name="splitter_2"> + <property name="enabled"> + <bool>true</bool> </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QPushButton" name="pushButtonPaste"> - <property name="text"> - <string>Set args from clipboard</string> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QPushButton" name="pushButtonReset"> - <property name="text"> - <string>Reset args to profile</string> - </property> - </widget> - </item> - <item row="3" column="0" colspan="4"> - <widget class="QTreeWidget" name="treeWidgetSkillDetails"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <property name="editTriggers"> - <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> + <property name="childrenCollapsible"> + <bool>false</bool> </property> - <column> - <property name="text"> - <string>Key</string> + <widget class="QWidget" name="groupBoxSkillDetailsTop" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="leftMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>Value</string> + <property name="topMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>Type</string> + <property name="rightMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>defaultValue (hidden in GUI)</string> + <property name="bottomMargin"> + <number>0</number> </property> - </column> - </widget> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="pushButtonCopy"> - <property name="text"> - <string>Copy args to clipboard</string> - </property> + <item> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QPushButton" name="pushButtonPaste"> + <property name="text"> + <string>Set args from clipboard</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QPushButton" name="pushButtonReset"> + <property name="text"> + <string>Reset args to profile</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="4"> + <widget class="QComboBox" name="comboBoxProfiles"> + <item> + <property name="text"> + <string><No Profile selected. Using root></string> + </property> + </item> + </widget> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="pushButtonCopy"> + <property name="text"> + <string>Copy args to clipboard</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="skillDescription" native="true"/> + </item> + </layout> </widget> - </item> - <item row="1" column="1" colspan="3"> - <widget class="QComboBox" name="comboBoxProfiles"> - <item> - <property name="text"> - <string><No Profile selected. Using root></string> - </property> - </item> + <widget class="QWidget" name="groupBoxSkillDetailsBottom" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <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="QTreeWidget" name="treeWidgetSkillDetails"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> + </property> + <column> + <property name="text"> + <string>Key</string> + </property> + </column> + <column> + <property name="text"> + <string>Value</string> + </property> + </column> + <column> + <property name="text"> + <string>Type</string> + </property> + </column> + <column> + <property name="text"> + <string>defaultValue (hidden in GUI)</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonExecuteSkill"> + <property name="text"> + <string>Request Execution</string> + </property> + </widget> + </item> + </layout> + </widget> </widget> </item> </layout> diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp index b35fb56f3..8b88718f9 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp @@ -24,25 +24,22 @@ #include <string> -#include <RobotAPI/libraries/skills/core/Skill.h> - -#include "aronTreeWidget/visitors/AronTreeWidgetConverter.h" -#include "aronTreeWidget/visitors/AronTreeWidgetCreator.h" -#include "aronTreeWidget/visitors/AronTreeWidgetModalCreator.h" - -// modals -#include "aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.h" - -// debug #include <QClipboard> #include <QDoubleSpinBox> +#include <QGridLayout> +#include <QTextBrowser> #include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include <RobotAPI/libraries/skills/core/Skill.h> #include <RobotAPI/libraries/skills/core/SkillExecutionRequest.h> #include "aronTreeWidget/Data.h" +#include "aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.h" +#include "aronTreeWidget/visitors/AronTreeWidgetConverter.h" +#include "aronTreeWidget/visitors/AronTreeWidgetCreator.h" +#include "aronTreeWidget/visitors/AronTreeWidgetModalCreator.h" +#include "aronTreeWidget/widgets/SkillDescriptionWidget.h" -//configSk namespace armarx { QPointer<QDialog> @@ -112,6 +109,12 @@ namespace armarx widget.doubleSpinBoxUpdateFreq->setSingleStep(0.5); widget.doubleSpinBoxUpdateFreq->setSuffix(" Hz"); + skillDescriptionWidget = new SkillDescriptionWidget(); + widget.skillDescription->parentWidget()->layout()->replaceWidget(widget.skillDescription, + skillDescriptionWidget); + widget.skillDescription = skillDescriptionWidget; + + refreshSkillsResultTimer = new QTimer(this); updateTimerFrequency(); refreshSkillsResultTimer->start(); @@ -534,28 +537,7 @@ namespace armarx ARMARX_CHECK(skills.at(*selectedSkill.skillId.providerId).count(selectedSkill.skillId) > 0); auto skillDesc = skills.at(*selectedSkill.skillId.providerId).at(selectedSkill.skillId); - { - auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails, - {QString::fromStdString("Name"), - QString::fromStdString(skillDesc.skillId.skillName)}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } - - { - auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails, - {QString::fromStdString("Description"), - QString::fromStdString(skillDesc.description)}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } - - { - auto it = new QTreeWidgetItem( - widget.treeWidgetSkillDetails, - {QString::fromStdString("Timeout"), - QString::fromStdString(std::to_string(skillDesc.timeout.toMilliSeconds())) + - " ms"}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } + skillDescriptionWidget->setSkillDescription(skillDesc); // select root profile widget.comboBoxProfiles->setCurrentIndex(0); diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h index 1fd2e2f11..9f251bf9e 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h @@ -46,6 +46,8 @@ namespace armarx { + class SkillDescriptionWidget; + class SkillInfoTreeWidgetItem : public QTreeWidgetItem { public: @@ -166,6 +168,7 @@ namespace armarx // others QTimer* refreshSkillsResultTimer; + SkillDescriptionWidget* skillDescriptionWidget = nullptr; // connected flag std::atomic_bool connected = false; diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp new file mode 100644 index 000000000..60f5908c7 --- /dev/null +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp @@ -0,0 +1,72 @@ +#include "SkillDescriptionWidget.h" + +#include <sstream> + +#include <QGridLayout> +#include <QLabel> +#include <QTextEdit> + +#include <ArmarXGui/libraries/ArmarXGuiBase/widgets/cpp-markdown/markdown.h> // ToDo: Move cpp-markdown to own Axii module. + +namespace armarx +{ + + static std::string + markdownToHtml(const std::string& markdownText, size_t spacesPerTab = 2) + { + ::markdown::Document document(spacesPerTab); + document.read(markdownText); + + std::stringstream html; + document.write(html); + return html.str(); + } + + SkillDescriptionWidget::SkillDescriptionWidget(QWidget* parent) : QWidget{parent} + { + QGridLayout* layout = new QGridLayout; + setLayout(layout); + + layout->setMargin(0); + + int row = 0; + int col = 0; + + layout->addWidget(new QLabel("Name:"), row, col++); + name = new QLabel; + layout->addWidget(name, row, col++); + + // Add some padding. + layout->addWidget(new QLabel(), row, col++); + + { + QLabel* label = new QLabel("Timeout:"); + label->setAlignment(Qt::AlignmentFlag::AlignRight); + layout->addWidget(label, row, col++); + } + timeout = new QLabel; + timeout->setAlignment(Qt::AlignmentFlag::AlignRight); + layout->addWidget(timeout, row, col++); + + ++row; + int numCols = col; + col = 0; + + description = new QTextEdit; + description->setReadOnly(true); + layout->addWidget(description, row, col, 1, numCols); + ++row; + } + + void + SkillDescriptionWidget::setSkillDescription(const skills::SkillDescription& desc) + { + name->setText(QString::fromStdString(desc.skillId.skillName)); + description->setHtml(QString::fromStdString(markdownToHtml(desc.description))); + + std::stringstream timeoutStr; + timeoutStr << desc.timeout; + timeout->setText(QString::fromStdString(timeoutStr.str())); + } + +} // namespace armarx diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h new file mode 100644 index 000000000..b58f06d0d --- /dev/null +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h @@ -0,0 +1,27 @@ +#pragma once + +#include <QWidget> + +#include <RobotAPI/libraries/skills/core/SkillDescription.h> + +class QLabel; +class QTextEdit; + +namespace armarx +{ + + class SkillDescriptionWidget : public QWidget + { + public: + SkillDescriptionWidget(QWidget* parent = nullptr); + + void setSkillDescription(const skills::SkillDescription& desc); + + private: + QLabel* name = nullptr; + QTextEdit* description = nullptr; + QLabel* timeout = nullptr; + }; + + +} // namespace armarx -- GitLab