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
> **⚠️ 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.
......@@ -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.
![Gazebo RVIZ Sim](/screenshots/plugin.png)
![Learn Environment](/screenshots/plugin.png)
### Control the Real Panda
......
......@@ -58,6 +58,7 @@ set(${PROJECT_NAME}_HDRS
include/${PROJECT_NAME}/notebook_converter.hpp
include/${PROJECT_NAME}/folder_structure_constants.hpp
include/${PROJECT_NAME}/execute_frame.hpp
include/${PROJECT_NAME}/toast.hpp
)
set(${PROJECT_NAME}_SRCS
......@@ -73,6 +74,7 @@ set(${PROJECT_NAME}_SRCS
src/task_ui.cpp
src/notebook_converter.cpp
src/execute_frame.cpp
src/toast.cpp
)
set(${PROJECT_NAME}_UIS
......
......@@ -4,6 +4,7 @@
#include "task.hpp"
#include "task_manager.hpp"
#include "execute_frame.hpp"
#include "toast.hpp"
#include <QWidget>
#include <QPushButton>
......@@ -59,7 +60,10 @@ private Q_SLOTS:
void handleStartOwnScript();
void handleStartSolution();
void handleToggleSolution();
void handleResetNotebook();
void handleResetNotebook();
protected:
void resizeEvent(QResizeEvent* event) override;
private:
/**
......@@ -72,6 +76,7 @@ private:
void initializeHelpMenu();
void initializeStartMenu();
void setExecutionFrame(const QString& imagePath, const QString& text);
void showToast(const QString &message); ///< Displays a toast message.
TaskManager *taskManager; ///< Pointer to the TaskManager object.
Subtask *subtask; ///< Pointer to the subtask object.
......@@ -89,8 +94,10 @@ private:
QPushButton *menuToggleSolutionBtn; ///< Button to toggle the solution.
QPushButton *menuResetNotebookBtn; ///< Button to reset the notebook.
QMenu* startMenu; ///< Menu for starting the subtask.
QMenu* helpMenu; ///< Menu for showing the help options.
QMenu *startMenu; ///< Menu for starting the subtask.
QMenu *helpMenu; ///< Menu for showing the help options.
Toast *toast; ///< Toast message for subtask.
};
#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 {
const char* HIDE_SOLUTION_TEXT = "Hide Solution";
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?";
}
......@@ -61,12 +66,23 @@ SubtaskItem::SubtaskItem(QWidget *parent, Subtask *subtask)
bodyText(subtask->description),
subtask(subtask),
taskManager(nullptr),
executeSubtaskFrame(nullptr) {
executeSubtaskFrame(nullptr),
toast(new Toast(this)) {
setupItemUI(headerText, linkText, bodyText);
updateUI(true);
initializeHelpMenu();
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)
......@@ -184,9 +200,27 @@ void SubtaskItem::handleStartSolution() {
}
void SubtaskItem::handleToggleSolution() {
if (taskManager && subtask) {
taskManager->toggleSolution(*subtask);
if (!taskManager || !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());
if (menu) {
menu->close();
......@@ -204,6 +238,7 @@ void SubtaskItem::handleResetNotebook() {
if (taskManager && subtask) {
NotebookConverter* converter = new NotebookConverter();
converter->resetNotebook(subtask->filePath, subtask->solutionFilePath);
showToast(NOTEBOOK_RESET_TOAST);
}
}
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:
1. **Open a Terminal in VS Code**
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**
......
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