diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 21caed41e4dcd3f725ca26fa95435a37077b8115..6a760ec649bd39ee2f7642b5a58eaf3343631394 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -34,7 +34,7 @@ "ms-vscode.cmake-tools", "ms-python.python", - // remove to not show cell tags in editor + // remove VS Code plugin to not show cell tags in editor "-ms-toolsai.vscode-jupyter-cell-tags" ] } diff --git a/catkin_ws/src/learn_environment/CMakeLists.txt b/catkin_ws/src/learn_environment/CMakeLists.txt index 31ba7301f3009b1d1d01fa894fba88ae51a10f18..bac241fb2903b836c996015279875fa6c2c52384 100644 --- a/catkin_ws/src/learn_environment/CMakeLists.txt +++ b/catkin_ws/src/learn_environment/CMakeLists.txt @@ -80,7 +80,7 @@ set(${PROJECT_NAME}_UIS ) set(${PROJECT_NAME}_QRC - resources.qrc + resource/resources.qrc ) # Create header from ui file (uic) diff --git a/catkin_ws/src/learn_environment/docs/for_developers/define_tasks.md b/catkin_ws/src/learn_environment/docs/for_developers/define_tasks.md index 9f8fedc629f48c8e1c2dc57051da36644c0faada..afe5de95d36f6efb76c250b44854a9cb76b72cec 100644 --- a/catkin_ws/src/learn_environment/docs/for_developers/define_tasks.md +++ b/catkin_ws/src/learn_environment/docs/for_developers/define_tasks.md @@ -159,7 +159,6 @@ As tasks are divided into tasks and subtasks, you need to define some fields in "tasks": [ { "title": "ROS Basics", - "description": "Learn the fundamentals of ROS (Robot Operating System), including node creation, topic subscription, and communication between nodes.", "folder": "/ROS/Task1", "topic": "ROS", "difficulty": "beginner", @@ -197,7 +196,6 @@ The configuration will be displayed like this: | Field | Description | Optional/Required | Default Value | |----------------|------------------------------------------------------|-------------------|---------------| | `title` | The title of the task | Required | N/A | -| `description` | A short description of the task | Required | N/A | | `folder` | The relative folder path for all subtask files.<br>Format: `/YOUR_TASK_TOPIC/YOUR_TASK_NAME`, the `/` are important.<br><br> Your solution notebook will then need to be located in `/task_pool/solution_scripts/YOUR_TASK_TOPIC/YOUR_TASK_NAME` <br> Your evaluation script will then need to be located in `/task_pool/evaluation_scripts/YOUR_TASK_TOPIC/YOUR_TASK_NAME` | Required | N/A | | `topic` | The topic related to the task (all defined Topics are stored in the `/task_pool/task_definitions.json` file. Learn more on how to define topics and what they do [here](./define_topics.md).) | Required | N/A | | `difficulty` | The difficulty level of the task (e.g., beginner, intermediate, advanced)<br>All defined difficulty levels are stored in the `/task_pool/difficulty_levels.json` file. Learn more on how to define difficulty levels and what they do [here](./define_difficulty_levels.md). | Required | N/A | diff --git a/catkin_ws/src/learn_environment/include/learn_environment/folder_structure_constants.hpp b/catkin_ws/src/learn_environment/include/learn_environment/folder_structure_constants.hpp index 2ab3417b3d8aead2a0431b9793cef6c69147ff72..73d8029d5d46ec9f587917cb82e0c8119282b4d6 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/folder_structure_constants.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/folder_structure_constants.hpp @@ -26,6 +26,24 @@ namespace FolderStructureConstants { */ inline const QString RESET_ROBOT_SCRIPT_PATH = "/task_pool/reset_robot.py"; + /** + * @var FolderStructureConstants::TASK_DEFINITIONS_PATH + * @brief Path to the task definitions json. + */ + inline const QString TASK_DEFINITIONS_PATH = ":/task_pool/task_definitions.json"; + + /** + * @var FolderStructureConstants::TOPIC_DEFINITIONS_PATH + * @brief Path to the topic definitions json. + */ + inline const QString TOPIC_DEFINITIONS_PATH = ":/task_pool/topic_definitions.json"; + + /** + * @var FolderStructureConstants::TOPIC_DEFINITIONS_PATH + * @brief Path to the difficulty definitions json. + */ + inline const QString DIFFICULTY_LEVELS_DEFINITION_PATH = ":/task_pool/difficulty_levels.json"; + /** * @var FolderStructureConstants::SOLUTION_SCRIPTS_SOURCE_PATH * @brief Path to the solution scripts source directory. diff --git a/catkin_ws/src/learn_environment/include/learn_environment/learn_environment.hpp b/catkin_ws/src/learn_environment/include/learn_environment/learn_environment.hpp index c10c46ec7de1b4149eb1673454e464ebc144cc95..a1af02ae451c6f444e0099e5c89520a0f65d8253 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/learn_environment.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/learn_environment.hpp @@ -3,7 +3,6 @@ #include <rviz/panel.h> #include <ui_learn_environment.h> -#include "sidebar.hpp" #include "task_manager.hpp" #include "task_ui.hpp" #include "notebook_converter.hpp" @@ -54,15 +53,8 @@ public: */ virtual void save(rviz::Config config) const override; -private Q_SLOTS: - /** - * @brief Toggles the visibility of the sidebar. - */ - void toggleSidebarVisibility(); - private: Ui::LearnEnvironment *ui; ///< Pointer to the UI object. - Sidebar *sidebar; ///< Pointer to the Sidebar object. TaskManager *taskManager; ///< Pointer to the TaskManager object. TaskUI *taskUI; ///< Pointer to the TaskUI object. QProcess *process; ///< Pointer to the QProcess object. @@ -74,11 +66,6 @@ private: * @brief Initializes the UI and the startup logic. */ void initialize(); - - /** - * @brief Sets up the splitter and layout for the main window. - */ - void setupSplitterAndLayout(); }; #endif // LEARN_ENVIRONMENT_HPP \ No newline at end of file diff --git a/catkin_ws/src/learn_environment/include/learn_environment/subtask_item.hpp b/catkin_ws/src/learn_environment/include/learn_environment/subtask_item.hpp index 4010fbde9cc5ad6708daf3b5f8bc292352fa2f03..139b1444c1d4aa141ed30905143fabe442bf3a1d 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/subtask_item.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/subtask_item.hpp @@ -3,6 +3,7 @@ #include "task.hpp" #include "task_manager.hpp" +#include "execute_frame.hpp" #include <QWidget> #include <QPushButton> diff --git a/catkin_ws/src/learn_environment/include/learn_environment/task.hpp b/catkin_ws/src/learn_environment/include/learn_environment/task.hpp index 14bb0d18792efda80ce2d58628b81a2f95fd3e24..168003c21218a8b9e4e58e6a144fa785d4cf02f8 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/task.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/task.hpp @@ -60,7 +60,6 @@ struct Subtask { */ struct Task { QString title; ///< The title of the task. - QString description; ///< The description of the task. QString folder; ///< The folder path of the task. QString difficulty; ///< The difficulty level of the task. QString topic; ///< The topic of the task. diff --git a/catkin_ws/src/learn_environment/include/learn_environment/task_manager.hpp b/catkin_ws/src/learn_environment/include/learn_environment/task_manager.hpp index 2c441c3b839afa53ce471886cdd4f9b9963c9473..78d4ae3efb24b2661576c0236ec69bb464d2252c 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/task_manager.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/task_manager.hpp @@ -3,11 +3,8 @@ #include "task.hpp" #include "task_executor.hpp" -#include "execute_frame.hpp" #include <QObject> -#include <QPushButton> -#include <QFrame> #include <QVector> #include <QSharedPointer> @@ -28,11 +25,9 @@ public: /** * @brief Constructs a TaskManager object. * @param taskUI Pointer to the TaskUI object. - * @param nextButton Pointer to the next button. - * @param previousButton Pointer to the previous button. * @param parent Pointer to the parent QObject. */ - TaskManager(TaskUI *taskUI, QPushButton *nextButton, QPushButton *previousButton, QFrame *resetRobotFrame, QObject *parent = nullptr); + TaskManager(TaskUI *taskUI, QObject *parent = nullptr); /** * @brief Starts or stops a subtask. @@ -49,32 +44,33 @@ public: public Q_SLOTS: /** - * @brief Starts or stops a subtask. - * @param subtask Reference to the subtask to be started or stopped. + * @brief Slot for changing to the next task. */ - void startStopSubtask(const Subtask &subtask); + void nextTask(); /** - * @brief Forces the reset of the robot to its initial state. + * @brief Slot for changing to the previous task. */ - void forceResetRobot(); + void previousTask(); -private Q_SLOTS: /** - * @brief Slot for handling the next button click event. + * @brief Selects a task by its index. + * @param index The index of the task to be selected. */ - void onNextButtonClicked(); + void selectTask(int index); /** - * @brief Slot for handling the previous button click event. + * @brief Starts or stops a subtask. + * @param subtask Reference to the subtask to be started or stopped. */ - void onPreviousButtonClicked(); + void startStopSubtask(const Subtask &subtask); /** - * @brief Selects a task by its index. - * @param index The index of the task to be selected. + * @brief Forces the reset of the robot to its initial state. */ - void selectTask(int index); + void forceResetRobot(); + +private Q_SLOTS: /** * @brief Slot for handling the task execution started event. @@ -111,10 +107,6 @@ private Q_SLOTS: private: TaskUI *taskUI; ///< Pointer to the TaskUI object. TaskExecutor *taskExecutor; ///< Pointer to the TaskExecutor object. - QPushButton *nextButton; ///< Pointer to the next button. - QPushButton *previousButton; ///< Pointer to the previous button. - QFrame *resetRobotFrame; ///< Pointer to the reset robot frame. - ExecuteFrame *executeResetRobotFrame; ///< Pointer to the execute reset robot frame. QVector<QSharedPointer<Task>> tasks; ///< Vector of tasks. QVector<Subtask*> queued_and_running_subtasks; ///< Vector of queued and running subtasks. int currentQueueStartSolution = false; ///< Flag indicating whether to start the solution or the users script. diff --git a/catkin_ws/src/learn_environment/include/learn_environment/task_ui.hpp b/catkin_ws/src/learn_environment/include/learn_environment/task_ui.hpp index 120d8d8ab037baa9ce4eb3c02e905058066d651c..4e2d939fea1a766ea89883333e31ad65bcf65c75 100644 --- a/catkin_ws/src/learn_environment/include/learn_environment/task_ui.hpp +++ b/catkin_ws/src/learn_environment/include/learn_environment/task_ui.hpp @@ -4,11 +4,14 @@ #include "sidebar.hpp" #include "task.hpp" #include "task_manager.hpp" +#include "execute_frame.hpp" #include <QObject> #include <QVBoxLayout> #include <QLabel> +#include <QToolButton> #include <QPushButton> +#include <QFrame> /** * @class TaskUI @@ -18,8 +21,7 @@ * related to tasks and subtasks. It interacts with the TaskManager and Sidebar * to provide a cohesive user experience. */ -class TaskUI : public QObject -{ +class TaskUI : public QWidget { Q_OBJECT public: @@ -31,13 +33,16 @@ public: * @param folderLabel Pointer to the QLabel for displaying the folder information. * @param nextButton Pointer to the QPushButton for navigating to the next task. * @param previousButton Pointer to the QPushButton for navigating to the previous task. - * @param sidebar Reference to the Sidebar object. + * @param menuButton Pointer to the QToolButton for opening the sidebar. + * @param resetRobotStartButton Pointer to the QToolButton for resetting the robot. + * @param resetRobotFrame Pointer to the QFrame displaying the reset of the robot. + * @param centralWidget Pointer to the central QWidget for the plugin. * @param parent Pointer to the parent QObject. */ TaskUI(QVBoxLayout *subtaskListLayout, QLabel *mainTitleLabel, QLabel *difficultyLabel, QLabel *folderLabel, QLabel *topicLabel, - QPushButton *nextButton, QPushButton *previousButton, - Sidebar &sidebar, QObject *parent = nullptr); + QPushButton *nextButton, QPushButton *previousButton, QToolButton *menuButton, + QToolButton *resetRobotStartButton, QFrame *resetRobotFrame, QWidget *centralwidget, QWidget *parent = nullptr); /** * @brief Initializes the UI with the loaded tasks. @@ -62,6 +67,23 @@ public: */ void setTaskManager(TaskManager *manager); + /** + * @brief Update the UI to show that the robot reset has been started. + * @param noSubtasksLeft indicator, if subtasks are running after the rest or not. + */ + void startedRobotResetUI(bool noSubtasksLeft); + + /** + * @brief Update the UI to show that the robot reset has finished. + */ + void finishedRobotResetUI(); + + /** + * @brief Update the UI to show that the robot reset has failed. + * @param error the error that occured while resetting the robot. + */ + void failedRobotResetUI(const QString &error); + Q_SIGNALS: /** * @brief Signal emitted when a task is selected. @@ -69,8 +91,14 @@ Q_SIGNALS: */ void taskSelected(int index); +private Q_SLOTS: + /** + * @brief Toggles the visibility of the sidebar. + */ + void toggleSidebarVisibility(); + private: - Sidebar &sidebar; ///< Reference to the Sidebar object. + Sidebar *sidebar; ///< Pointer to the Sidebar object. QVBoxLayout *subtaskListLayout; ///< Pointer to the QVBoxLayout for displaying subtasks. QLabel *mainTitleLabel; ///< Pointer to the QLabel for displaying the main title. QLabel *difficultyLabel; ///< Pointer to the QLabel for displaying the difficulty level. @@ -78,6 +106,11 @@ private: QLabel *topicLabel; ///< Pointer to the QLabel for displaying the topic of the task. QPushButton *nextButton; ///< Pointer to the QPushButton for navigating to the next task. QPushButton *previousButton; ///< Pointer to the QPushButton for navigating to the previous task. + QToolButton *menuButton; ///< Pointer to the QToolButton for opening the sidebar. + QToolButton *resetRobotStartButton; ///< Pointer to the QToolButton for resetting the robot. + QFrame *resetRobotFrame; ///< Pointer to the reset robot frame. + ExecuteFrame *executeResetRobotFrame; ///< Pointer to the execute reset robot frame. + QWidget *centralwidget; ///< Pointer to the central QWidget for the plugin. QVector<QSharedPointer<Task>> tasks; ///< Vector of loaded tasks. TaskManager *taskManager; ///< Pointer to the TaskManager object. @@ -91,6 +124,11 @@ private: * @brief Adds a line between subtask widgets in the QListWidget. */ void addLineBetweenWidgets(); + + /** + * @brief Sets up the splitter and layout for the main window. + */ + void setupSplitterAndLayout(); }; #endif // TASKUI_HPP \ No newline at end of file diff --git a/catkin_ws/src/learn_environment/resource/learn_environment.ui b/catkin_ws/src/learn_environment/resource/learn_environment.ui index fc12258b58d9f66ce58c2932a643ddb240c13d76..d89ea16fc66e70c454a57d08a45ba4a926b3f08a 100644 --- a/catkin_ws/src/learn_environment/resource/learn_environment.ui +++ b/catkin_ws/src/learn_environment/resource/learn_environment.ui @@ -139,7 +139,7 @@ QToolButton:hover { background: white; border-radius: 8px; }</string> </property> <property name="icon"> <iconset resource="resources.qrc"> - <normaloff>:/resource/icons/menu.png</normaloff>:/resource/icons/menu.png</iconset> + <normaloff>:/icons/menu.png</normaloff>:/icons/menu.png</iconset> </property> <property name="iconSize"> <size> @@ -336,7 +336,7 @@ QToolButton:hover { background: #e6e6e6; border-radius: 8px; }</string> </property> <property name="icon"> <iconset resource="resources.qrc"> - <normaloff>:/resource/icons/play.png</normaloff>:/resource/icons/play.png</iconset> + <normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset> </property> <property name="iconSize"> <size> diff --git a/catkin_ws/src/learn_environment/resource/resources.qrc b/catkin_ws/src/learn_environment/resource/resources.qrc new file mode 100644 index 0000000000000000000000000000000000000000..ce61f86b4b0b809ac7af4199b66e9d17eba5f6a9 --- /dev/null +++ b/catkin_ws/src/learn_environment/resource/resources.qrc @@ -0,0 +1,17 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource> + <file>icons/menu.png</file> + <file>icons/close.png</file> + <file>icons/help.png</file> + <file>icons/play.png</file> + <file>icons/stop.png</file> + <file>icons/failed.png</file> + <file>icons/succeeded.png</file> + <file>icons/loading.gif</file> + <file>icons/queued.gif</file> + + <file>../task_pool/task_definitions.json</file> + <file>../task_pool/difficulty_levels.json</file> + <file>../task_pool/topic_definitions.json</file> + </qresource> + </RCC> diff --git a/catkin_ws/src/learn_environment/resources.qrc b/catkin_ws/src/learn_environment/resources.qrc deleted file mode 100644 index 6e23edd0c293aadaca3396c321b320f07f14942e..0000000000000000000000000000000000000000 --- a/catkin_ws/src/learn_environment/resources.qrc +++ /dev/null @@ -1,17 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> - <qresource> - <file>resource/icons/menu.png</file> - <file>resource/icons/close.png</file> - <file>resource/icons/help.png</file> - <file>resource/icons/play.png</file> - <file>resource/icons/stop.png</file> - <file>resource/icons/failed.png</file> - <file>resource/icons/succeeded.png</file> - <file>resource/icons/loading.gif</file> - <file>resource/icons/queued.gif</file> - - <file>task_pool/task_definitions.json</file> - <file>task_pool/difficulty_levels.json</file> - <file>task_pool/topic_definitions.json</file> - </qresource> - </RCC> diff --git a/catkin_ws/src/learn_environment/src/learn_environment.cpp b/catkin_ws/src/learn_environment/src/learn_environment.cpp index 97a31330092b00ebbfeec331ce4add3460e32bfa..5df964a7bbc299e2357c2d30c181177cfe7bb83f 100644 --- a/catkin_ws/src/learn_environment/src/learn_environment.cpp +++ b/catkin_ws/src/learn_environment/src/learn_environment.cpp @@ -4,13 +4,6 @@ #include <pluginlib/class_list_macros.hpp> #include <QMainWindow> -#include <QSplitter> -#include <QHBoxLayout> - -namespace { - const char* CLOSE_ICON_PATH = ":/resource/icons/close.png"; - const char* MENU_ICON_PATH = ":/resource/icons/menu.png"; -} LearnEnvironment::LearnEnvironment(QWidget *parent) : rviz::Panel(parent), @@ -36,7 +29,6 @@ LearnEnvironment::~LearnEnvironment() } void LearnEnvironment::initialize() { - sidebar = new Sidebar(this); taskUI = new TaskUI( ui->subtaskListVLayout, ui->mainTitleLabel, @@ -45,17 +37,18 @@ void LearnEnvironment::initialize() { ui->topicLabel, ui->nextButton, ui->previousButton, - *sidebar, + ui->menuButton, + ui->resetRobotStartButton, + ui->resetRobotFrame, + ui->centralwidget, this ); - taskManager = new TaskManager(taskUI, ui->nextButton, ui->previousButton, ui->resetRobotFrame, this); - - setupSplitterAndLayout(); + taskManager = new TaskManager(taskUI, this); - sidebar->setVisible(false); - - connect(ui->menuButton, &QPushButton::clicked, this, &LearnEnvironment::toggleSidebarVisibility); - connect(ui->resetRobotStartButton, &QToolButton::clicked, taskManager, &TaskManager::forceResetRobot); + QHBoxLayout *mainPanelLayout = new QHBoxLayout(this); + mainPanelLayout->addWidget(taskUI); + mainPanelLayout->setContentsMargins(0, 0, 0, 0); + setLayout(mainPanelLayout); notebookConverter->moveToThread(notebookThread); connect(notebookThread, &QThread::started, notebookConverter, &NotebookConverter::processTaskPool); @@ -63,33 +56,6 @@ void LearnEnvironment::initialize() { notebookThread->start(); } -void LearnEnvironment::setupSplitterAndLayout() { - QSplitter *splitter = new QSplitter(Qt::Horizontal, this); - - splitter->addWidget(sidebar); - splitter->addWidget(ui->centralwidget); - - QList<int> sizes; - sizes << 200 << 600; - splitter->setSizes(sizes); - - // Set the layout to the main widget - QHBoxLayout *mainLayout = new QHBoxLayout(this); - mainLayout->addWidget(splitter); - setLayout(mainLayout); -} - -void LearnEnvironment::toggleSidebarVisibility() { - bool isVisible = sidebar->isVisible(); - sidebar->setVisible(!isVisible); - - if (sidebar->isVisible()) { - ui->menuButton->setIcon(QIcon(CLOSE_ICON_PATH)); - } else { - ui->menuButton->setIcon(QIcon(MENU_ICON_PATH)); - } -} - void LearnEnvironment::load(const rviz::Config &config) { // Implement loading of user-specific settings } diff --git a/catkin_ws/src/learn_environment/src/subtask_item.cpp b/catkin_ws/src/learn_environment/src/subtask_item.cpp index 830454b8f770f5790365ff6192faafe5ccd564e4..de37eb452d82d1f3ed6a2283f66aad94f918a36a 100644 --- a/catkin_ws/src/learn_environment/src/subtask_item.cpp +++ b/catkin_ws/src/learn_environment/src/subtask_item.cpp @@ -17,13 +17,13 @@ namespace { const char* LINK_STYLE = "font-family:'monospace'; font-size:10pt; color:'#444444'; vertical-align:bottom;"; const char* BODY_STYLE = "color:#444444;"; - const char* START_ICON_PATH = ":/resource/icons/play.png"; - const char* STOP_ICON_PATH = ":/resource/icons/stop.png"; - const char* HELP_ICON_PATH = ":/resource/icons/help.png"; - const char* LOADING_GIF_PATH = ":/resource/icons/loading.gif"; - const char* QUEUED_GIF_PATH = ":/resource/icons/queued.gif"; - const char* FAILED_ICON_PATH = ":/resource/icons/failed.png"; - const char* SUCCEEDED_ICON_PATH = ":/resource/icons/succeeded.png"; + const char* START_ICON_PATH = ":/icons/play.png"; + const char* STOP_ICON_PATH = ":/icons/stop.png"; + const char* HELP_ICON_PATH = ":/icons/help.png"; + const char* LOADING_GIF_PATH = ":/icons/loading.gif"; + const char* QUEUED_GIF_PATH = ":/icons/queued.gif"; + const char* FAILED_ICON_PATH = ":/icons/failed.png"; + const char* SUCCEEDED_ICON_PATH = ":/icons/succeeded.png"; const char* ICON_BUTTON_STYLE = "QToolButton { border: 0; padding: 0; }" diff --git a/catkin_ws/src/learn_environment/src/task_manager.cpp b/catkin_ws/src/learn_environment/src/task_manager.cpp index a70cf54a13091080e8e0c1de21ec731080b60f58..8ab2e9d6bb9c4f276d8e63a1e60208486c71651f 100644 --- a/catkin_ws/src/learn_environment/src/task_manager.cpp +++ b/catkin_ws/src/learn_environment/src/task_manager.cpp @@ -3,40 +3,21 @@ #include "learn_environment/task_parser.hpp" #include "learn_environment/task_executor.hpp" #include "learn_environment/notebook_converter.hpp" +#include "learn_environment/folder_structure_constants.hpp" #include <QDebug> #include <QToolButton> -namespace { - const char* TASK_DEFINITIONS_PATH = ":/task_pool/task_definitions.json"; - const char* TOPIC_DEFINITIONS_PATH = ":/task_pool/topic_definitions.json"; - const char* DIFFICULTY_LEVELS_DEFINITION_PATH = ":/task_pool/difficulty_levels.json"; - const char* LOADING_GIF_PATH = ":/resource/icons/loading.gif"; - const char* START_ICON_PATH = ":/resource/icons/play.png"; - const char* STOP_ICON_PATH = ":/resource/icons/stop.png"; - const char* FAILED_ICON_PATH = ":/resource/icons/failed.png"; - const char* SUCCEEDED_ICON_PATH = ":/resource/icons/succeeded.png"; - const char* RESET_ROBOT_TEXT = "Resetting the robot to its default state..."; - const char* RESET_ROBOT_TEXT_SCRIPTS = "Resetting the robot before script execution..."; - const char* RESET_ROBOT_TEXT_SUCCESS = "Robot successfully reset!"; - const char* RESET_ROBOT_TEXT_FAILED = "<b>Robot reset failed!</b>"; - const char* STOP_RESET_ROBOT_TOOLTIP = "Cancel reset process"; - const char* RESET_ROBOT_TOOLTIP = "Reset robot to default state"; - const char* RESET_ROBOT_START_BUTTON_NAME = "resetRobotStartButton"; -} - -TaskManager::TaskManager(TaskUI *taskUI, QPushButton *nextButton, QPushButton *previousButton, QFrame *resetRobotFrame, QObject *parent) +TaskManager::TaskManager(TaskUI *taskUI, QObject *parent) : QObject(parent), taskUI(taskUI), taskExecutor(new TaskExecutor(this)), - nextButton(nextButton), - previousButton(previousButton), - resetRobotFrame(resetRobotFrame), - executeResetRobotFrame(nullptr), currentTaskIndex(0) { TaskParser parser; - tasks = parser.loadTasks(TASK_DEFINITIONS_PATH, DIFFICULTY_LEVELS_DEFINITION_PATH, TOPIC_DEFINITIONS_PATH); + tasks = parser.loadTasks(FolderStructureConstants::TASK_DEFINITIONS_PATH, + FolderStructureConstants::DIFFICULTY_LEVELS_DEFINITION_PATH, + FolderStructureConstants::TOPIC_DEFINITIONS_PATH); if (tasks.isEmpty()) { qCritical() << "No tasks loaded. Exiting TaskManager initialization."; @@ -56,12 +37,6 @@ TaskManager::TaskManager(TaskUI *taskUI, QPushButton *nextButton, QPushButton *p taskUI->initializeUI(tasks); selectTask(currentTaskIndex); - - connect(nextButton, &QPushButton::clicked, this, &TaskManager::onNextButtonClicked); - connect(previousButton, &QPushButton::clicked, this, &TaskManager::onPreviousButtonClicked); - - // Connect TaskUI's taskSelected signal to the selectTask slot - connect(taskUI, &TaskUI::taskSelected, this, &TaskManager::selectTask); } void TaskManager::startStopSubtask(Subtask &subtask, bool startSolution) @@ -132,14 +107,14 @@ void TaskManager::selectTask(int index) } } -void TaskManager::onNextButtonClicked() +void TaskManager::nextTask() { if (currentTaskIndex < tasks.size() - 1) { selectTask(currentTaskIndex + 1); } } -void TaskManager::onPreviousButtonClicked() +void TaskManager::previousTask() { if (currentTaskIndex > 0) { selectTask(currentTaskIndex - 1); @@ -317,58 +292,19 @@ void TaskManager::onResetRobotStarted() return; } - QString message = "Robot reset started."; resetRobotInProgress = true; - - QToolButton* resetRobotStartButton = resetRobotFrame->findChild<QToolButton*>(RESET_ROBOT_START_BUTTON_NAME); - if (resetRobotStartButton) { - resetRobotStartButton->setIcon(QIcon(STOP_ICON_PATH)); - resetRobotStartButton->setToolTip(STOP_RESET_ROBOT_TOOLTIP); - } else { - qCritical() << "Reset robot start button not found."; - } - if (!executeResetRobotFrame) { - executeResetRobotFrame = new ExecuteFrame(resetRobotFrame); - QVBoxLayout *resetLayout = qobject_cast<QVBoxLayout*>(resetRobotFrame->layout()); - if (!resetLayout) { - resetLayout = new QVBoxLayout(resetRobotFrame); - resetRobotFrame->setLayout(resetLayout); - } - if (!resetLayout) { - qCritical() << "Reset layout not found."; - return; - } - resetLayout->addWidget(executeResetRobotFrame); - } - if (!executeResetRobotFrame) { - qCritical() << "Execute reset robot frame not found."; - return; - } - executeResetRobotFrame->setImage(LOADING_GIF_PATH); - if (queued_and_running_subtasks.isEmpty()) { - executeResetRobotFrame->setText(RESET_ROBOT_TEXT); - } else { - executeResetRobotFrame->setText(RESET_ROBOT_TEXT_SCRIPTS); - } - logWithHashes(message); + bool noSubtasksLeft = queued_and_running_subtasks.isEmpty(); + taskUI->startedRobotResetUI(noSubtasksLeft); + + logWithHashes("Robot reset started."); } void TaskManager::onResetRobotFinished() { resetRobotInProgress = false; - // update UI - QToolButton* resetRobotStartButton = resetRobotFrame->findChild<QToolButton*>(RESET_ROBOT_START_BUTTON_NAME); - if (resetRobotStartButton) { - resetRobotStartButton->setIcon(QIcon(START_ICON_PATH)); - resetRobotStartButton->setToolTip(RESET_ROBOT_TOOLTIP); - } - if (executeResetRobotFrame) { - if (!executeResetRobotFrame->getText().contains(RESET_ROBOT_TEXT_FAILED)) { - executeResetRobotFrame->setImage(SUCCEEDED_ICON_PATH); - executeResetRobotFrame->setText(RESET_ROBOT_TEXT_SUCCESS); - } - } + + taskUI->finishedRobotResetUI(); initiateFirstSubtask(); } @@ -379,10 +315,7 @@ void TaskManager::onResetRobotFailed(const QString &error) resetRobotInProgress = false; - if (executeResetRobotFrame) { - executeResetRobotFrame->setImage(FAILED_ICON_PATH); - executeResetRobotFrame->setText(QString(RESET_ROBOT_TEXT_FAILED) + "<br>" + error); - } + taskUI->failedRobotResetUI(error); onTaskExecutionFailed(error); } diff --git a/catkin_ws/src/learn_environment/src/task_parser.cpp b/catkin_ws/src/learn_environment/src/task_parser.cpp index 1d9003b3a573ff5ec8de7c0ff548d251dd7a87dc..bc95523c1970706a0b4b861e486f98eaba3903fd 100644 --- a/catkin_ws/src/learn_environment/src/task_parser.cpp +++ b/catkin_ws/src/learn_environment/src/task_parser.cpp @@ -88,7 +88,6 @@ QVector<QSharedPointer<Task>> TaskParser::parseTasks(const json& taskJsonData, c QSharedPointer<Task> task(new Task); try { task->title = QString::fromStdString(taskJson.at(TITLE_KEY).get<std::string>()); - task->description = QString::fromStdString(taskJson.at(DESCRIPTION_KEY).get<std::string>()); task->difficulty = QString::fromStdString(taskJson.at(DIFFICULTY_KEY).get<std::string>()); task->folder = QString::fromStdString(taskJson.at(FOLDER_KEY).get<std::string>()); @@ -180,9 +179,15 @@ QVector<Subtask> TaskParser::parseSubtasks(const json& subtasksJson, QSharedPoin subtask.title = QString::fromStdString(subtaskJson.at(TITLE_KEY).get<std::string>()); subtask.description = QString::fromStdString(subtaskJson.at(DESCRIPTION_KEY).get<std::string>()); subtask.file = QString::fromStdString(subtaskJson.at(SOLUTION_FILE_KEY).get<std::string>()); - subtask.filePath = FolderStructureConstants::getPackagePath() + FolderStructureConstants::USER_WORKSPACE + parentTask->folder + QString::fromStdString(subtaskJson.at(SOLUTION_FILE_KEY).get<std::string>()); - subtask.solutionFilePath = FolderStructureConstants::getPackagePath() + FolderStructureConstants::SOLUTION_SCRIPTS_SOURCE_PATH + parentTask->folder + QString::fromStdString(subtaskJson.at(SOLUTION_FILE_KEY).get<std::string>()); - subtask.evaluationFilePath = FolderStructureConstants::getPackagePath() + FolderStructureConstants::EVALUATION_SCRIPTS_SOURCE_PATH + parentTask->folder + QString::fromStdString(subtaskJson.at(EVALUATION_FILE_KEY).get<std::string>()); + subtask.filePath = FolderStructureConstants::getPackagePath() + + FolderStructureConstants::USER_WORKSPACE + parentTask->folder + + QString::fromStdString(subtaskJson.at(SOLUTION_FILE_KEY).get<std::string>()); + subtask.solutionFilePath = FolderStructureConstants::getPackagePath() + + FolderStructureConstants::SOLUTION_SCRIPTS_SOURCE_PATH + parentTask->folder + + QString::fromStdString(subtaskJson.at(SOLUTION_FILE_KEY).get<std::string>()); + subtask.evaluationFilePath = FolderStructureConstants::getPackagePath() + + FolderStructureConstants::EVALUATION_SCRIPTS_SOURCE_PATH + parentTask->folder + + QString::fromStdString(subtaskJson.at(EVALUATION_FILE_KEY).get<std::string>()); // Optional fields if (subtaskJson.contains(TIMEOUT_SECONDS_KEY)) { subtask.timeoutSeconds = subtaskJson.at(TIMEOUT_SECONDS_KEY).get<int>(); diff --git a/catkin_ws/src/learn_environment/src/task_ui.cpp b/catkin_ws/src/learn_environment/src/task_ui.cpp index fb3d1d75e4683eb40f0208ff665bb09cbc7887f5..26375edc317967e1b3efc9ee91d45e341419e6dd 100644 --- a/catkin_ws/src/learn_environment/src/task_ui.cpp +++ b/catkin_ws/src/learn_environment/src/task_ui.cpp @@ -2,7 +2,9 @@ #include "learn_environment/subtask_item.hpp" #include <QRegExp> +#include <QSplitter> #include <QDebug> +#include <QHBoxLayout> namespace { const char* FOLDER_HTML_TEMPLATE = R"( @@ -13,26 +15,48 @@ namespace { <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style="font-family:'monospace'; color:'#444444';">)"; + const char* CLOSE_ICON_PATH = ":/icons/close.png"; + const char* MENU_ICON_PATH = ":/icons/menu.png"; + const char* LOADING_GIF_PATH = ":/icons/loading.gif"; + const char* START_ICON_PATH = ":/icons/play.png"; + const char* STOP_ICON_PATH = ":/icons/stop.png"; + const char* FAILED_ICON_PATH = ":/icons/failed.png"; + const char* SUCCEEDED_ICON_PATH = ":/icons/succeeded.png"; + const char* RESET_ROBOT_TEXT = "Resetting the robot to its default state..."; + const char* RESET_ROBOT_TEXT_SCRIPTS = "Resetting the robot before script execution..."; + const char* RESET_ROBOT_TEXT_SUCCESS = "Robot successfully reset!"; + const char* RESET_ROBOT_TEXT_FAILED = "<b>Robot reset failed!</b>"; + const char* STOP_RESET_ROBOT_TOOLTIP = "Cancel reset process"; + const char* RESET_ROBOT_TOOLTIP = "Reset robot to default state"; } TaskUI::TaskUI(QVBoxLayout *subtaskListLayout, QLabel *mainTitleLabel, QLabel *difficultyLabel, QLabel *folderLabel, QLabel *topicLabel, - QPushButton *nextButton, QPushButton *previousButton, - Sidebar &sidebar, QObject *parent) - : QObject(parent), - sidebar(sidebar), + QPushButton *nextButton, QPushButton *previousButton, QToolButton *menuButton, + QToolButton *resetRobotStartButton, QFrame *resetRobotFrame, QWidget *centralwidget, QWidget *parent) + : QWidget(parent), + sidebar(new Sidebar(this)), subtaskListLayout(subtaskListLayout), mainTitleLabel(mainTitleLabel), difficultyLabel(difficultyLabel), folderLabel(folderLabel), topicLabel(topicLabel), nextButton(nextButton), - previousButton(previousButton) + previousButton(previousButton), + menuButton(menuButton), + resetRobotStartButton(resetRobotStartButton), + resetRobotFrame(resetRobotFrame), + executeResetRobotFrame(nullptr), + centralwidget(centralwidget) { } void TaskUI::setTaskManager(TaskManager *manager) { taskManager = manager; + connect(resetRobotStartButton, &QToolButton::clicked, taskManager, &TaskManager::forceResetRobot); + connect(nextButton, &QPushButton::clicked, taskManager, &TaskManager::nextTask); + connect(previousButton, &QPushButton::clicked, taskManager, &TaskManager::previousTask); + connect(this, &TaskUI::taskSelected, taskManager, &TaskManager::selectTask); } void TaskUI::initializeUI(const QVector<QSharedPointer<Task>> &loadedTasks) @@ -43,9 +67,13 @@ void TaskUI::initializeUI(const QVector<QSharedPointer<Task>> &loadedTasks) return; } - sidebar.fillSidebarWithTasks(tasks); + sidebar->fillSidebarWithTasks(tasks); + sidebar->setVisible(false); - connect(&sidebar, &Sidebar::taskSelected, this, &TaskUI::taskSelected); + setupSplitterAndLayout(); + + connect(menuButton, &QPushButton::clicked, this, &TaskUI::toggleSidebarVisibility); + connect(sidebar, &Sidebar::taskSelected, this, &TaskUI::taskSelected); } void TaskUI::setTaskUI(int currentTaskIndex) @@ -72,7 +100,7 @@ void TaskUI::setTaskUI(int currentTaskIndex) setSubtaskItems(currentTaskIndex); - sidebar.selectTask(currentTaskIndex); + sidebar->selectTask(currentTaskIndex); } void TaskUI::setSubtaskItems(int currentTaskIndex) @@ -137,4 +165,84 @@ void TaskUI::addLineBetweenWidgets() layout->addWidget(line); subtaskListLayout->addWidget(container); +} + +void TaskUI::setupSplitterAndLayout() { + QSplitter *splitter = new QSplitter(Qt::Horizontal, this); + + centralwidget->setStyleSheet("border: 0; margin: 0;"); + + splitter->addWidget(sidebar); + splitter->addWidget(centralwidget); + splitter->setStyleSheet("border: 0; margin: 0;"); + splitter->setContentsMargins(0, 0, 0, 0); + + QList<int> sizes; + sizes << 200 << 600; + splitter->setSizes(sizes); + + // Set the layout to the main widget + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->addWidget(splitter); + mainLayout->setContentsMargins(0, 0, 0, 0); + setLayout(mainLayout); +} + +void TaskUI::toggleSidebarVisibility() { + bool isVisible = sidebar->isVisible(); + sidebar->setVisible(!isVisible); + + if (sidebar->isVisible()) { + menuButton->setIcon(QIcon(CLOSE_ICON_PATH)); + } else { + menuButton->setIcon(QIcon(MENU_ICON_PATH)); + } +} + +void TaskUI::startedRobotResetUI(bool noSubtasksLeft) { + resetRobotStartButton->setIcon(QIcon(STOP_ICON_PATH)); + resetRobotStartButton->setToolTip(STOP_RESET_ROBOT_TOOLTIP); + + + if (!executeResetRobotFrame) { + executeResetRobotFrame = new ExecuteFrame(resetRobotFrame); + QVBoxLayout *resetLayout = qobject_cast<QVBoxLayout*>(resetRobotFrame->layout()); + if (!resetLayout) { + resetLayout = new QVBoxLayout(resetRobotFrame); + resetRobotFrame->setLayout(resetLayout); + } + if (!resetLayout) { + qCritical() << "Reset layout not found."; + return; + } + resetLayout->addWidget(executeResetRobotFrame); + } + if (!executeResetRobotFrame) { + qCritical() << "Execute reset robot frame not found."; + return; + } + executeResetRobotFrame->setImage(LOADING_GIF_PATH); + if (noSubtasksLeft) { + executeResetRobotFrame->setText(RESET_ROBOT_TEXT); + } else { + executeResetRobotFrame->setText(RESET_ROBOT_TEXT_SCRIPTS); + } +} + +void TaskUI::finishedRobotResetUI() { + resetRobotStartButton->setIcon(QIcon(START_ICON_PATH)); + resetRobotStartButton->setToolTip(RESET_ROBOT_TOOLTIP); + if (executeResetRobotFrame) { + if (!executeResetRobotFrame->getText().contains(RESET_ROBOT_TEXT_FAILED)) { + executeResetRobotFrame->setImage(SUCCEEDED_ICON_PATH); + executeResetRobotFrame->setText(RESET_ROBOT_TEXT_SUCCESS); + } + } +} + +void TaskUI::failedRobotResetUI(const QString &error) { + if (executeResetRobotFrame) { + executeResetRobotFrame->setImage(FAILED_ICON_PATH); + executeResetRobotFrame->setText(QString(RESET_ROBOT_TEXT_FAILED) + "<br>" + error); + } } \ No newline at end of file diff --git a/catkin_ws/src/learn_environment/task_pool/task_definitions.json b/catkin_ws/src/learn_environment/task_pool/task_definitions.json index 44dcd5e8bf55cdbd96850ac708125e81638a601b..c6b804c9210368adf31b89cfcb58225f8e3b8f4f 100644 --- a/catkin_ws/src/learn_environment/task_pool/task_definitions.json +++ b/catkin_ws/src/learn_environment/task_pool/task_definitions.json @@ -2,7 +2,6 @@ "tasks": [ { "title": "ROS Basics", - "description": "Basic Tasks for learning ROS.", "topic": "ROS", "difficulty": "beginner", "folder": "/ros/ros_basics", @@ -34,7 +33,6 @@ }, { "title": "ROS Intermediate", - "description": "Using ROS to control the robot.", "topic": "ROS", "difficulty": "intermediate", "folder": "/ros/ros_intermediate", @@ -56,7 +54,6 @@ }, { "title": "MoveIt Basics", - "description": "Basic Task for learning MoveIt.", "folder": "/move_it/move_it_basics", "topic": "MoveIt", "difficulty": "beginner", @@ -77,7 +74,6 @@ }, { "title": "Trajectory Controller", - "description": "TODO: Add Description.", "folder": "/controller/controller_task", "topic": "Controller", "difficulty": "beginner", @@ -92,7 +88,6 @@ }, { "title": "Task 4 (TODO: Rename)", - "description": "TODO: Add Description.", "topic": "Topic 3", "difficulty": "beginner", "folder": "/beginner/exercise_4", @@ -113,7 +108,6 @@ }, { "title": "Task 5 (TODO: Rename)", - "description": "TODO: Add Description.", "topic": "Topic 2", "difficulty": "beginner", "folder": "/beginner/exercise_5", @@ -134,7 +128,6 @@ }, { "title": "Task 6 (TODO: Rename)", - "description": "TODO: Add Description.", "topic": "Topic 2", "difficulty": "beginner", "folder": "/beginner/exercise_6", @@ -149,7 +142,6 @@ }, { "title": "Task 7 (TODO: Rename)", - "description": "TODO: Add Description.", "topic": "fjdsfkl", "difficulty": "intermediate", "folder": "/intermediate/exercise_1",