Skip to content
Snippets Groups Projects
Commit 1fb424e9 authored by Malte Laurin Matthey's avatar Malte Laurin Matthey
Browse files

Add toast messages into subtasks (for confirming showing solution, resetting...

Add toast messages into subtasks (for confirming showing solution, resetting etc.), small documentation improvement
parent 4d71bdd2
No related branches found
No related tags found
No related merge requests found
# ROS Noetic Standard Container with Preinstalled Learn Environment Plugin # ROS Noetic Standard Container with Preinstalled Learn Environment Plugin
> **⚠️ WARNING:** This README is for setting up the container with all functionalities. For the Learn Environment Plugin, follow the instructions in this README first. Then, refer to [GETTING_STARTED.md](./catkin_ws/src/learn_environment/tasks/GETTING_STARTED.md) for starting the turotial or [CONTRIBUTE.md](./catkin_ws/src/learn_environment/developer_docs/CONTRIBUTE.md) for contribution guidelines (creating new tasks & extending the plugin). > **⚠️ WARNING:** This README is for setting up the container with all functionalities. For the Learn Environment Plugin, follow the instructions in this README first. Then, refer to [GETTING_STARTED.md](./catkin_ws/src/learn_environment/tasks/GETTING_STARTED.md) for starting the tutorial or [CONTRIBUTE.md](./catkin_ws/src/learn_environment/developer_docs/CONTRIBUTE.md) for contribution guidelines (creating new tasks & extending the plugin).
This repository provides a Visual Studio Code development container with ROS Noetic installed to control a Franka Panda Robot in both simulation and real environments. It also has the Learn Environment plugin for RViz preinstalled so you can start learning how to work with the robot immediatly. This repository provides a Visual Studio Code development container with ROS Noetic installed to control a Franka Panda Robot in both simulation and real environments. It also has the Learn Environment plugin for RViz preinstalled so you can start learning how to work with the robot immediatly.
...@@ -148,7 +148,7 @@ Access the desktop environment of the container in your browser at [http://local ...@@ -148,7 +148,7 @@ Access the desktop environment of the container in your browser at [http://local
To get started with the plugin, follow the instructions provided [here](./catkin_ws/src/learn_environment/tasks/GETTING_STARTED.md). If VS Code does not automatically open the `/catkin_ws/src/learn_environment/tasks` folder, please navigate to it manually. This is the starting point of the tutorial. To get started with the plugin, follow the instructions provided [here](./catkin_ws/src/learn_environment/tasks/GETTING_STARTED.md). If VS Code does not automatically open the `/catkin_ws/src/learn_environment/tasks` folder, please navigate to it manually. This is the starting point of the tutorial.
![Gazebo RVIZ Sim](/screenshots/plugin.png) ![Learn Environment](/screenshots/plugin.png)
### Control the Real Panda ### Control the Real Panda
......
...@@ -58,6 +58,7 @@ set(${PROJECT_NAME}_HDRS ...@@ -58,6 +58,7 @@ set(${PROJECT_NAME}_HDRS
include/${PROJECT_NAME}/notebook_converter.hpp include/${PROJECT_NAME}/notebook_converter.hpp
include/${PROJECT_NAME}/folder_structure_constants.hpp include/${PROJECT_NAME}/folder_structure_constants.hpp
include/${PROJECT_NAME}/execute_frame.hpp include/${PROJECT_NAME}/execute_frame.hpp
include/${PROJECT_NAME}/toast.hpp
) )
set(${PROJECT_NAME}_SRCS set(${PROJECT_NAME}_SRCS
...@@ -73,6 +74,7 @@ set(${PROJECT_NAME}_SRCS ...@@ -73,6 +74,7 @@ set(${PROJECT_NAME}_SRCS
src/task_ui.cpp src/task_ui.cpp
src/notebook_converter.cpp src/notebook_converter.cpp
src/execute_frame.cpp src/execute_frame.cpp
src/toast.cpp
) )
set(${PROJECT_NAME}_UIS set(${PROJECT_NAME}_UIS
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "task.hpp" #include "task.hpp"
#include "task_manager.hpp" #include "task_manager.hpp"
#include "execute_frame.hpp" #include "execute_frame.hpp"
#include "toast.hpp"
#include <QWidget> #include <QWidget>
#include <QPushButton> #include <QPushButton>
...@@ -59,7 +60,10 @@ private Q_SLOTS: ...@@ -59,7 +60,10 @@ private Q_SLOTS:
void handleStartOwnScript(); void handleStartOwnScript();
void handleStartSolution(); void handleStartSolution();
void handleToggleSolution(); void handleToggleSolution();
void handleResetNotebook(); void handleResetNotebook();
protected:
void resizeEvent(QResizeEvent* event) override;
private: private:
/** /**
...@@ -72,6 +76,7 @@ private: ...@@ -72,6 +76,7 @@ private:
void initializeHelpMenu(); void initializeHelpMenu();
void initializeStartMenu(); void initializeStartMenu();
void setExecutionFrame(const QString& imagePath, const QString& text); void setExecutionFrame(const QString& imagePath, const QString& text);
void showToast(const QString &message); ///< Displays a toast message.
TaskManager *taskManager; ///< Pointer to the TaskManager object. TaskManager *taskManager; ///< Pointer to the TaskManager object.
Subtask *subtask; ///< Pointer to the subtask object. Subtask *subtask; ///< Pointer to the subtask object.
...@@ -89,8 +94,10 @@ private: ...@@ -89,8 +94,10 @@ private:
QPushButton *menuToggleSolutionBtn; ///< Button to toggle the solution. QPushButton *menuToggleSolutionBtn; ///< Button to toggle the solution.
QPushButton *menuResetNotebookBtn; ///< Button to reset the notebook. QPushButton *menuResetNotebookBtn; ///< Button to reset the notebook.
QMenu* startMenu; ///< Menu for starting the subtask. QMenu *startMenu; ///< Menu for starting the subtask.
QMenu* helpMenu; ///< Menu for showing the help options. QMenu *helpMenu; ///< Menu for showing the help options.
Toast *toast; ///< Toast message for subtask.
}; };
#endif // SUBTASK_ITEM_HPP #endif // SUBTASK_ITEM_HPP
\ No newline at end of file
#ifndef TOAST_HPP
#define TOAST_HPP
#include <QWidget>
#include <QLabel>
#include <QTimer>
class Toast : public QWidget {
Q_OBJECT
public:
explicit Toast(QWidget *parent = nullptr);
void showToast(const QString &message);
private Q_SLOTS:
void fadeOut();
private:
QLabel *toastLabel;
QTimer *toastTimer;
};
#endif // TOAST_HPP
\ No newline at end of file
...@@ -51,6 +51,11 @@ namespace { ...@@ -51,6 +51,11 @@ namespace {
const char* HIDE_SOLUTION_TEXT = "Hide Solution"; const char* HIDE_SOLUTION_TEXT = "Hide Solution";
const char* RESET_NOTEBOOK_TEXT = "Reset Notebook"; const char* RESET_NOTEBOOK_TEXT = "Reset Notebook";
const char* SHOWING_SOLUTION_TOAST = "Solution added to your notebook.";
const char* SHOWING_SOLUTION_FAILED_TOAST = "There is no solution for this task.";
const char* HIDE_SOLUTION_TOAST = "Solution removed from your notebook.";
const char* NOTEBOOK_RESET_TOAST = "Notebook has been reset.";
const char* RESET_NOTEBOOK_CONFIRMATION_TEXT = "This will remove all changes made to the notebook and can't be undone. Are you sure you want to proceed?"; const char* RESET_NOTEBOOK_CONFIRMATION_TEXT = "This will remove all changes made to the notebook and can't be undone. Are you sure you want to proceed?";
} }
...@@ -61,12 +66,23 @@ SubtaskItem::SubtaskItem(QWidget *parent, Subtask *subtask) ...@@ -61,12 +66,23 @@ SubtaskItem::SubtaskItem(QWidget *parent, Subtask *subtask)
bodyText(subtask->description), bodyText(subtask->description),
subtask(subtask), subtask(subtask),
taskManager(nullptr), taskManager(nullptr),
executeSubtaskFrame(nullptr) { executeSubtaskFrame(nullptr),
toast(new Toast(this)) {
setupItemUI(headerText, linkText, bodyText); setupItemUI(headerText, linkText, bodyText);
updateUI(true); updateUI(true);
initializeHelpMenu(); initializeHelpMenu();
initializeStartMenu(); initializeStartMenu();
resizeEvent(nullptr); // Initial positioning of toast
}
void SubtaskItem::resizeEvent(QResizeEvent* event) {
QWidget::resizeEvent(event);
}
void SubtaskItem::showToast(const QString &message) {
toast->showToast(message);
} }
void SubtaskItem::updateUI(bool constructorCall) void SubtaskItem::updateUI(bool constructorCall)
...@@ -184,9 +200,27 @@ void SubtaskItem::handleStartSolution() { ...@@ -184,9 +200,27 @@ void SubtaskItem::handleStartSolution() {
} }
void SubtaskItem::handleToggleSolution() { void SubtaskItem::handleToggleSolution() {
if (taskManager && subtask) { if (!taskManager || !subtask) {
taskManager->toggleSolution(*subtask); return;
}
QString notebookPath = subtask->filePath;
if (!QFile::exists(notebookPath)) {
return;
} }
bool hadSolutionBeforeToggle = NotebookConverter::hasSolutionCells(notebookPath);
taskManager->toggleSolution(*subtask);
bool hasSolutionAfterToggle = NotebookConverter::hasSolutionCells(notebookPath);
if (hasSolutionAfterToggle && !hadSolutionBeforeToggle) {
showToast(SHOWING_SOLUTION_TOAST);
} else if (!hasSolutionAfterToggle && !hadSolutionBeforeToggle) {
showToast(SHOWING_SOLUTION_FAILED_TOAST);
} else {
showToast(HIDE_SOLUTION_TOAST);
}
QMenu* menu = qobject_cast<QMenu*>(sender()->parent()->parent()); QMenu* menu = qobject_cast<QMenu*>(sender()->parent()->parent());
if (menu) { if (menu) {
menu->close(); menu->close();
...@@ -204,6 +238,7 @@ void SubtaskItem::handleResetNotebook() { ...@@ -204,6 +238,7 @@ void SubtaskItem::handleResetNotebook() {
if (taskManager && subtask) { if (taskManager && subtask) {
NotebookConverter* converter = new NotebookConverter(); NotebookConverter* converter = new NotebookConverter();
converter->resetNotebook(subtask->filePath, subtask->solutionFilePath); converter->resetNotebook(subtask->filePath, subtask->solutionFilePath);
showToast(NOTEBOOK_RESET_TOAST);
} }
} }
QMenu* menu = qobject_cast<QMenu*>(sender()->parent()->parent()); QMenu* menu = qobject_cast<QMenu*>(sender()->parent()->parent());
......
#include "learn_environment/toast.hpp"
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QVBoxLayout>
Toast::Toast(QWidget *parent) : QWidget(parent), toastLabel(new QLabel(this)), toastTimer(new QTimer(this)) {
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(toastLabel);
layout->setContentsMargins(0, 0, 0, 0);
toastLabel->setStyleSheet("background-color: rgba(0, 0, 0, 160); color: white; padding: 6px; border-radius: 6px;");
toastLabel->setAlignment(Qt::AlignCenter);
toastLabel->setWordWrap(true);
QFont toastFont = toastLabel->font();
toastFont.setPointSize(10);
toastLabel->setFont(toastFont);
toastLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
toastLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
toastLabel->hide();
connect(toastTimer, &QTimer::timeout, this, &Toast::fadeOut);
}
void Toast::showToast(const QString &message) {
toastLabel->setText(message);
toastLabel->adjustSize();
this->resize(toastLabel->sizeHint());
toastLabel->move(0, 0);
int margin = 10;
int x = (parentWidget()->width() - this->width()) / 2;
int y = parentWidget()->height() - this->height() - margin;
this->move(x, y);
QGraphicsEffect* existingEffect = toastLabel->graphicsEffect();
if (existingEffect) {
delete existingEffect;
toastLabel->setGraphicsEffect(nullptr);
}
QGraphicsOpacityEffect* opacityEffect = new QGraphicsOpacityEffect(toastLabel);
toastLabel->setGraphicsEffect(opacityEffect);
QPropertyAnimation* fadeIn = new QPropertyAnimation(opacityEffect, "opacity");
fadeIn->setDuration(500);
fadeIn->setStartValue(0);
fadeIn->setEndValue(1);
fadeIn->setEasingCurve(QEasingCurve::InOutQuad);
fadeIn->start(QPropertyAnimation::DeleteWhenStopped);
this->raise();
toastLabel->show();
this->show();
toastTimer->start(2500);
}
void Toast::fadeOut() {
if (!toastLabel || !toastLabel->graphicsEffect() || toastLabel->isHidden())
return;
QGraphicsOpacityEffect* effect = qobject_cast<QGraphicsOpacityEffect*>(toastLabel->graphicsEffect());
if (!effect) return;
QPropertyAnimation* fadeOut = new QPropertyAnimation(effect, "opacity");
fadeOut->setDuration(500);
fadeOut->setStartValue(1);
fadeOut->setEndValue(0);
fadeOut->setEasingCurve(QEasingCurve::InOutQuad);
connect(fadeOut, &QPropertyAnimation::finished, this, [this, effect]() {
toastLabel->hide();
toastLabel->setGraphicsEffect(nullptr);
});
fadeOut->start(QPropertyAnimation::DeleteWhenStopped);
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ To get started, follow these steps: ...@@ -3,6 +3,7 @@ To get started, follow these steps:
1. **Open a Terminal in VS Code** 1. **Open a Terminal in VS Code**
Go to `Terminal` > `New Terminal` in the VS Code menu. The shortcut is ``Ctrl + ` `` Go to `Terminal` > `New Terminal` in the VS Code menu. The shortcut is ``Ctrl + ` ``
- If you don't use VS Code, open any terminal. If you installed this with a docker setup as recommended , make sure to open the terminal in the container.
2. **Execute the Launch Command** 2. **Execute the Launch Command**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment