From 67c8767abb5eab21de2c36092c65ded8d4eff573 Mon Sep 17 00:00:00 2001 From: m-melchior Date: Mon, 25 Jan 2021 02:32:25 +0100 Subject: [PATCH 1/3] Implemented Save Canvas Layout Added Option to save / load canvas layout to the plot plugin. Based on new "AddVariable" due to some inconsistency when manually adding plots to the canvas. Uses same mechanism as "OnAddVariable", except there is no sender but the latest IncrementalPlot is being used from the list that can be retrieved by "Plots" --- gazebo/gui/plot/PlotCanvas.cc | 94 ++++++++------- gazebo/gui/plot/PlotCanvas.hh | 18 +-- gazebo/gui/plot/PlotWindow.cc | 220 +++++++++++++++++++++++++++++----- gazebo/gui/plot/PlotWindow.hh | 12 ++ 4 files changed, 266 insertions(+), 78 deletions(-) diff --git a/gazebo/gui/plot/PlotCanvas.cc b/gazebo/gui/plot/PlotCanvas.cc index e6f875f256..fa39f26021 100644 --- a/gazebo/gui/plot/PlotCanvas.cc +++ b/gazebo/gui/plot/PlotCanvas.cc @@ -110,8 +110,7 @@ PlotCanvas::PlotCanvas(QWidget *_parent) this->dataPtr->deleteCanvasAct = new QAction("Delete canvas", settingsMenu); this->dataPtr->deleteCanvasAct->setStatusTip(tr("Delete entire canvas")); - connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, - SLOT(OnDeleteCanvas())); + connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, SLOT(OnDeleteCanvas())); QAction *showGridAct = new QAction("Show grid", settingsMenu); showGridAct->setStatusTip(tr("Show/hide grid lines on plot")); @@ -167,17 +166,17 @@ PlotCanvas::PlotCanvas(QWidget *_parent) this->dataPtr->yVariableContainer->setContentsMargins(0, 0, 0, 0); connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), - this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); + SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), + this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableRemoved(unsigned int, unsigned int)), - this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); + SIGNAL(VariableRemoved(unsigned int, unsigned int)), + this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableMoved(unsigned int, unsigned int)), - this, SLOT(OnMoveVariable(unsigned int, unsigned int))); + SIGNAL(VariableMoved(unsigned int, unsigned int)), + this, SLOT(OnMoveVariable(unsigned int, unsigned int))); connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableLabelChanged(unsigned int, std::string)), - this, SLOT(OnSetVariableLabel(unsigned int, std::string))); + SIGNAL(VariableLabelChanged(unsigned int, std::string)), + this, SLOT(OnSetVariableLabel(unsigned int, std::string))); QVBoxLayout *variableContainerLayout = new QVBoxLayout; variableContainerLayout->addWidget(xVariableContainer); @@ -211,8 +210,7 @@ PlotCanvas::PlotCanvas(QWidget *_parent) // empty plot this->dataPtr->emptyPlot = new IncrementalPlot(this); - connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), - this, SLOT(OnAddVariable(std::string))); + connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), this, SLOT(OnAddVariable(std::string))); plotLayout->addWidget(this->dataPtr->emptyPlot); // set initial show grid state @@ -250,8 +248,7 @@ void PlotCanvas::SetVariableLabel(const unsigned int _id, } ///////////////////////////////////////////////// -unsigned int PlotCanvas::AddVariable(const std::string &_variable, - const unsigned int _plotId) +unsigned int PlotCanvas::AddVariable(const std::string &_variable, const unsigned int _plotId) { unsigned int targetId = VariablePill::EmptyVariable; if (_plotId != EmptyPlot) @@ -259,8 +256,7 @@ unsigned int PlotCanvas::AddVariable(const std::string &_variable, // find a variable that belongs to the specified plotId and make that the // the target variable that the new variable will be added to auto it = this->dataPtr->plotData.find(_plotId); - if (it != this->dataPtr->plotData.end() && - !it->second->variableCurves.empty()) + if (it != this->dataPtr->plotData.end() && !it->second->variableCurves.empty()) { targetId = it->second->variableCurves.begin()->first; } @@ -268,13 +264,13 @@ unsigned int PlotCanvas::AddVariable(const std::string &_variable, // add to container and let the signals/slots do the work on adding the // a new plot with the curve in the overloaded AddVariable function - return this->dataPtr->yVariableContainer->AddVariablePill(_variable, - targetId); + unsigned int _retVal = this->dataPtr->yVariableContainer->AddVariablePill(_variable, targetId); + + return _retVal; } ///////////////////////////////////////////////// -void PlotCanvas::AddVariable(const unsigned int _id, - const std::string &_variable, const unsigned int _plotId) +void PlotCanvas::AddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _plotId) { unsigned int plotId; if (_plotId == EmptyPlot) @@ -330,16 +326,41 @@ void PlotCanvas::AddVariable(const unsigned int _id, } } + +///////////////////////////////////////////////// +void PlotCanvas::AddVariable(const std::string &_variable, IncrementalPlot *plot_in) +{ + if (!plot_in) + return; + + if (plot_in == this->dataPtr->emptyPlot) + { + // add new variable to new plot + this->AddVariable(_variable); + } + else + { + for (const auto &it : this->dataPtr->plotData) + { + if (plot_in == it.second->plot) + { + // add to existing plot + this->AddVariable(_variable, it.second->id); + return; + } + } + } +} + + ///////////////////////////////////////////////// -void PlotCanvas::RemoveVariable(const unsigned int _id, - const unsigned int _plotId) +void PlotCanvas::RemoveVariable(const unsigned int _id, const unsigned int _plotId) { auto it = this->dataPtr->plotData.end(); if (_plotId == EmptyPlot) { // find which plot the variable belongs to - for (auto pIt = this->dataPtr->plotData.begin(); - pIt != this->dataPtr->plotData.end(); ++pIt) + for (auto pIt = this->dataPtr->plotData.begin(); pIt != this->dataPtr->plotData.end(); ++pIt) { auto v = pIt->second->variableCurves.find(_id); if (v != pIt->second->variableCurves.end()) @@ -414,8 +435,7 @@ unsigned int PlotCanvas::AddPlot() plot->setAutoDelete(false); plot->ShowGrid(this->dataPtr->emptyPlot->IsShowGrid()); plot->ShowHoverLine(this->dataPtr->emptyPlot->IsShowHoverLine()); - connect(plot, SIGNAL(VariableAdded(std::string)), this, - SLOT(OnAddVariable(std::string))); + connect(plot, SIGNAL(VariableAdded(std::string)), this, SLOT(OnAddVariable(std::string))); this->dataPtr->plotSplitter->addWidget(plot); PlotData *p = new PlotData; @@ -487,8 +507,7 @@ unsigned int PlotCanvas::PlotByVariable(const unsigned int _variableId) const ///////////////////////////////////////////////// void PlotCanvas::OnAddVariable(const std::string &_variable) { - IncrementalPlot *plot = - qobject_cast(QObject::sender()); + IncrementalPlot *plot = qobject_cast(QObject::sender()); if (!plot) return; @@ -513,8 +532,7 @@ void PlotCanvas::OnAddVariable(const std::string &_variable) } ///////////////////////////////////////////////// -void PlotCanvas::OnAddVariable(const unsigned int _id, - const std::string &_variable, const unsigned int _targetId) +void PlotCanvas::OnAddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _targetId) { if (_targetId != VariablePill::EmptyVariable) { @@ -544,9 +562,9 @@ void PlotCanvas::OnRemoveVariable(const unsigned int _id, } ///////////////////////////////////////////////// -void PlotCanvas::OnMoveVariable(const unsigned int _id, - const unsigned int _targetId) +void PlotCanvas::OnMoveVariable(const unsigned int _id, const unsigned int _targetId) { + printf("OnMoveVariable\n"); auto plotIt = this->dataPtr->plotData.end(); auto targetPlotIt = this->dataPtr->plotData.end(); unsigned int curveId = 0; @@ -677,8 +695,7 @@ void PlotCanvas::Restart() unsigned int curveId = v.second; // get variable pill - VariablePill *variablePill = - this->dataPtr->yVariableContainer->GetVariablePill(variableId); + VariablePill *variablePill = this->dataPtr->yVariableContainer->GetVariablePill(variableId); if (!variablePill) continue; @@ -908,8 +925,7 @@ std::string PlotCanvas::Title() const } ///////////////////////////////////////////////// -void PlotCanvas::Export(const std::string &_dirName, - const FileType _type) const +void PlotCanvas::Export(const std::string &_dirName, const FileType _type) const { std::string title = this->Title(); @@ -940,8 +956,7 @@ void PlotCanvas::ExportPDF(const std::string &_filePrefix) const IncrementalPlot *plot = it.second->plot; - QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), - plot->canvas()->height()); + QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), plot->canvas()->height()); QwtPlotRenderer renderer; renderer.renderDocument(plot, QString(filename.c_str()), docSize, 20); @@ -969,8 +984,7 @@ void PlotCanvas::ExportCSV(const std::string &_filePrefix) const std::replace(label.begin(), label.end(), '/', '_'); std::replace(label.begin(), label.end(), '?', ':'); - std::string filename = - common::unique_file_path(_filePrefix + "-" + label, "csv"); + std::string filename = common::unique_file_path(_filePrefix + "-" + label, "csv"); std::ofstream out(filename); // \todo: fix hardcoded sim_time diff --git a/gazebo/gui/plot/PlotCanvas.hh b/gazebo/gui/plot/PlotCanvas.hh index dd52ff81f4..511e5625a1 100644 --- a/gazebo/gui/plot/PlotCanvas.hh +++ b/gazebo/gui/plot/PlotCanvas.hh @@ -51,23 +51,25 @@ namespace gazebo /// \brief Set the label of a variable. /// \param[in] _id Unique id of the variable /// \param[in] _label New variable label. - public: void SetVariableLabel(const unsigned int _id, - const std::string &_label); + public: void SetVariableLabel(const unsigned int _id, const std::string &_label); /// \brief Add a new variable to a plot. /// \param[in] _variable Name of the variable. /// \param[in] _plotId Unique id of the plot to add the variable to. /// \return Unique id of the variable - public: unsigned int AddVariable(const std::string &_variable, - const unsigned int _plotId = EmptyPlot); + public: unsigned int AddVariable(const std::string &_variable, const unsigned int _plotId = EmptyPlot); + + /// \brief Add a new variable to a plot. + /// \param[in] _variable Name of the variable. + /// \param[in] _plot pointer to the plot to add the variable to. + public: void AddVariable(const std::string &_variable, IncrementalPlot *plot); /// \brief Remove a variable from a plot. /// \param[in] _id Unique id of the variable /// \param[in] _plotId Unique id of plot to remove the variable from. /// If EmptyPlot is specified, the function will search through all /// plots for the variable and remove it from the plot if found. - public: void RemoveVariable(const unsigned int _id, - const unsigned int _plotId = EmptyPlot); + public: void RemoveVariable(const unsigned int _id, const unsigned int _plotId = EmptyPlot); /// \brief Add a new plot to the canvas. /// \return Unique id of the plot @@ -147,9 +149,7 @@ namespace gazebo /// \param[in] _variable Name of the variable /// \param[in] _plotId Unique id of the plot to add the variable to. /// EmptyPlot means add to a new plot. - private: void AddVariable(const unsigned int _id, - const std::string &_variable, - const unsigned int _plotId = EmptyPlot); + private: void AddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _plotId = EmptyPlot); /// \brief Update the axis label for plots in the canvas. /// Currently used to determine which plot will display the x-axis label diff --git a/gazebo/gui/plot/PlotWindow.cc b/gazebo/gui/plot/PlotWindow.cc index e17857c26f..b6c332c89e 100644 --- a/gazebo/gui/plot/PlotWindow.cc +++ b/gazebo/gui/plot/PlotWindow.cc @@ -15,7 +15,9 @@ * */ #include +#include +#include "gazebo/common/Console.hh" #include "gazebo/gui/qt.h" #include "gazebo/gui/GuiIface.hh" #include "gazebo/gui/MainWindow.hh" @@ -30,6 +32,11 @@ using namespace gazebo; using namespace gui; +using namespace tinyxml2; +#ifndef XMLCheckResult + #define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { printf("Error: %i\n", a_eResult); return a_eResult; } +#endif + namespace gazebo { namespace gui @@ -84,26 +91,16 @@ PlotWindow::PlotWindow(QWidget *_parent) this->setWindowIcon(QIcon(":/images/gazebo.svg")); this->setWindowTitle("Gazebo: Plotting Utility"); this->setObjectName("plotWindow"); - this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | - Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint | - Qt::CustomizeWindowHint); + this->setWindowFlags( Qt::Window | + Qt::WindowTitleHint | + Qt::WindowCloseButtonHint | + Qt::WindowStaysOnTopHint | + Qt::CustomizeWindowHint); // new empty canvas this->dataPtr->canvasSplitter = new QSplitter(Qt::Vertical); this->AddCanvas(); - // add button - QPushButton *addCanvasButton = new QPushButton("+"); - addCanvasButton->setObjectName("plotAddCanvas"); - addCanvasButton->setDefault(false); - addCanvasButton->setAutoDefault(false); - addCanvasButton->setToolTip("Add a new canvas"); - QGraphicsDropShadowEffect *addCanvasShadow = new QGraphicsDropShadowEffect(); - addCanvasShadow->setBlurRadius(8); - addCanvasShadow->setOffset(0, 0); - addCanvasButton->setGraphicsEffect(addCanvasShadow); - connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); - // export button QPushButton *exportPlotButton = new QPushButton("Export"); exportPlotButton->setIcon(QIcon(":/images/file_upload.svg")); @@ -117,9 +114,47 @@ PlotWindow::PlotWindow(QWidget *_parent) exportPlotButton->setGraphicsEffect(exportPlotShadow); connect(exportPlotButton, SIGNAL(clicked()), this, SLOT(OnExport())); + // save button + QPushButton *saveCanvasButton = new QPushButton("Save"); + saveCanvasButton->setObjectName("plotSaveCanvas"); + saveCanvasButton->setDefault(false); + saveCanvasButton->setAutoDefault(false); + saveCanvasButton->setToolTip("Save canvas"); + QGraphicsDropShadowEffect *saveCanvasShadow = new QGraphicsDropShadowEffect(); + saveCanvasShadow->setBlurRadius(8); + saveCanvasShadow->setOffset(0, 0); + saveCanvasButton->setGraphicsEffect(saveCanvasShadow); + connect(saveCanvasButton, SIGNAL(clicked()), this, SLOT(OnSaveCanvas())); + + // load button + QPushButton *loadCanvasButton = new QPushButton("Load"); + loadCanvasButton->setObjectName("plotLoadCanvas"); + loadCanvasButton->setDefault(false); + loadCanvasButton->setAutoDefault(false); + loadCanvasButton->setToolTip("Load canvas"); + QGraphicsDropShadowEffect *loadCanvasShadow = new QGraphicsDropShadowEffect(); + loadCanvasShadow->setBlurRadius(8); + loadCanvasShadow->setOffset(0, 0); + loadCanvasButton->setGraphicsEffect(loadCanvasShadow); + connect(loadCanvasButton, SIGNAL(clicked()), this, SLOT(OnLoadCanvas())); + + // add button + QPushButton *addCanvasButton = new QPushButton("+"); + addCanvasButton->setObjectName("plotAddCanvas"); + addCanvasButton->setDefault(false); + addCanvasButton->setAutoDefault(false); + addCanvasButton->setToolTip("Add a new canvas"); + QGraphicsDropShadowEffect *addCanvasShadow = new QGraphicsDropShadowEffect(); + addCanvasShadow->setBlurRadius(8); + addCanvasShadow->setOffset(0, 0); + addCanvasButton->setGraphicsEffect(addCanvasShadow); + connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); + QHBoxLayout *addButtonLayout = new QHBoxLayout; addButtonLayout->addWidget(exportPlotButton); addButtonLayout->addStretch(); + addButtonLayout->addWidget(saveCanvasButton); + addButtonLayout->addWidget(loadCanvasButton); addButtonLayout->addWidget(addCanvasButton); addButtonLayout->setAlignment(Qt::AlignRight | Qt::AlignBottom); addButtonLayout->setContentsMargins(0, 0, 0, 0); @@ -186,6 +221,128 @@ PlotCanvas *PlotWindow::AddCanvas() return canvas; } +///////////////////////////////////////////////// +void PlotWindow::SavePlotLayout() { + QString _fileName = ""; + + XMLError _XMLError; + XMLDocument _plotLayout_XMLDocument; + XMLNode *_rootNode = nullptr; + XMLElement *_canvas_XMLElement = nullptr; + XMLElement *_plot_XMLElement = nullptr; + XMLElement *_variable_XMLElement = nullptr; + + PlotCanvas *_canvas = nullptr; + + _fileName = QFileDialog::getSaveFileName(this, tr("Save Plot Layout"), "~", tr("XML File (*.xml)")); + + _rootNode = _plotLayout_XMLDocument.NewElement("PlotLayout"); + _plotLayout_XMLDocument.InsertFirstChild(_rootNode); + + for (int _canvasIndex = 0; _canvasIndex < this->dataPtr->canvasSplitter->count(); ++_canvasIndex) { + _canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(_canvasIndex)); + if (!_canvas) { + continue; + } + + _canvas_XMLElement = _plotLayout_XMLDocument.NewElement("Canvas"); + _rootNode->InsertEndChild(_canvas_XMLElement); + + for (const auto &_plot : _canvas->Plots()) { + _plot_XMLElement = _plotLayout_XMLDocument.NewElement("Plot"); + _canvas_XMLElement->InsertEndChild(_plot_XMLElement); + + for (const auto &_curve : _plot->Curves()) { + auto c = _curve.lock(); + if (!c) + continue; + + std::string _label = c->Label(); + + _variable_XMLElement = _plotLayout_XMLDocument.NewElement("Variable"); + + _variable_XMLElement->SetAttribute("Label", _label.c_str()); + + _plot_XMLElement->InsertEndChild(_variable_XMLElement); + } // for (const auto &_curve : _plot->Curves()) {} + } // for (const auto &_plot : _canvas->Plots()) { + } // for (int _canvasIndex = 0; _canvasIndex < this->dataPtr->canvasSplitter->count(); ++_canvasIndex) { + + // todo: treat XMLError + _XMLError = _plotLayout_XMLDocument.SaveFile(_fileName.toLocal8Bit()); +} // void PlotWindow::SavePlotLayout() { + +///////////////////////////////////////////////// +void PlotWindow::LoadPlotLayout() { + QString _fileName = ""; + + XMLError _XMLError; + XMLDocument _plotLayout_XMLDocument; + XMLNode *_rootNode = nullptr; + XMLElement *_canvas_XMLElement = nullptr; + XMLElement *_plot_XMLElement = nullptr; + XMLElement *_variable_XMLElement = nullptr; + + PlotCanvas *_canvas = nullptr; + + std::string _label; + + std::vector _plotVector; + IncrementalPlot *_plot = nullptr; + int _size = 0; + + _fileName = QFileDialog::getOpenFileName(this, tr("Load Plot Layout"), "~", tr("XML File (*.xml)")); + + if (_fileName == "") { + return; + } + + this->Clear(); + + _XMLError = _plotLayout_XMLDocument.LoadFile(_fileName.toLocal8Bit()); + + _rootNode = _plotLayout_XMLDocument.FirstChild(); + if (_rootNode == nullptr) + return; + + _canvas_XMLElement = _rootNode->FirstChildElement("Canvas"); + while (_canvas_XMLElement != nullptr) { + _canvas = this->AddCanvas(); + + if(!_canvas) { + return; + } + + _plot_XMLElement = _canvas_XMLElement->FirstChildElement("Plot"); + while (_plot_XMLElement != nullptr) { + _variable_XMLElement = _plot_XMLElement->FirstChildElement("Variable"); + while (_variable_XMLElement != nullptr) { + + const char *tempChars = _variable_XMLElement->Attribute("Label"); + if (tempChars != nullptr) { + std::string _label = tempChars; + + _plotVector = _canvas->Plots(); + _size = _plotVector.size(); + + if (_size == 0) { + _canvas->AddVariable(_label); + } else { // if (_size == 0) { + _plot = _plotVector.back(); + _canvas->AddVariable(_label, _plot); + } // } else { // if (_size == 0) { + } // if (tempChars != nullptr) { + + _variable_XMLElement = _variable_XMLElement->NextSiblingElement("Variable"); + } // while (_variable_XMLElement != nullptr) { + + _plot_XMLElement = _plot_XMLElement->NextSiblingElement("Plot"); + } // while (_plot_XMLElement != nullptr) + + _canvas_XMLElement = _canvas_XMLElement->NextSiblingElement("Canvas"); + } // while (_canvas_XMLElement != nullptr) +} // void PlotWindow::LoadPlotLayout() { + ///////////////////////////////////////////////// void PlotWindow::RemoveCanvas(PlotCanvas *_canvas) { @@ -203,8 +360,7 @@ void PlotWindow::Clear() { while (this->CanvasCount() > 0u) { - PlotCanvas *canvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(0)); + PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(0)); this->RemoveCanvas(canvas); } } @@ -221,6 +377,18 @@ void PlotWindow::OnAddCanvas() this->AddCanvas(); } +///////////////////////////////////////////////// +void PlotWindow::OnSaveCanvas() +{ + this->SavePlotLayout(); +} + +///////////////////////////////////////////////// +void PlotWindow::OnLoadCanvas() +{ + this->LoadPlotLayout(); +} + ///////////////////////////////////////////////// void PlotWindow::OnRemoveCanvas() { @@ -244,12 +412,10 @@ void PlotWindow::UpdateCanvas() { // disable Delete Canvas option in settings if there is only one // canvas in the window - PlotCanvas *plotCanvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(0)); + PlotCanvas *plotCanvas = qobject_cast(this->dataPtr->canvasSplitter->widget(0)); if (plotCanvas) { - plotCanvas->SetDeleteCanvasEnabled( - this->dataPtr->canvasSplitter->count() != 1); + plotCanvas->SetDeleteCanvasEnabled(this->dataPtr->canvasSplitter->count() != 1); } } @@ -261,8 +427,7 @@ void PlotWindow::Update() { for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); if (!canvas) continue; canvas->Restart(); @@ -272,8 +437,7 @@ void PlotWindow::Update() for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); if (!canvas) continue; canvas->Update(); @@ -308,8 +472,7 @@ void PlotWindow::OnExport() for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { bool hasData = false; - PlotCanvas *canvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); if (!canvas) continue; @@ -359,8 +522,7 @@ std::list PlotWindow::Plots() for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = - qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); if (!canvas) continue; diff --git a/gazebo/gui/plot/PlotWindow.hh b/gazebo/gui/plot/PlotWindow.hh index e2e9653fed..6d4dd1c451 100644 --- a/gazebo/gui/plot/PlotWindow.hh +++ b/gazebo/gui/plot/PlotWindow.hh @@ -47,6 +47,12 @@ namespace gazebo /// \brief Add a new canvas. public: PlotCanvas *AddCanvas(); + /// \brief Save canvas layout + public: void SavePlotLayout(); + + /// \brief Load canvas layout + public: void LoadPlotLayout(); + /// \brief Get a list of all the plots /// \return A list of all the plots. public: std::list Plots(); @@ -80,6 +86,12 @@ namespace gazebo /// \brief Qt Callback when a new plot canvas should be added. private slots: void OnAddCanvas(); + /// \brief Qt Callback when the canvas setup should be saved. + private slots: void OnSaveCanvas(); + + /// \brief Qt Callback when the canvas setup should be loaded. + private slots: void OnLoadCanvas(); + /// \brief Qt Callback when a plot canvas should be removed. private slots: void OnRemoveCanvas(); From 88e90f1aaef9d3edf27f9bdee2a0ab4737bfcc52 Mon Sep 17 00:00:00 2001 From: Michael Melchior Date: Tue, 20 Apr 2021 14:46:35 +0200 Subject: [PATCH 2/3] Reformatted Code Adjusted code to fit the max 80 characters per line rule. --- gazebo/gui/plot/PlotCanvas.cc | 1672 ++++++++++------------ gazebo/gui/plot/PlotWindow.cc | 656 +++++---- gazebo/gui/plot/VariablePillContainer.cc | 833 +++++------ 3 files changed, 1498 insertions(+), 1663 deletions(-) diff --git a/gazebo/gui/plot/PlotCanvas.cc b/gazebo/gui/plot/PlotCanvas.cc index fa39f26021..71ac030a3f 100644 --- a/gazebo/gui/plot/PlotCanvas.cc +++ b/gazebo/gui/plot/PlotCanvas.cc @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ #include #include @@ -36,49 +36,55 @@ using namespace gazebo; using namespace gui; -namespace gazebo -{ - namespace gui - { - /// \brief Helper data structure to store plot data - class PlotData - { - /// \brief Unique id of the plot - public: unsigned int id; +namespace gazebo { +namespace gui { +/// \brief Helper data structure to store plot data +class PlotData { + /// \brief Unique id of the plot +public: + unsigned int id; - /// brief Pointer to the plot - public: IncrementalPlot *plot = nullptr; + /// brief Pointer to the plot +public: + IncrementalPlot *plot = nullptr; - /// \brief A map of container variable ids to their plot curve ids. - public: std::map variableCurves; - }; + /// \brief A map of container variable ids to their plot curve ids. +public: + std::map variableCurves; +}; - /// \internal - /// \brief PlotCanvas private data - class PlotCanvasPrivate - { - /// \brief Text label - public: EditableLabel *title; +/// \internal +/// \brief PlotCanvas private data +class PlotCanvasPrivate { + /// \brief Text label +public: + EditableLabel *title; - /// \brief Splitter that contains all the plots. - public: QSplitter *plotSplitter; + /// \brief Splitter that contains all the plots. +public: + QSplitter *plotSplitter; - /// \brief A map of plot id to plot data; - public: std::map plotData; + /// \brief A map of plot id to plot data; +public: + std::map plotData; - /// \brief Pointer to an empty plot. - public: IncrementalPlot *emptyPlot = nullptr; + /// \brief Pointer to an empty plot. +public: + IncrementalPlot *emptyPlot = nullptr; - /// \brief Container for all the variableCurves on the Y axis. - public: VariablePillContainer *yVariableContainer = nullptr; + /// \brief Container for all the variableCurves on the Y axis. +public: + VariablePillContainer *yVariableContainer = nullptr; - /// \brief Delete canvas Qt action - public: QAction *deleteCanvasAct = nullptr; + /// \brief Delete canvas Qt action +public: + QAction *deleteCanvasAct = nullptr; - /// \brief Global plot counter. - public: static unsigned int globalPlotId; - }; - } + /// \brief Global plot counter. +public: + static unsigned int globalPlotId; +}; +} } // empty plot id @@ -88,915 +94,829 @@ const unsigned int PlotCanvas::EmptyPlot = ignition::math::MAX_UI32; unsigned int PlotCanvasPrivate::globalPlotId = 0; ///////////////////////////////////////////////// -PlotCanvas::PlotCanvas(QWidget *_parent) - : QWidget(_parent), - dataPtr(new PlotCanvasPrivate()) -{ - this->setObjectName("plotCanvas"); - - // Plot title - this->dataPtr->title = new EditableLabel("Plot Name"); - - QHBoxLayout *titleLayout = new QHBoxLayout; - titleLayout->addWidget(this->dataPtr->title); - titleLayout->setAlignment(Qt::AlignHCenter); - - // Settings - QMenu *settingsMenu = new QMenu; - settingsMenu->setObjectName("material"); - QAction *clearPlotAct = new QAction("Clear all fields", settingsMenu); - clearPlotAct->setStatusTip(tr("Clear variables and all plots on canvas")); - connect(clearPlotAct, SIGNAL(triggered()), this, SLOT(OnClearCanvas())); - - this->dataPtr->deleteCanvasAct = new QAction("Delete canvas", settingsMenu); - this->dataPtr->deleteCanvasAct->setStatusTip(tr("Delete entire canvas")); - connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, SLOT(OnDeleteCanvas())); - - QAction *showGridAct = new QAction("Show grid", settingsMenu); - showGridAct->setStatusTip(tr("Show/hide grid lines on plot")); - showGridAct->setCheckable(true); - - QAction *showHoverLineAct = new QAction("Show hover line", settingsMenu); - showHoverLineAct->setStatusTip(tr("Show hover line")); - showHoverLineAct->setCheckable(true); - connect(showHoverLineAct, SIGNAL(triggered()), this, SLOT(OnShowHoverLine())); - - settingsMenu->addAction(clearPlotAct); - settingsMenu->addAction(this->dataPtr->deleteCanvasAct); - settingsMenu->addAction(showGridAct); - settingsMenu->addAction(showHoverLineAct); - - QToolButton *settingsButton = new QToolButton(); - settingsButton->setObjectName("plotCanvasTitleTool"); - settingsButton->installEventFilter(this); - settingsButton->setToolTip(tr("Settings")); - settingsButton->setIcon(QIcon(":/images/settings.svg")); - settingsButton->setIconSize(QSize(25, 25)); - settingsButton->setFixedSize(QSize(45, 35)); - settingsButton->setToolButtonStyle(Qt::ToolButtonIconOnly); - settingsButton->setPopupMode(QToolButton::InstantPopup); - settingsButton->setMenu(settingsMenu); - - QHBoxLayout *settingsLayout = new QHBoxLayout; - settingsLayout->addWidget(settingsButton); - - QHBoxLayout *titleSettingsLayout = new QHBoxLayout; - titleSettingsLayout->addLayout(titleLayout); - titleSettingsLayout->addLayout(settingsLayout); - titleSettingsLayout->setContentsMargins(0, 0, 0, 0); - - QFrame *titleFrame = new QFrame; - titleFrame->setObjectName("plotCanvasTitleFrame"); - titleFrame->setLayout(titleSettingsLayout); - - // X and Y variable containers - VariablePillContainer *xVariableContainer = new VariablePillContainer(this); - xVariableContainer->SetText("x "); - xVariableContainer->SetMaxSize(1); - xVariableContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - xVariableContainer->setContentsMargins(0, 0, 0, 0); - // \todo: fix hardcoded x axis - xVariableContainer->AddVariablePill("sim_time"); - xVariableContainer->setEnabled(false); - - this->dataPtr->yVariableContainer = new VariablePillContainer(this); - this->dataPtr->yVariableContainer->SetText("y "); - this->dataPtr->yVariableContainer->setSizePolicy( - QSizePolicy::Expanding, QSizePolicy::Fixed); - this->dataPtr->yVariableContainer->setContentsMargins(0, 0, 0, 0); - - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), - this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableRemoved(unsigned int, unsigned int)), - this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableMoved(unsigned int, unsigned int)), - this, SLOT(OnMoveVariable(unsigned int, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableLabelChanged(unsigned int, std::string)), - this, SLOT(OnSetVariableLabel(unsigned int, std::string))); - - QVBoxLayout *variableContainerLayout = new QVBoxLayout; - variableContainerLayout->addWidget(xVariableContainer); - variableContainerLayout->addWidget(this->dataPtr->yVariableContainer); - variableContainerLayout->setSpacing(0); - variableContainerLayout->setContentsMargins(0, 0, 0, 0); - - // plot - QScrollArea *plotScrollArea = new QScrollArea(this); - plotScrollArea->setObjectName("plotScrollArea"); - plotScrollArea->setLineWidth(0); - plotScrollArea->setFrameShape(QFrame::NoFrame); - plotScrollArea->setFrameShadow(QFrame::Plain); - plotScrollArea->setSizePolicy(QSizePolicy::Minimum, - QSizePolicy::Expanding); - - plotScrollArea->setWidgetResizable(true); - plotScrollArea->viewport()->installEventFilter(this); - - QFrame *plotFrame = new QFrame(plotScrollArea); - plotFrame->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); - plotFrame->setObjectName("plotCanvasPlotFrame"); - QVBoxLayout *plotLayout = new QVBoxLayout; - plotFrame->setLayout(plotLayout); - - this->dataPtr->plotSplitter = new QSplitter(Qt::Vertical); - this->dataPtr->plotSplitter->setVisible(false); - plotLayout->addWidget(this->dataPtr->plotSplitter); - - plotScrollArea->setWidget(plotFrame); - - // empty plot - this->dataPtr->emptyPlot = new IncrementalPlot(this); - connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), this, SLOT(OnAddVariable(std::string))); - plotLayout->addWidget(this->dataPtr->emptyPlot); - - // set initial show grid state - showGridAct->setChecked(this->dataPtr->emptyPlot->IsShowGrid()); - connect(showGridAct, SIGNAL(triggered()), this, SLOT(OnShowGrid())); - - QFrame *mainFrame = new QFrame; - mainFrame->setObjectName("plotCanvasFrame"); - QVBoxLayout *mainFrameLayout = new QVBoxLayout; - mainFrameLayout->addWidget(titleFrame); - mainFrameLayout->addLayout(variableContainerLayout); - mainFrameLayout->addWidget(plotScrollArea); - mainFrameLayout->setContentsMargins(0, 0, 0, 0); - mainFrame->setLayout(mainFrameLayout); - - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(mainFrame); - mainLayout->setContentsMargins(0, 0, 0, 0); - this->setLayout(mainLayout); -} - -///////////////////////////////////////////////// -PlotCanvas::~PlotCanvas() -{ - this->Clear(); +PlotCanvas::PlotCanvas(QWidget *_parent) : + QWidget(_parent), dataPtr(new PlotCanvasPrivate()) { + this->setObjectName("plotCanvas"); + + // Plot title + this->dataPtr->title = new EditableLabel("Plot Name"); + + QHBoxLayout *titleLayout = new QHBoxLayout; + titleLayout->addWidget(this->dataPtr->title); + titleLayout->setAlignment(Qt::AlignHCenter); + + // Settings + QMenu *settingsMenu = new QMenu; + settingsMenu->setObjectName("material"); + QAction *clearPlotAct = new QAction("Clear all fields", settingsMenu); + clearPlotAct->setStatusTip(tr("Clear variables and all plots on canvas")); + connect(clearPlotAct, SIGNAL(triggered()), this, SLOT(OnClearCanvas())); + + this->dataPtr->deleteCanvasAct = new QAction("Delete canvas", settingsMenu); + this->dataPtr->deleteCanvasAct->setStatusTip(tr("Delete entire canvas")); + connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, + SLOT(OnDeleteCanvas())); + + QAction *showGridAct = new QAction("Show grid", settingsMenu); + showGridAct->setStatusTip(tr("Show/hide grid lines on plot")); + showGridAct->setCheckable(true); + + QAction *showHoverLineAct = new QAction("Show hover line", settingsMenu); + showHoverLineAct->setStatusTip(tr("Show hover line")); + showHoverLineAct->setCheckable(true); + connect(showHoverLineAct, SIGNAL(triggered()), this, + SLOT(OnShowHoverLine())); + + settingsMenu->addAction(clearPlotAct); + settingsMenu->addAction(this->dataPtr->deleteCanvasAct); + settingsMenu->addAction(showGridAct); + settingsMenu->addAction(showHoverLineAct); + + QToolButton *settingsButton = new QToolButton(); + settingsButton->setObjectName("plotCanvasTitleTool"); + settingsButton->installEventFilter(this); + settingsButton->setToolTip(tr("Settings")); + settingsButton->setIcon(QIcon(":/images/settings.svg")); + settingsButton->setIconSize(QSize(25, 25)); + settingsButton->setFixedSize(QSize(45, 35)); + settingsButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + settingsButton->setPopupMode(QToolButton::InstantPopup); + settingsButton->setMenu(settingsMenu); + + QHBoxLayout *settingsLayout = new QHBoxLayout; + settingsLayout->addWidget(settingsButton); + + QHBoxLayout *titleSettingsLayout = new QHBoxLayout; + titleSettingsLayout->addLayout(titleLayout); + titleSettingsLayout->addLayout(settingsLayout); + titleSettingsLayout->setContentsMargins(0, 0, 0, 0); + + QFrame *titleFrame = new QFrame; + titleFrame->setObjectName("plotCanvasTitleFrame"); + titleFrame->setLayout(titleSettingsLayout); + + // X and Y variable containers + VariablePillContainer *xVariableContainer = new VariablePillContainer(this); + xVariableContainer->SetText("x "); + xVariableContainer->SetMaxSize(1); + xVariableContainer->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Fixed); + xVariableContainer->setContentsMargins(0, 0, 0, 0); + // \todo: fix hardcoded x axis + xVariableContainer->AddVariablePill("sim_time"); + xVariableContainer->setEnabled(false); + + this->dataPtr->yVariableContainer = new VariablePillContainer(this); + this->dataPtr->yVariableContainer->SetText("y "); + this->dataPtr->yVariableContainer->setSizePolicy(QSizePolicy::Expanding, + QSizePolicy::Fixed); + this->dataPtr->yVariableContainer->setContentsMargins(0, 0, 0, 0); + + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), + this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableRemoved(unsigned int, unsigned int)), + this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableMoved(unsigned int, unsigned int)), + this, SLOT(OnMoveVariable(unsigned int, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableLabelChanged(unsigned int, std::string)), + this, SLOT(OnSetVariableLabel(unsigned int, std::string))); + + QVBoxLayout *variableContainerLayout = new QVBoxLayout; + variableContainerLayout->addWidget(xVariableContainer); + variableContainerLayout->addWidget(this->dataPtr->yVariableContainer); + variableContainerLayout->setSpacing(0); + variableContainerLayout->setContentsMargins(0, 0, 0, 0); + + // plot + QScrollArea *plotScrollArea = new QScrollArea(this); + plotScrollArea->setObjectName("plotScrollArea"); + plotScrollArea->setLineWidth(0); + plotScrollArea->setFrameShape(QFrame::NoFrame); + plotScrollArea->setFrameShadow(QFrame::Plain); + plotScrollArea->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + plotScrollArea->setWidgetResizable(true); + plotScrollArea->viewport()->installEventFilter(this); + + QFrame *plotFrame = new QFrame(plotScrollArea); + plotFrame->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + plotFrame->setObjectName("plotCanvasPlotFrame"); + QVBoxLayout *plotLayout = new QVBoxLayout; + plotFrame->setLayout(plotLayout); + + this->dataPtr->plotSplitter = new QSplitter(Qt::Vertical); + this->dataPtr->plotSplitter->setVisible(false); + plotLayout->addWidget(this->dataPtr->plotSplitter); + + plotScrollArea->setWidget(plotFrame); + + // empty plot + this->dataPtr->emptyPlot = new IncrementalPlot(this); + connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), this, + SLOT(OnAddVariable(std::string))); + plotLayout->addWidget(this->dataPtr->emptyPlot); + + // set initial show grid state + showGridAct->setChecked(this->dataPtr->emptyPlot->IsShowGrid()); + connect(showGridAct, SIGNAL(triggered()), this, SLOT(OnShowGrid())); + + QFrame *mainFrame = new QFrame; + mainFrame->setObjectName("plotCanvasFrame"); + QVBoxLayout *mainFrameLayout = new QVBoxLayout; + mainFrameLayout->addWidget(titleFrame); + mainFrameLayout->addLayout(variableContainerLayout); + mainFrameLayout->addWidget(plotScrollArea); + mainFrameLayout->setContentsMargins(0, 0, 0, 0); + mainFrame->setLayout(mainFrameLayout); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(mainFrame); + mainLayout->setContentsMargins(0, 0, 0, 0); + this->setLayout(mainLayout); +} + +///////////////////////////////////////////////// +PlotCanvas::~PlotCanvas() { + this->Clear(); } ///////////////////////////////////////////////// void PlotCanvas::SetVariableLabel(const unsigned int _id, - const std::string &_label) -{ - // set new variable labeland let the signals/slots do the work on updating - // the plot curve - return this->dataPtr->yVariableContainer->SetVariablePillLabel(_id, _label); -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::AddVariable(const std::string &_variable, const unsigned int _plotId) -{ - unsigned int targetId = VariablePill::EmptyVariable; - if (_plotId != EmptyPlot) - { - // find a variable that belongs to the specified plotId and make that the - // the target variable that the new variable will be added to - auto it = this->dataPtr->plotData.find(_plotId); - if (it != this->dataPtr->plotData.end() && !it->second->variableCurves.empty()) - { - targetId = it->second->variableCurves.begin()->first; - } - } - - // add to container and let the signals/slots do the work on adding the - // a new plot with the curve in the overloaded AddVariable function - unsigned int _retVal = this->dataPtr->yVariableContainer->AddVariablePill(_variable, targetId); - - return _retVal; -} - -///////////////////////////////////////////////// -void PlotCanvas::AddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _plotId) -{ - unsigned int plotId; - if (_plotId == EmptyPlot) - { - // create new plot for the variable and add plot to canvas - plotId = this->AddPlot(); - } - else - plotId = _plotId; - - // add variable to existing plot - auto it = this->dataPtr->plotData.find(plotId); - if (it == this->dataPtr->plotData.end()) - return; - - PlotData *p = it->second; - PlotCurveWeakPtr curve = p->plot->AddCurve(_variable); - auto c = curve.lock(); - if (c) - { - p->variableCurves[_id] = c->Id(); - } - else - { - gzerr << "Unable to add curve to plot" << std::endl; - return; - } - - // hide initial empty plot - if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) - { - this->dataPtr->emptyPlot->setVisible(false); - this->dataPtr->plotSplitter->setVisible(true); - } - - - if (common::URI::Valid(_variable)) - { - common::URI uri(_variable); - std::string schemeStr = uri.Scheme(); - if (schemeStr == "data") - { - PlotManager::Instance()->AddIntrospectionCurve(_variable, curve); - // give it a more compact, friendly name - // do this after PlotManager AddIntrospectionCurve call! - std::string label = PlotManager::Instance()->HumanReadableName(_variable); - this->SetVariableLabel(_id, label); - } - } - else - { - PlotManager::Instance()->AddTopicCurve(_variable, curve); - } -} - - -///////////////////////////////////////////////// -void PlotCanvas::AddVariable(const std::string &_variable, IncrementalPlot *plot_in) -{ - if (!plot_in) - return; - - if (plot_in == this->dataPtr->emptyPlot) - { - // add new variable to new plot - this->AddVariable(_variable); - } - else - { - for (const auto &it : this->dataPtr->plotData) - { - if (plot_in == it.second->plot) - { - // add to existing plot - this->AddVariable(_variable, it.second->id); - return; - } - } - } -} - - -///////////////////////////////////////////////// -void PlotCanvas::RemoveVariable(const unsigned int _id, const unsigned int _plotId) -{ - auto it = this->dataPtr->plotData.end(); - if (_plotId == EmptyPlot) - { - // find which plot the variable belongs to - for (auto pIt = this->dataPtr->plotData.begin(); pIt != this->dataPtr->plotData.end(); ++pIt) - { - auto v = pIt->second->variableCurves.find(_id); - if (v != pIt->second->variableCurves.end()) - { - it = pIt; - break; - } - } - } - else - { - // get the plot which the variable belongs to - it = this->dataPtr->plotData.find(_plotId); - } - - if (it == this->dataPtr->plotData.end()) - return; - - auto v = it->second->variableCurves.find(_id); - if (v == it->second->variableCurves.end()) - return; - - unsigned int curveId = v->second; - - // remove curve from manager - PlotCurveWeakPtr plotCurve = it->second->plot->Curve(curveId); - std::string curveLabel; - { - auto pc = plotCurve.lock(); - curveLabel = pc->Label(); - } - // assume topic name starts with '/' - if (!curveLabel.empty() && curveLabel[0] == '/') - PlotManager::Instance()->RemoveTopicCurve(plotCurve); - else - PlotManager::Instance()->RemoveIntrospectionCurve(plotCurve); - - // erase from map - it->second->variableCurves.erase(v); - - // delete whole plot if no more curves - if (it->second->variableCurves.empty()) - { - it->second->plot->hide(); - it->second->plot->RemoveCurve(curveId); - delete it->second->plot; - delete it->second; - this->dataPtr->plotData.erase(it); - - this->UpdateAxisLabel(); - } - else - { - it->second->plot->RemoveCurve(curveId); - } - - if (this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) - { - this->dataPtr->emptyPlot->setVisible(true); - this->dataPtr->plotSplitter->setVisible(false); - } - - // remove from variable pill container - this->dataPtr->yVariableContainer->RemoveVariablePill(_id); -} - - -///////////////////////////////////////////////// -unsigned int PlotCanvas::AddPlot() -{ - IncrementalPlot *plot = new IncrementalPlot(this); - plot->setAutoDelete(false); - plot->ShowGrid(this->dataPtr->emptyPlot->IsShowGrid()); - plot->ShowHoverLine(this->dataPtr->emptyPlot->IsShowHoverLine()); - connect(plot, SIGNAL(VariableAdded(std::string)), this, SLOT(OnAddVariable(std::string))); - this->dataPtr->plotSplitter->addWidget(plot); - - PlotData *p = new PlotData; - p->id = this->dataPtr->globalPlotId++; - p->plot = plot; - this->dataPtr->plotData[p->id] = p; - - this->UpdateAxisLabel(); - - return p->id; -} - -///////////////////////////////////////////////// -void PlotCanvas::RemovePlot(const unsigned int _id) -{ - auto it = this->dataPtr->plotData.find(_id); - if (it == this->dataPtr->plotData.end()) - return; - - // remove the plot if it does not contain any variableCurves (curves) - if (it->second->variableCurves.empty()) - { - it->second->plot->hide(); - delete it->second->plot; - delete it->second; - this->dataPtr->plotData.erase(it); - return; - } - - unsigned int plotId = it->first; - // remove all variableCurves except last one - while (it->second->variableCurves.size() > 1) - { - auto v = it->second->variableCurves.begin(); - this->RemoveVariable(v->first, plotId); - } - - // remove last variable - this will also delete the plot which causes - // plot data iterator to be invalid. So do this last. - this->RemoveVariable(it->second->variableCurves.begin()->first, plotId); - - this->UpdateAxisLabel(); -} - -///////////////////////////////////////////////// -void PlotCanvas::Clear() -{ - while (!this->dataPtr->plotData.empty()) - { - auto p = this->dataPtr->plotData.begin(); - this->RemovePlot(p->first); - } -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::PlotByVariable(const unsigned int _variableId) const -{ - for (const auto it : this->dataPtr->plotData) - { - const auto v = it.second->variableCurves.find(_variableId); - if (v != it.second->variableCurves.end()) - { - return it.first; - } - } - return EmptyPlot; -} - -///////////////////////////////////////////////// -void PlotCanvas::OnAddVariable(const std::string &_variable) -{ - IncrementalPlot *plot = qobject_cast(QObject::sender()); - - if (!plot) - return; - - if (plot == this->dataPtr->emptyPlot) - { - // add new variable to new plot - this->AddVariable(_variable); - } - else - { - for (const auto &it : this->dataPtr->plotData) - { - if (plot == it.second->plot) - { - // add to existing plot - this->AddVariable(_variable, it.second->id); - return; - } - } - } -} - -///////////////////////////////////////////////// -void PlotCanvas::OnAddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _targetId) -{ - if (_targetId != VariablePill::EmptyVariable) - { - // Add a variable to existing plot - for (const auto it : this->dataPtr->plotData) - { - const auto v = it.second->variableCurves.find(_targetId); - if (v != it.second->variableCurves.end()) - { - this->AddVariable(_id, _variable, it.second->id); - break; - } - } - } - else - { - // add variable to new plot - this->AddVariable(_id, _variable); - } + const std::string &_label) { + // set new variable labeland let the signals/slots do the work on updating + // the plot curve + return this->dataPtr->yVariableContainer->SetVariablePillLabel(_id, _label); +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::AddVariable(const std::string &_variable, + const unsigned int _plotId) { + unsigned int targetId = VariablePill::EmptyVariable; + + if (_plotId != EmptyPlot) { + // find a variable that belongs to the specified plotId and make that the + // the target variable that the new variable will be added to + auto it = this->dataPtr->plotData.find(_plotId); + if (it != this->dataPtr->plotData.end() + && !it->second->variableCurves.empty()) { + targetId = it->second->variableCurves.begin()->first; + } + } + + // add to container and let the signals/slots do the work on adding the + // a new plot with the curve in the overloaded AddVariable function + unsigned int _retVal = this->dataPtr->yVariableContainer->AddVariablePill( + _variable, targetId); + + return _retVal; +} + +///////////////////////////////////////////////// +void PlotCanvas::AddVariable(const unsigned int _id, + const std::string &_variable, const unsigned int _plotId) { + unsigned int plotId; + if (_plotId == EmptyPlot) { + // create new plot for the variable and add plot to canvas + plotId = this->AddPlot(); + } else + plotId = _plotId; + + // add variable to existing plot + auto it = this->dataPtr->plotData.find(plotId); + if (it == this->dataPtr->plotData.end()) + return; + + PlotData *p = it->second; + PlotCurveWeakPtr curve = p->plot->AddCurve(_variable); + auto c = curve.lock(); + if (c) { + p->variableCurves[_id] = c->Id(); + } else { + gzerr << "Unable to add curve to plot" << std::endl; + return; + } + + // hide initial empty plot + if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { + this->dataPtr->emptyPlot->setVisible(false); + this->dataPtr->plotSplitter->setVisible(true); + } + + if (common::URI::Valid(_variable)) { + common::URI uri(_variable); + std::string schemeStr = uri.Scheme(); + if (schemeStr == "data") { + PlotManager::Instance()->AddIntrospectionCurve(_variable, curve); + // give it a more compact, friendly name + // do this after PlotManager AddIntrospectionCurve call! + std::string label = PlotManager::Instance()->HumanReadableName( + _variable); + this->SetVariableLabel(_id, label); + } + } else { + PlotManager::Instance()->AddTopicCurve(_variable, curve); + } +} + +///////////////////////////////////////////////// +void PlotCanvas::AddVariable(const std::string &_variable, + IncrementalPlot *plot_in) { + if (!plot_in) + return; + + if (plot_in == this->dataPtr->emptyPlot) { + // add new variable to new plot + this->AddVariable(_variable); + } else { + for (const auto &it : this->dataPtr->plotData) { + if (plot_in == it.second->plot) { + // add to existing plot + this->AddVariable(_variable, it.second->id); + return; + } + } + } +} + +///////////////////////////////////////////////// +void PlotCanvas::RemoveVariable(const unsigned int _id, + const unsigned int _plotId) { + auto it = this->dataPtr->plotData.end(); + if (_plotId == EmptyPlot) { + // find which plot the variable belongs to + for (auto pIt = this->dataPtr->plotData.begin(); + pIt != this->dataPtr->plotData.end(); ++pIt) { + auto v = pIt->second->variableCurves.find(_id); + if (v != pIt->second->variableCurves.end()) { + it = pIt; + break; + } + } + } else { + // get the plot which the variable belongs to + it = this->dataPtr->plotData.find(_plotId); + } + + if (it == this->dataPtr->plotData.end()) + return; + + auto v = it->second->variableCurves.find(_id); + if (v == it->second->variableCurves.end()) + return; + + unsigned int curveId = v->second; + + // remove curve from manager + PlotCurveWeakPtr plotCurve = it->second->plot->Curve(curveId); + std::string curveLabel; + { + auto pc = plotCurve.lock(); + curveLabel = pc->Label(); + } + // assume topic name starts with '/' + if (!curveLabel.empty() && curveLabel[0] == '/') + PlotManager::Instance()->RemoveTopicCurve(plotCurve); + else + PlotManager::Instance()->RemoveIntrospectionCurve(plotCurve); + + // erase from map + it->second->variableCurves.erase(v); + + // delete whole plot if no more curves + if (it->second->variableCurves.empty()) { + it->second->plot->hide(); + it->second->plot->RemoveCurve(curveId); + delete it->second->plot; + delete it->second; + this->dataPtr->plotData.erase(it); + + this->UpdateAxisLabel(); + } else { + it->second->plot->RemoveCurve(curveId); + } + + if (this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { + this->dataPtr->emptyPlot->setVisible(true); + this->dataPtr->plotSplitter->setVisible(false); + } + + // remove from variable pill container + this->dataPtr->yVariableContainer->RemoveVariablePill(_id); +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::AddPlot() { + IncrementalPlot *plot = new IncrementalPlot(this); + plot->setAutoDelete(false); + plot->ShowGrid(this->dataPtr->emptyPlot->IsShowGrid()); + plot->ShowHoverLine(this->dataPtr->emptyPlot->IsShowHoverLine()); + connect(plot, SIGNAL(VariableAdded(std::string)), this, + SLOT(OnAddVariable(std::string))); + this->dataPtr->plotSplitter->addWidget(plot); + + PlotData *p = new PlotData; + p->id = this->dataPtr->globalPlotId++; + p->plot = plot; + this->dataPtr->plotData[p->id] = p; + + this->UpdateAxisLabel(); + + return p->id; +} + +///////////////////////////////////////////////// +void PlotCanvas::RemovePlot(const unsigned int _id) { + auto it = this->dataPtr->plotData.find(_id); + if (it == this->dataPtr->plotData.end()) + return; + + // remove the plot if it does not contain any variableCurves (curves) + if (it->second->variableCurves.empty()) { + it->second->plot->hide(); + delete it->second->plot; + delete it->second; + this->dataPtr->plotData.erase(it); + return; + } + + unsigned int plotId = it->first; + // remove all variableCurves except last one + while (it->second->variableCurves.size() > 1) { + auto v = it->second->variableCurves.begin(); + this->RemoveVariable(v->first, plotId); + } + + // remove last variable - this will also delete the plot which causes + // plot data iterator to be invalid. So do this last. + this->RemoveVariable(it->second->variableCurves.begin()->first, plotId); + + this->UpdateAxisLabel(); +} + +///////////////////////////////////////////////// +void PlotCanvas::Clear() { + while (!this->dataPtr->plotData.empty()) { + auto p = this->dataPtr->plotData.begin(); + this->RemovePlot(p->first); + } +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::PlotByVariable(const unsigned int _variableId) const { + for (const auto it : this->dataPtr->plotData) { + const auto v = it.second->variableCurves.find(_variableId); + if (v != it.second->variableCurves.end()) { + return it.first; + } + } + return EmptyPlot; +} + +///////////////////////////////////////////////// +void PlotCanvas::OnAddVariable(const std::string &_variable) { +// gzdbg << "PlotCanvas::OnAddVariable: " << _variable << std::endl; + + IncrementalPlot *plot = qobject_cast(QObject::sender()); + + if (!plot) + return; + + if (plot == this->dataPtr->emptyPlot) { + // add new variable to new plot + this->AddVariable(_variable); + } else { + for (const auto &it : this->dataPtr->plotData) { + if (plot == it.second->plot) { + // add to existing plot + this->AddVariable(_variable, it.second->id); + return; + } + } + } +} + +///////////////////////////////////////////////// +void PlotCanvas::OnAddVariable(const unsigned int _id, + const std::string &_variable, const unsigned int _targetId) { + if (_targetId != VariablePill::EmptyVariable) { + // Add a variable to existing plot + for (const auto it : this->dataPtr->plotData) { + const auto v = it.second->variableCurves.find(_targetId); + if (v != it.second->variableCurves.end()) { + this->AddVariable(_id, _variable, it.second->id); + break; + } + } + } else { + // add variable to new plot + this->AddVariable(_id, _variable); + } } ///////////////////////////////////////////////// void PlotCanvas::OnRemoveVariable(const unsigned int _id, - const unsigned int /*_targetId*/) -{ - this->RemoveVariable(_id); -} - -///////////////////////////////////////////////// -void PlotCanvas::OnMoveVariable(const unsigned int _id, const unsigned int _targetId) -{ - printf("OnMoveVariable\n"); - auto plotIt = this->dataPtr->plotData.end(); - auto targetPlotIt = this->dataPtr->plotData.end(); - unsigned int curveId = 0; - - // find plot which the variable belongs to - // find target plot (if any) that the variable will be moved to - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) - { - auto v = it->second->variableCurves.find(_id); - if (v != it->second->variableCurves.end()) - { - plotIt = it; - curveId = v->second; - } - - if (it->second->variableCurves.find(_targetId) != - it->second->variableCurves.end()) - { - targetPlotIt = it; - } - - if (plotIt != this->dataPtr->plotData.end() && - targetPlotIt != this->dataPtr->plotData.end()) - break; - } - - // detach from old plot and attach to new one - if (plotIt != this->dataPtr->plotData.end()) - { - PlotData *p = plotIt->second; - - // detach variable from plot (qwt plot doesn't seem to do anything - // apart from setting the plot item to null) - PlotCurvePtr plotCurve = p->plot->DetachCurve(curveId); - p->variableCurves.erase(p->variableCurves.find(_id)); - - if (targetPlotIt != this->dataPtr->plotData.end()) - { - // attach variable to target plot - targetPlotIt->second->plot->AttachCurve(plotCurve); - targetPlotIt->second->variableCurves[_id] = plotCurve->Id(); - } - else - { - // create new plot - unsigned int plotId = this->AddPlot(); - auto it = this->dataPtr->plotData.find(plotId); - GZ_ASSERT(it != this->dataPtr->plotData.end(), "Failed to add new plot"); - PlotData *newPlotData = it->second; - // attach curve to plot - newPlotData->plot->AttachCurve(plotCurve); - newPlotData->variableCurves[_id] = plotCurve->Id(); - - // hide initial empty plot - if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) - { - this->dataPtr->emptyPlot->setVisible(false); - this->dataPtr->plotSplitter->setVisible(true); - } - } - - // delete plot if empty - if (p->variableCurves.empty()) - { - p->plot->hide(); - - // careful about deleting by iterator (plotIt) as it may have been - // changed if a new plot is added to the vector - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) - { - if (it->second == p) - { - this->dataPtr->plotData.erase(it); - break; - } - } - - p->plot->detachItems(QwtPlotItem::Rtti_PlotItem, false); - delete p->plot; - delete p; - - this->UpdateAxisLabel(); - } - } + const unsigned int /*_targetId*/) { + this->RemoveVariable(_id); +} + +///////////////////////////////////////////////// +void PlotCanvas::OnMoveVariable(const unsigned int _id, + const unsigned int _targetId) { + auto plotIt = this->dataPtr->plotData.end(); + auto targetPlotIt = this->dataPtr->plotData.end(); + unsigned int curveId = 0; + + // find plot which the variable belongs to + // find target plot (if any) that the variable will be moved to + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) { + auto v = it->second->variableCurves.find(_id); + if (v != it->second->variableCurves.end()) { + plotIt = it; + curveId = v->second; + } + + if (it->second->variableCurves.find(_targetId) + != it->second->variableCurves.end()) { + targetPlotIt = it; + } + + if (plotIt != this->dataPtr->plotData.end() + && targetPlotIt != this->dataPtr->plotData.end()) + break; + } + + // detach from old plot and attach to new one + if (plotIt != this->dataPtr->plotData.end()) { + PlotData *p = plotIt->second; + + // detach variable from plot (qwt plot doesn't seem to do anything + // apart from setting the plot item to null) + PlotCurvePtr plotCurve = p->plot->DetachCurve(curveId); + p->variableCurves.erase(p->variableCurves.find(_id)); + + if (targetPlotIt != this->dataPtr->plotData.end()) { + // attach variable to target plot + targetPlotIt->second->plot->AttachCurve(plotCurve); + targetPlotIt->second->variableCurves[_id] = plotCurve->Id(); + } else { + // create new plot + unsigned int plotId = this->AddPlot(); + auto it = this->dataPtr->plotData.find(plotId); + GZ_ASSERT(it != this->dataPtr->plotData.end(), + "Failed to add new plot"); + PlotData *newPlotData = it->second; + // attach curve to plot + newPlotData->plot->AttachCurve(plotCurve); + newPlotData->variableCurves[_id] = plotCurve->Id(); + + // hide initial empty plot + if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { + this->dataPtr->emptyPlot->setVisible(false); + this->dataPtr->plotSplitter->setVisible(true); + } + } + + // delete plot if empty + if (p->variableCurves.empty()) { + p->plot->hide(); + + // careful about deleting by iterator (plotIt) as it may have been + // changed if a new plot is added to the vector + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) { + if (it->second == p) { + this->dataPtr->plotData.erase(it); + break; + } + } + + p->plot->detachItems(QwtPlotItem::Rtti_PlotItem, false); + delete p->plot; + delete p; + + this->UpdateAxisLabel(); + } + } } ///////////////////////////////////////////////// void PlotCanvas::OnSetVariableLabel(const unsigned int _id, - const std::string &_label) -{ - // find plot which the variable belongs to - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) - { - auto v = it->second->variableCurves.find(_id); - if (v != it->second->variableCurves.end()) - { - it->second->plot->SetCurveLabel(v->second, _label); - break; - } - } -} - -///////////////////////////////////////////////// -void PlotCanvas::Update() -{ - // Update all the plots - for (auto p : this->dataPtr->plotData) - p.second->plot->Update(); -} - -///////////////////////////////////////////////// -void PlotCanvas::Restart() -{ - // tuple of original variable label, variable pointer, plot id to add - // variable to. - std::vector> - variableCurvesToClone; - - // Restart all the plots - for (const auto &it : this->dataPtr->plotData) - { - for (const auto &v : it.second->variableCurves) - { - unsigned int variableId = v.first; - unsigned int curveId = v.second; - - // get variable pill - VariablePill *variablePill = this->dataPtr->yVariableContainer->GetVariablePill(variableId); - - if (!variablePill) - continue; - - PlotCurveWeakPtr curve = it.second->plot->Curve(curveId); - auto c = curve.lock(); - if (!c) - continue; - - if (c->Active()) - { - c->SetActive(false); - // remove from manager so they don't get updated any more. - // assume topic name starts with '/' - std::string curveLabel = c->Label(); - if (!curveLabel.empty() && curveLabel[0] == '/') - PlotManager::Instance()->RemoveTopicCurve(c); - else - PlotManager::Instance()->RemoveIntrospectionCurve(c); - - // add to the list of variables to clone - variableCurvesToClone.push_back(std::make_tuple(variablePill->Text(), - variablePill, it.first)); - } - // increment curve age. - c->SetAge(c->Age()+1); - - // update the label of variable pill to show the age of the curve - std::string varText = variablePill->Text(); - std::stringstream ss; - if (c->Age() == 1u) - { - // update curve label and append curve age to the end - ss << varText << "_1"; - this->SetVariableLabel(variableId, ss.str()); - } - else - { - // if it's more than one simulation-run old, update the age suffix - size_t idx = varText.rfind("_"); - if (idx != std::string::npos) - { - int age = std::atoi(varText.substr(idx+1).c_str()); - if (age > 0) - { - ss << varText.substr(0, idx+1) << c->Age(); - this->SetVariableLabel(variableId, ss.str()); - } - } - } - } - } - - // add new copy of variable pill with original label - for (const auto &v : variableCurvesToClone) - { - std::string varText; - VariablePill *varPill; - unsigned int plotId; - std::tie(varText, varPill, plotId) = v; - - // add clones of the variable - this will also add it to the plot manager. - unsigned int id = this->AddVariable(varPill->Name(), plotId); - - // give it the original variable label - if (varText != varPill->Name()) - this->SetVariableLabel(id, varText); - } -} - -///////////////////////////////////////////////// -bool PlotCanvas::eventFilter(QObject *_o, QEvent *_e) -{ - if (_e->type() == QEvent::Wheel) - { - _e->ignore(); - return true; - } - - return QWidget::eventFilter(_o, _e); -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::PlotCount() const -{ - unsigned int plotCount = this->dataPtr->plotData.size(); - if (this->dataPtr->emptyPlot) - plotCount += (this->dataPtr->emptyPlot->isVisible() ? 1u : 0u); - - return plotCount; -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::VariableCount(const unsigned int _plotId) const -{ - auto it = this->dataPtr->plotData.find(_plotId); - if (it == this->dataPtr->plotData.end()) - return 0u; + const std::string &_label) { + // find plot which the variable belongs to + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) { + auto v = it->second->variableCurves.find(_id); + if (v != it->second->variableCurves.end()) { + it->second->plot->SetCurveLabel(v->second, _label); + break; + } + } +} + +///////////////////////////////////////////////// +void PlotCanvas::Update() { + // Update all the plots + for (auto p : this->dataPtr->plotData) + p.second->plot->Update(); +} + +///////////////////////////////////////////////// +void PlotCanvas::Restart() { + // tuple of original variable label, variable pointer, plot id to add + // variable to. + std::vector> variableCurvesToClone; + + // Restart all the plots + for (const auto &it : this->dataPtr->plotData) { + for (const auto &v : it.second->variableCurves) { + unsigned int variableId = v.first; + unsigned int curveId = v.second; + + // get variable pill + VariablePill *variablePill = + this->dataPtr->yVariableContainer->GetVariablePill( + variableId); + + if (!variablePill) + continue; + + PlotCurveWeakPtr curve = it.second->plot->Curve(curveId); + auto c = curve.lock(); + if (!c) + continue; + + if (c->Active()) { + c->SetActive(false); + // remove from manager so they don't get updated any more. + // assume topic name starts with '/' + std::string curveLabel = c->Label(); + if (!curveLabel.empty() && curveLabel[0] == '/') + PlotManager::Instance()->RemoveTopicCurve(c); + else + PlotManager::Instance()->RemoveIntrospectionCurve(c); + + // add to the list of variables to clone + variableCurvesToClone.push_back( + std::make_tuple(variablePill->Text(), variablePill, + it.first)); + } + // increment curve age. + c->SetAge(c->Age() + 1); + + // update the label of variable pill to show the age of the curve + std::string varText = variablePill->Text(); + std::stringstream ss; + if (c->Age() == 1u) { + // update curve label and append curve age to the end + ss << varText << "_1"; + this->SetVariableLabel(variableId, ss.str()); + } else { + // if it's more than one simulation-run old, update the age suffix + size_t idx = varText.rfind("_"); + if (idx != std::string::npos) { + int age = std::atoi(varText.substr(idx + 1).c_str()); + if (age > 0) { + ss << varText.substr(0, idx + 1) << c->Age(); + this->SetVariableLabel(variableId, ss.str()); + } + } + } + } + } - return it->second->variableCurves.size(); + // add new copy of variable pill with original label + for (const auto &v : variableCurvesToClone) { + std::string varText; + VariablePill *varPill; + unsigned int plotId; + std::tie(varText, varPill, plotId) = v; + + // add clones of the variable - this will also add it to the plot manager. + unsigned int id = this->AddVariable(varPill->Name(), plotId); + + // give it the original variable label + if (varText != varPill->Name()) + this->SetVariableLabel(id, varText); + } +} + +///////////////////////////////////////////////// +bool PlotCanvas::eventFilter(QObject *_o, QEvent *_e) { + if (_e->type() == QEvent::Wheel) { + _e->ignore(); + return true; + } + + return QWidget::eventFilter(_o, _e); } - -///////////////////////////////////////////////// -PlotCurveWeakPtr PlotCanvas::PlotCurve(const unsigned int _variableId) -{ - for (const auto it : this->dataPtr->plotData) - { - const auto v = it.second->variableCurves.find(_variableId); - if (v != it.second->variableCurves.end()) - { - return it.second->plot->Curve(v->second); - } - } - return PlotCurveWeakPtr(); + +///////////////////////////////////////////////// +unsigned int PlotCanvas::PlotCount() const { + unsigned int plotCount = this->dataPtr->plotData.size(); + if (this->dataPtr->emptyPlot) + plotCount += (this->dataPtr->emptyPlot->isVisible() ? 1u : 0u); + + return plotCount; +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::VariableCount(const unsigned int _plotId) const { + auto it = this->dataPtr->plotData.find(_plotId); + if (it == this->dataPtr->plotData.end()) + return 0u; + + return it->second->variableCurves.size(); +} + +///////////////////////////////////////////////// +PlotCurveWeakPtr PlotCanvas::PlotCurve(const unsigned int _variableId) { + for (const auto it : this->dataPtr->plotData) { + const auto v = it.second->variableCurves.find(_variableId); + if (v != it.second->variableCurves.end()) { + return it.second->plot->Curve(v->second); + } + } + return PlotCurveWeakPtr(); } ///////////////////////////////////////////////// -std::vector PlotCanvas::Plots() const -{ - std::vector plots; - for (const auto it : this->dataPtr->plotData) - plots.push_back(it.second->plot); +std::vector PlotCanvas::Plots() const { + std::vector plots; + for (const auto it : this->dataPtr->plotData) + plots.push_back(it.second->plot); - return plots; + return plots; } ///////////////////////////////////////////////// -void PlotCanvas::OnClearCanvas() -{ - // Ask for confirmation - std::string msg = "Are you sure you want to clear all fields? \n\n" - "This will remove all the plots in this canvas. \n"; +void PlotCanvas::OnClearCanvas() { + // Ask for confirmation + std::string msg = "Are you sure you want to clear all fields? \n\n" + "This will remove all the plots in this canvas. \n"; - QMessageBox msgBox(QMessageBox::Warning, QString("Clear canvas?"), - QString(msg.c_str())); - msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | - Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); + QMessageBox msgBox(QMessageBox::Warning, QString("Clear canvas?"), + QString(msg.c_str())); + msgBox.setWindowFlags( + Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint + | Qt::CustomizeWindowHint); - QPushButton *cancelButton = - msgBox.addButton("Cancel", QMessageBox::RejectRole); - QPushButton *clearButton = msgBox.addButton("Clear", - QMessageBox::AcceptRole); - msgBox.setDefaultButton(clearButton); - msgBox.setEscapeButton(cancelButton); - msgBox.show(); - msgBox.move(this->mapToGlobal(this->pos())); - msgBox.exec(); - if (msgBox.clickedButton() != clearButton) - return; + QPushButton *cancelButton = msgBox.addButton("Cancel", + QMessageBox::RejectRole); + QPushButton *clearButton = msgBox.addButton("Clear", + QMessageBox::AcceptRole); + msgBox.setDefaultButton(clearButton); + msgBox.setEscapeButton(cancelButton); + msgBox.show(); + msgBox.move(this->mapToGlobal(this->pos())); + msgBox.exec(); + if (msgBox.clickedButton() != clearButton) + return; - this->Clear(); + this->Clear(); } ///////////////////////////////////////////////// -void PlotCanvas::OnDeleteCanvas() -{ - // Ask for confirmation - std::string msg = "Are you sure you want to delete the entire canvas? \n"; +void PlotCanvas::OnDeleteCanvas() { + // Ask for confirmation + std::string msg = "Are you sure you want to delete the entire canvas? \n"; - QMessageBox msgBox(QMessageBox::Warning, QString("Delete canvas?"), - QString(msg.c_str())); - msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | - Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); + QMessageBox msgBox(QMessageBox::Warning, QString("Delete canvas?"), + QString(msg.c_str())); + msgBox.setWindowFlags( + Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint + | Qt::CustomizeWindowHint); - QPushButton *cancelButton = - msgBox.addButton("Cancel", QMessageBox::RejectRole); - QPushButton *deleteButton = msgBox.addButton("Delete", - QMessageBox::AcceptRole); - msgBox.setDefaultButton(deleteButton); - msgBox.setEscapeButton(cancelButton); - msgBox.show(); - msgBox.move(this->mapToGlobal(this->pos())); - msgBox.exec(); - if (msgBox.clickedButton() != deleteButton) - return; + QPushButton *cancelButton = msgBox.addButton("Cancel", + QMessageBox::RejectRole); + QPushButton *deleteButton = msgBox.addButton("Delete", + QMessageBox::AcceptRole); + msgBox.setDefaultButton(deleteButton); + msgBox.setEscapeButton(cancelButton); + msgBox.show(); + msgBox.move(this->mapToGlobal(this->pos())); + msgBox.exec(); + if (msgBox.clickedButton() != deleteButton) + return; - emit CanvasDeleted(); + emit CanvasDeleted(); } ///////////////////////////////////////////////// -void PlotCanvas::OnShowGrid() -{ - this->dataPtr->emptyPlot->ShowGrid(!this->dataPtr->emptyPlot->IsShowGrid()); +void PlotCanvas::OnShowGrid() { + this->dataPtr->emptyPlot->ShowGrid(!this->dataPtr->emptyPlot->IsShowGrid()); - for (const auto &it : this->dataPtr->plotData) - it.second->plot->ShowGrid(!it.second->plot->IsShowGrid()); + for (const auto &it : this->dataPtr->plotData) + it.second->plot->ShowGrid(!it.second->plot->IsShowGrid()); } ///////////////////////////////////////////////// -void PlotCanvas::OnShowHoverLine() -{ - this->dataPtr->emptyPlot->ShowHoverLine( - !this->dataPtr->emptyPlot->IsShowHoverLine()); +void PlotCanvas::OnShowHoverLine() { + this->dataPtr->emptyPlot->ShowHoverLine( + !this->dataPtr->emptyPlot->IsShowHoverLine()); - for (const auto &it : this->dataPtr->plotData) - it.second->plot->ShowHoverLine(!it.second->plot->IsShowHoverLine()); + for (const auto &it : this->dataPtr->plotData) + it.second->plot->ShowHoverLine(!it.second->plot->IsShowHoverLine()); } ///////////////////////////////////////////////// -void PlotCanvas::UpdateAxisLabel() -{ - // show the x-axis label in the last plot only - for (int i = 0; i < this->dataPtr->plotSplitter->count(); ++i) - { - IncrementalPlot *p = - qobject_cast(this->dataPtr->plotSplitter->widget(i)); +void PlotCanvas::UpdateAxisLabel() { + // show the x-axis label in the last plot only + for (int i = 0; i < this->dataPtr->plotSplitter->count(); ++i) { + IncrementalPlot *p = qobject_cast( + this->dataPtr->plotSplitter->widget(i)); - if (p) - { - p->ShowAxisLabel(IncrementalPlot::X_BOTTOM_AXIS, - i == (this->dataPtr->plotSplitter->count()-1)); - } - } + if (p) { + p->ShowAxisLabel(IncrementalPlot::X_BOTTOM_AXIS, + i == (this->dataPtr->plotSplitter->count() - 1)); + } + } } ///////////////////////////////////////////////// -void PlotCanvas::SetDeleteCanvasEnabled(const bool _enable) -{ - if (this->dataPtr->deleteCanvasAct) - this->dataPtr->deleteCanvasAct->setEnabled(_enable); +void PlotCanvas::SetDeleteCanvasEnabled(const bool _enable) { + if (this->dataPtr->deleteCanvasAct) + this->dataPtr->deleteCanvasAct->setEnabled(_enable); } ///////////////////////////////////////////////// -std::string PlotCanvas::Title() const -{ - return this->dataPtr->title->Text(); +std::string PlotCanvas::Title() const { + return this->dataPtr->title->Text(); } ///////////////////////////////////////////////// -void PlotCanvas::Export(const std::string &_dirName, const FileType _type) const -{ - std::string title = this->Title(); +void PlotCanvas::Export(const std::string &_dirName, + const FileType _type) const { + std::string title = this->Title(); - // Cleanup the title - std::replace(title.begin(), title.end(), '/', '_'); - std::replace(title.begin(), title.end(), '?', ':'); + // Cleanup the title + std::replace(title.begin(), title.end(), '/', '_'); + std::replace(title.begin(), title.end(), '?', ':'); - std::string filePrefix = _dirName + "/" + title; + std::string filePrefix = _dirName + "/" + title; - if (_type == FileType::PDFFile) - this->ExportPDF(filePrefix); - else if (_type == FileType::CSVFile) - this->ExportCSV(filePrefix); + if (_type == FileType::PDFFile) + this->ExportPDF(filePrefix); + else if (_type == FileType::CSVFile) + this->ExportCSV(filePrefix); } ///////////////////////////////////////////////// -void PlotCanvas::ExportPDF(const std::string &_filePrefix) const -{ - // Render the plot to a PDF - int index = 0; - for (const auto it : this->dataPtr->plotData) - { - std::string suffix = - this->dataPtr->plotData.size() > 1 ? std::to_string(index) : ""; +void PlotCanvas::ExportPDF(const std::string &_filePrefix) const { + // Render the plot to a PDF + int index = 0; + for (const auto it : this->dataPtr->plotData) { + std::string suffix = + this->dataPtr->plotData.size() > 1 ? std::to_string(index) : ""; - std::string filename = - common::unique_file_path(_filePrefix + suffix, "pdf"); + std::string filename = common::unique_file_path(_filePrefix + suffix, + "pdf"); - IncrementalPlot *plot = it.second->plot; + IncrementalPlot *plot = it.second->plot; - QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), plot->canvas()->height()); + QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), + plot->canvas()->height()); - QwtPlotRenderer renderer; - renderer.renderDocument(plot, QString(filename.c_str()), docSize, 20); + QwtPlotRenderer renderer; + renderer.renderDocument(plot, QString(filename.c_str()), docSize, 20); - gzmsg << "Plot exported to file [" << filename << "]" << std::endl; + gzmsg << "Plot exported to file [" << filename << "]" << std::endl; - index++; - } + index++; + } } ///////////////////////////////////////////////// -void PlotCanvas::ExportCSV(const std::string &_filePrefix) const -{ - // Save data from each curve into a separate file. - for (const auto it : this->dataPtr->plotData) - { - for (const auto &curve : it.second->plot->Curves()) - { - auto c = curve.lock(); - if (!c) - continue; +void PlotCanvas::ExportCSV(const std::string &_filePrefix) const { + // Save data from each curve into a separate file. + for (const auto it : this->dataPtr->plotData) { + for (const auto &curve : it.second->plot->Curves()) { + auto c = curve.lock(); + if (!c) + continue; - // Cleanup the label - std::string label = c->Label(); - std::replace(label.begin(), label.end(), '/', '_'); - std::replace(label.begin(), label.end(), '?', ':'); + // Cleanup the label + std::string label = c->Label(); + std::replace(label.begin(), label.end(), '/', '_'); + std::replace(label.begin(), label.end(), '?', ':'); - std::string filename = common::unique_file_path(_filePrefix + "-" + label, "csv"); + std::string filename = common::unique_file_path( + _filePrefix + "-" + label, "csv"); - std::ofstream out(filename); - // \todo: fix hardcoded sim_time - out << "sim_time, " << c->Label() << std::endl; - for (unsigned int j = 0; j < c->Size(); ++j) - { - ignition::math::Vector2d pt = c->Point(j); - out << pt.X() << ", " << pt.Y() << std::endl; - } - out.close(); + std::ofstream out(filename); + // \todo: fix hardcoded sim_time + out << "sim_time, " << c->Label() << std::endl; + for (unsigned int j = 0; j < c->Size(); ++j) { + ignition::math::Vector2d pt = c->Point(j); + out << pt.X() << ", " << pt.Y() << std::endl; + } + out.close(); - gzmsg << "Plot exported to file [" << filename << "]" << std::endl; - } - } + gzmsg << "Plot exported to file [" << filename << "]" << std::endl; + } + } } diff --git a/gazebo/gui/plot/PlotWindow.cc b/gazebo/gui/plot/PlotWindow.cc index b6c332c89e..e796efbbc4 100644 --- a/gazebo/gui/plot/PlotWindow.cc +++ b/gazebo/gui/plot/PlotWindow.cc @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ #include #include @@ -34,191 +34,191 @@ using namespace gui; using namespace tinyxml2; #ifndef XMLCheckResult - #define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { printf("Error: %i\n", a_eResult); return a_eResult; } +#define XMLCheckResult(a_eResult) if (a_eResult != XML_SUCCESS) { \ + printf("Error: %i\n", a_eResult); \ + return a_eResult; \ + } #endif -namespace gazebo -{ - namespace gui - { - /// \brief Private data for the PlotWindow class - class PlotWindowPrivate - { - /// \brief Splitter to hold all the canvases. - public: QSplitter *canvasSplitter; - - /// \brief Mutex to protect the canvas updates - public: std::mutex mutex; - - /// \brief Flag to indicate whether the plots should be restarted. - public: bool restart = false; - }; - } +namespace gazebo { +namespace gui { +/// \brief Private data for the PlotWindow class +class PlotWindowPrivate { + /// \brief Splitter to hold all the canvases. +public: + QSplitter *canvasSplitter; + + /// \brief Mutex to protect the canvas updates +public: + std::mutex mutex; + + /// \brief Flag to indicate whether the plots should be restarted. +public: + bool restart = false; +}; +} } // A special list widget that allows dragging of items from it to a // plot -class DragableListWidget : public QListWidget -{ - public: explicit DragableListWidget(QWidget *_parent) - : QListWidget(_parent) - { - } - - protected: virtual void startDrag(Qt::DropActions /*_supportedActions*/) - { - QListWidgetItem *currItem = this->currentItem(); - QMimeData *currMimeData = new QMimeData; - QByteArray ba; - ba = currItem->text().toLatin1().data(); - currMimeData->setData("application/x-item", ba); - QDrag *drag = new QDrag(this); - drag->setMimeData(currMimeData); - drag->exec(Qt::LinkAction); - } - - protected: virtual Qt::DropActions supportedDropActions() const - { - return Qt::LinkAction; - } +class DragableListWidget: public QListWidget { +public: + explicit DragableListWidget(QWidget *_parent) : + QListWidget(_parent) { + } + +protected: + virtual void startDrag(Qt::DropActions /*_supportedActions*/) { + QListWidgetItem *currItem = this->currentItem(); + QMimeData *currMimeData = new QMimeData; + QByteArray ba; + ba = currItem->text().toLatin1().data(); + currMimeData->setData("application/x-item", ba); + QDrag *drag = new QDrag(this); + drag->setMimeData(currMimeData); + drag->exec(Qt::LinkAction); + } + +protected: + virtual Qt::DropActions supportedDropActions() const { + return Qt::LinkAction; + } }; ///////////////////////////////////////////////// -PlotWindow::PlotWindow(QWidget *_parent) - : QWidget(_parent), - dataPtr(new PlotWindowPrivate()) -{ - this->setWindowIcon(QIcon(":/images/gazebo.svg")); - this->setWindowTitle("Gazebo: Plotting Utility"); - this->setObjectName("plotWindow"); - this->setWindowFlags( Qt::Window | - Qt::WindowTitleHint | - Qt::WindowCloseButtonHint | - Qt::WindowStaysOnTopHint | - Qt::CustomizeWindowHint); - - // new empty canvas - this->dataPtr->canvasSplitter = new QSplitter(Qt::Vertical); - this->AddCanvas(); - - // export button - QPushButton *exportPlotButton = new QPushButton("Export"); - exportPlotButton->setIcon(QIcon(":/images/file_upload.svg")); - exportPlotButton->setObjectName("plotExport"); - exportPlotButton->setDefault(false); - exportPlotButton->setAutoDefault(false); - exportPlotButton->setToolTip("Export plot data"); - QGraphicsDropShadowEffect *exportPlotShadow = new QGraphicsDropShadowEffect(); - exportPlotShadow->setBlurRadius(8); - exportPlotShadow->setOffset(0, 0); - exportPlotButton->setGraphicsEffect(exportPlotShadow); - connect(exportPlotButton, SIGNAL(clicked()), this, SLOT(OnExport())); - - // save button - QPushButton *saveCanvasButton = new QPushButton("Save"); - saveCanvasButton->setObjectName("plotSaveCanvas"); - saveCanvasButton->setDefault(false); - saveCanvasButton->setAutoDefault(false); - saveCanvasButton->setToolTip("Save canvas"); - QGraphicsDropShadowEffect *saveCanvasShadow = new QGraphicsDropShadowEffect(); - saveCanvasShadow->setBlurRadius(8); - saveCanvasShadow->setOffset(0, 0); - saveCanvasButton->setGraphicsEffect(saveCanvasShadow); - connect(saveCanvasButton, SIGNAL(clicked()), this, SLOT(OnSaveCanvas())); - - // load button - QPushButton *loadCanvasButton = new QPushButton("Load"); - loadCanvasButton->setObjectName("plotLoadCanvas"); - loadCanvasButton->setDefault(false); - loadCanvasButton->setAutoDefault(false); - loadCanvasButton->setToolTip("Load canvas"); - QGraphicsDropShadowEffect *loadCanvasShadow = new QGraphicsDropShadowEffect(); - loadCanvasShadow->setBlurRadius(8); - loadCanvasShadow->setOffset(0, 0); - loadCanvasButton->setGraphicsEffect(loadCanvasShadow); - connect(loadCanvasButton, SIGNAL(clicked()), this, SLOT(OnLoadCanvas())); - - // add button - QPushButton *addCanvasButton = new QPushButton("+"); - addCanvasButton->setObjectName("plotAddCanvas"); - addCanvasButton->setDefault(false); - addCanvasButton->setAutoDefault(false); - addCanvasButton->setToolTip("Add a new canvas"); - QGraphicsDropShadowEffect *addCanvasShadow = new QGraphicsDropShadowEffect(); - addCanvasShadow->setBlurRadius(8); - addCanvasShadow->setOffset(0, 0); - addCanvasButton->setGraphicsEffect(addCanvasShadow); - connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); - - QHBoxLayout *addButtonLayout = new QHBoxLayout; - addButtonLayout->addWidget(exportPlotButton); - addButtonLayout->addStretch(); +PlotWindow::PlotWindow(QWidget *_parent) : + QWidget(_parent), dataPtr(new PlotWindowPrivate()) { + this->setWindowIcon(QIcon(":/images/gazebo.svg")); + this->setWindowTitle("Gazebo: Plotting Utility"); + this->setObjectName("plotWindow"); + this->setWindowFlags( + Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint + | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); + + // new empty canvas + this->dataPtr->canvasSplitter = new QSplitter(Qt::Vertical); + this->AddCanvas(); + + // export button + QPushButton *exportPlotButton = new QPushButton("Export"); + exportPlotButton->setIcon(QIcon(":/images/file_upload.svg")); + exportPlotButton->setObjectName("plotExport"); + exportPlotButton->setDefault(false); + exportPlotButton->setAutoDefault(false); + exportPlotButton->setToolTip("Export plot data"); + QGraphicsDropShadowEffect *exportPlotShadow = + new QGraphicsDropShadowEffect(); + exportPlotShadow->setBlurRadius(8); + exportPlotShadow->setOffset(0, 0); + exportPlotButton->setGraphicsEffect(exportPlotShadow); + connect(exportPlotButton, SIGNAL(clicked()), this, SLOT(OnExport())); + + // save button + QPushButton *saveCanvasButton = new QPushButton("Save"); + saveCanvasButton->setObjectName("plotSaveCanvas"); + saveCanvasButton->setDefault(false); + saveCanvasButton->setAutoDefault(false); + saveCanvasButton->setToolTip("Save canvas"); + QGraphicsDropShadowEffect *saveCanvasShadow = + new QGraphicsDropShadowEffect(); + saveCanvasShadow->setBlurRadius(8); + saveCanvasShadow->setOffset(0, 0); + saveCanvasButton->setGraphicsEffect(saveCanvasShadow); + connect(saveCanvasButton, SIGNAL(clicked()), this, SLOT(OnSaveCanvas())); + + // load button + QPushButton *loadCanvasButton = new QPushButton("Load"); + loadCanvasButton->setObjectName("plotLoadCanvas"); + loadCanvasButton->setDefault(false); + loadCanvasButton->setAutoDefault(false); + loadCanvasButton->setToolTip("Load canvas"); + QGraphicsDropShadowEffect *loadCanvasShadow = + new QGraphicsDropShadowEffect(); + loadCanvasShadow->setBlurRadius(8); + loadCanvasShadow->setOffset(0, 0); + loadCanvasButton->setGraphicsEffect(loadCanvasShadow); + connect(loadCanvasButton, SIGNAL(clicked()), this, SLOT(OnLoadCanvas())); + + // add button + QPushButton *addCanvasButton = new QPushButton("+"); + addCanvasButton->setObjectName("plotAddCanvas"); + addCanvasButton->setDefault(false); + addCanvasButton->setAutoDefault(false); + addCanvasButton->setToolTip("Add a new canvas"); + QGraphicsDropShadowEffect *addCanvasShadow = + new QGraphicsDropShadowEffect(); + addCanvasShadow->setBlurRadius(8); + addCanvasShadow->setOffset(0, 0); + addCanvasButton->setGraphicsEffect(addCanvasShadow); + connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); + + QHBoxLayout *addButtonLayout = new QHBoxLayout; + addButtonLayout->addWidget(exportPlotButton); + addButtonLayout->addStretch(); addButtonLayout->addWidget(saveCanvasButton); addButtonLayout->addWidget(loadCanvasButton); - addButtonLayout->addWidget(addCanvasButton); - addButtonLayout->setAlignment(Qt::AlignRight | Qt::AlignBottom); - addButtonLayout->setContentsMargins(0, 0, 0, 0); - addCanvasButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + addButtonLayout->addWidget(addCanvasButton); + addButtonLayout->setAlignment(Qt::AlignRight | Qt::AlignBottom); + addButtonLayout->setContentsMargins(0, 0, 0, 0); + addCanvasButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - // Plot layout - QVBoxLayout *plotLayout = new QVBoxLayout; - plotLayout->addWidget(this->dataPtr->canvasSplitter); - plotLayout->addLayout(addButtonLayout); - plotLayout->setStretchFactor(this->dataPtr->canvasSplitter, 1); - plotLayout->setStretchFactor(addButtonLayout, 0); + // Plot layout + QVBoxLayout *plotLayout = new QVBoxLayout; + plotLayout->addWidget(this->dataPtr->canvasSplitter); + plotLayout->addLayout(addButtonLayout); + plotLayout->setStretchFactor(this->dataPtr->canvasSplitter, 1); + plotLayout->setStretchFactor(addButtonLayout, 0); - auto plotFrame = new QFrame; - plotFrame->setLayout(plotLayout); + auto plotFrame = new QFrame; + plotFrame->setLayout(plotLayout); - // Palette - auto plotPalette = new Palette(this); + // Palette + auto plotPalette = new Palette(this); - auto splitter = new QSplitter(Qt::Horizontal, this); - splitter->addWidget(plotPalette); - splitter->addWidget(plotFrame); - splitter->setCollapsible(0, true); - splitter->setCollapsible(1, false); + auto splitter = new QSplitter(Qt::Horizontal, this); + splitter->addWidget(plotPalette); + splitter->addWidget(plotFrame); + splitter->setCollapsible(0, true); + splitter->setCollapsible(1, false); - QList sizes; - sizes << 30 << 70; - splitter->setSizes(sizes); + QList sizes; + sizes << 30 << 70; + splitter->setSizes(sizes); - auto mainLayout = new QHBoxLayout; - mainLayout->addWidget(splitter); - mainLayout->setContentsMargins(0, 0, 0, 0); + auto mainLayout = new QHBoxLayout; + mainLayout->addWidget(splitter); + mainLayout->setContentsMargins(0, 0, 0, 0); - this->setLayout(mainLayout); + this->setLayout(mainLayout); - QShortcut *space = new QShortcut(Qt::Key_Space, this); - QObject::connect(space, SIGNAL(activated()), this, SLOT(TogglePause())); + QShortcut *space = new QShortcut(Qt::Key_Space, this); + QObject::connect(space, SIGNAL(activated()), this, SLOT(TogglePause())); - QTimer *displayTimer = new QTimer(this); - connect(displayTimer, SIGNAL(timeout()), this, SLOT(Update())); - displayTimer->start(30); + QTimer *displayTimer = new QTimer(this); + connect(displayTimer, SIGNAL(timeout()), this, SLOT(Update())); + displayTimer->start(30); - PlotManager::Instance()->AddWindow(this); + PlotManager::Instance()->AddWindow(this); - this->setMinimumSize(640, 480); + this->setMinimumSize(640, 480); } ///////////////////////////////////////////////// -PlotWindow::~PlotWindow() -{ - PlotManager::Instance()->RemoveWindow(this); - this->Clear(); +PlotWindow::~PlotWindow() { + PlotManager::Instance()->RemoveWindow(this); + this->Clear(); } ///////////////////////////////////////////////// -PlotCanvas *PlotWindow::AddCanvas() -{ - PlotCanvas *canvas = new PlotCanvas(this); - connect(canvas, SIGNAL(CanvasDeleted()), this, SLOT(OnRemoveCanvas())); +PlotCanvas* PlotWindow::AddCanvas() { + PlotCanvas *canvas = new PlotCanvas(this); + connect(canvas, SIGNAL(CanvasDeleted()), this, SLOT(OnRemoveCanvas())); - this->dataPtr->canvasSplitter->addWidget(canvas); + this->dataPtr->canvasSplitter->addWidget(canvas); - this->UpdateCanvas(); + this->UpdateCanvas(); - return canvas; + return canvas; } ///////////////////////////////////////////////// @@ -234,42 +234,47 @@ void PlotWindow::SavePlotLayout() { PlotCanvas *_canvas = nullptr; - _fileName = QFileDialog::getSaveFileName(this, tr("Save Plot Layout"), "~", tr("XML File (*.xml)")); + _fileName = QFileDialog::getSaveFileName(this, tr("Save Plot Layout"), "~", + tr("XML File (*.xml)")); - _rootNode = _plotLayout_XMLDocument.NewElement("PlotLayout"); - _plotLayout_XMLDocument.InsertFirstChild(_rootNode); + _rootNode = _plotLayout_XMLDocument.NewElement("PlotLayout"); + _plotLayout_XMLDocument.InsertFirstChild(_rootNode); - for (int _canvasIndex = 0; _canvasIndex < this->dataPtr->canvasSplitter->count(); ++_canvasIndex) { - _canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(_canvasIndex)); - if (!_canvas) { - continue; - } + for (int _canvasIndex = 0; + _canvasIndex < this->dataPtr->canvasSplitter->count(); + ++_canvasIndex) { + _canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(_canvasIndex)); + if (!_canvas) { + continue; + } - _canvas_XMLElement = _plotLayout_XMLDocument.NewElement("Canvas"); - _rootNode->InsertEndChild(_canvas_XMLElement); + _canvas_XMLElement = _plotLayout_XMLDocument.NewElement("Canvas"); + _rootNode->InsertEndChild(_canvas_XMLElement); - for (const auto &_plot : _canvas->Plots()) { - _plot_XMLElement = _plotLayout_XMLDocument.NewElement("Plot"); + for (const auto &_plot : _canvas->Plots()) { + _plot_XMLElement = _plotLayout_XMLDocument.NewElement("Plot"); _canvas_XMLElement->InsertEndChild(_plot_XMLElement); - for (const auto &_curve : _plot->Curves()) { - auto c = _curve.lock(); - if (!c) - continue; + for (const auto &_curve : _plot->Curves()) { + auto _tempCurve = _curve.lock(); + if (!_tempCurve) + continue; - std::string _label = c->Label(); + std::string _label = _tempCurve->Label(); - _variable_XMLElement = _plotLayout_XMLDocument.NewElement("Variable"); + _variable_XMLElement = _plotLayout_XMLDocument.NewElement( + "Variable"); - _variable_XMLElement->SetAttribute("Label", _label.c_str()); + _variable_XMLElement->SetAttribute("Label", _label.c_str()); - _plot_XMLElement->InsertEndChild(_variable_XMLElement); - } // for (const auto &_curve : _plot->Curves()) {} - } // for (const auto &_plot : _canvas->Plots()) { - } // for (int _canvasIndex = 0; _canvasIndex < this->dataPtr->canvasSplitter->count(); ++_canvasIndex) { + _plot_XMLElement->InsertEndChild(_variable_XMLElement); + } // for (const auto &_curve : _plot->Curves()) {} + } // for (const auto &_plot : _canvas->Plots()) { + } // for (int _canvasIndex = 0; ... - // todo: treat XMLError - _XMLError = _plotLayout_XMLDocument.SaveFile(_fileName.toLocal8Bit()); + // todo: treat XMLError + _XMLError = _plotLayout_XMLDocument.SaveFile(_fileName.toLocal8Bit()); } // void PlotWindow::SavePlotLayout() { ///////////////////////////////////////////////// @@ -287,11 +292,12 @@ void PlotWindow::LoadPlotLayout() { std::string _label; - std::vector _plotVector; + std::vector _plotVector; IncrementalPlot *_plot = nullptr; int _size = 0; - _fileName = QFileDialog::getOpenFileName(this, tr("Load Plot Layout"), "~", tr("XML File (*.xml)")); + _fileName = QFileDialog::getOpenFileName(this, tr("Load Plot Layout"), "~", + tr("XML File (*.xml)")); if (_fileName == "") { return; @@ -299,7 +305,7 @@ void PlotWindow::LoadPlotLayout() { this->Clear(); - _XMLError = _plotLayout_XMLDocument.LoadFile(_fileName.toLocal8Bit()); + _XMLError = _plotLayout_XMLDocument.LoadFile(_fileName.toLocal8Bit()); _rootNode = _plotLayout_XMLDocument.FirstChild(); if (_rootNode == nullptr) @@ -309,16 +315,18 @@ void PlotWindow::LoadPlotLayout() { while (_canvas_XMLElement != nullptr) { _canvas = this->AddCanvas(); - if(!_canvas) { + if (!_canvas) { return; } _plot_XMLElement = _canvas_XMLElement->FirstChildElement("Plot"); while (_plot_XMLElement != nullptr) { - _variable_XMLElement = _plot_XMLElement->FirstChildElement("Variable"); + _variable_XMLElement = _plot_XMLElement->FirstChildElement( + "Variable"); while (_variable_XMLElement != nullptr) { - const char *tempChars = _variable_XMLElement->Attribute("Label"); + const char *tempChars = _variable_XMLElement->Attribute( + "Label"); if (tempChars != nullptr) { std::string _label = tempChars; @@ -333,7 +341,8 @@ void PlotWindow::LoadPlotLayout() { } // } else { // if (_size == 0) { } // if (tempChars != nullptr) { - _variable_XMLElement = _variable_XMLElement->NextSiblingElement("Variable"); + _variable_XMLElement = _variable_XMLElement->NextSiblingElement( + "Variable"); } // while (_variable_XMLElement != nullptr) { _plot_XMLElement = _plot_XMLElement->NextSiblingElement("Plot"); @@ -344,190 +353,169 @@ void PlotWindow::LoadPlotLayout() { } // void PlotWindow::LoadPlotLayout() { ///////////////////////////////////////////////// -void PlotWindow::RemoveCanvas(PlotCanvas *_canvas) -{ - int idx = this->dataPtr->canvasSplitter->indexOf(_canvas); - if (idx < 0) - return; - - _canvas->hide(); - _canvas->setParent(nullptr); - _canvas->deleteLater(); +void PlotWindow::RemoveCanvas(PlotCanvas *_canvas) { + int idx = this->dataPtr->canvasSplitter->indexOf(_canvas); + if (idx < 0) + return; + + _canvas->hide(); + _canvas->setParent(nullptr); + _canvas->deleteLater(); } ///////////////////////////////////////////////// -void PlotWindow::Clear() -{ - while (this->CanvasCount() > 0u) - { - PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(0)); - this->RemoveCanvas(canvas); - } +void PlotWindow::Clear() { + while (this->CanvasCount() > 0u) { + PlotCanvas *canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(0)); + this->RemoveCanvas(canvas); + } } ///////////////////////////////////////////////// -unsigned int PlotWindow::CanvasCount() const -{ - return static_cast(this->dataPtr->canvasSplitter->count()); +unsigned int PlotWindow::CanvasCount() const { + return static_cast(this->dataPtr->canvasSplitter->count()); } ///////////////////////////////////////////////// -void PlotWindow::OnAddCanvas() -{ - this->AddCanvas(); +void PlotWindow::OnAddCanvas() { + this->AddCanvas(); } ///////////////////////////////////////////////// -void PlotWindow::OnSaveCanvas() -{ - this->SavePlotLayout(); +void PlotWindow::OnSaveCanvas() { + this->SavePlotLayout(); } ///////////////////////////////////////////////// -void PlotWindow::OnLoadCanvas() -{ - this->LoadPlotLayout(); +void PlotWindow::OnLoadCanvas() { + this->LoadPlotLayout(); } ///////////////////////////////////////////////// -void PlotWindow::OnRemoveCanvas() -{ - PlotCanvas *canvas = qobject_cast(QObject::sender()); - if (!canvas) - return; - - this->RemoveCanvas(canvas); - - // add an empty canvas if the plot window is now empty - if (this->dataPtr->canvasSplitter->count() == 0) - this->AddCanvas(); - else - { - this->UpdateCanvas(); - } +void PlotWindow::OnRemoveCanvas() { + PlotCanvas *canvas = qobject_cast(QObject::sender()); + if (!canvas) + return; + + this->RemoveCanvas(canvas); + + // add an empty canvas if the plot window is now empty + if (this->dataPtr->canvasSplitter->count() == 0) + this->AddCanvas(); + else { + this->UpdateCanvas(); + } } ///////////////////////////////////////////////// -void PlotWindow::UpdateCanvas() -{ - // disable Delete Canvas option in settings if there is only one - // canvas in the window - PlotCanvas *plotCanvas = qobject_cast(this->dataPtr->canvasSplitter->widget(0)); - if (plotCanvas) - { - plotCanvas->SetDeleteCanvasEnabled(this->dataPtr->canvasSplitter->count() != 1); - } +void PlotWindow::UpdateCanvas() { + // disable Delete Canvas option in settings if there is only one + // canvas in the window + PlotCanvas *plotCanvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(0)); + if (plotCanvas) { + plotCanvas->SetDeleteCanvasEnabled( + this->dataPtr->canvasSplitter->count() != 1); + } } ///////////////////////////////////////////////// -void PlotWindow::Update() -{ - std::lock_guard lock(this->dataPtr->mutex); - if (this->dataPtr->restart) - { - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) - { - PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - canvas->Restart(); - } - this->dataPtr->restart = false; - } - - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) - { - PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - canvas->Update(); - } +void PlotWindow::Update() { + std::lock_guard lock(this->dataPtr->mutex); + if (this->dataPtr->restart) { + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { + PlotCanvas *canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(i)); + if (!canvas) + continue; + canvas->Restart(); + } + this->dataPtr->restart = false; + } + + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { + PlotCanvas *canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(i)); + if (!canvas) + continue; + canvas->Update(); + } } ///////////////////////////////////////////////// -void PlotWindow::Restart() -{ - std::lock_guard lock(this->dataPtr->mutex); - this->dataPtr->restart = true; +void PlotWindow::Restart() { + std::lock_guard lock(this->dataPtr->mutex); + this->dataPtr->restart = true; } ///////////////////////////////////////////////// -void PlotWindow::TogglePause() -{ - MainWindow *mainWindow = gui::get_main_window(); - if (!mainWindow) - return; - - if (mainWindow->IsPaused()) - mainWindow->Play(); - else - mainWindow->Pause(); +void PlotWindow::TogglePause() { + MainWindow *mainWindow = gui::get_main_window(); + if (!mainWindow) + return; + + if (mainWindow->IsPaused()) + mainWindow->Play(); + else + mainWindow->Pause(); } ///////////////////////////////////////////////// -void PlotWindow::OnExport() -{ - // Get the plots that have data. - std::list plots; - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) - { - bool hasData = false; - PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); - - if (!canvas) - continue; - - for (const auto &plot : canvas->Plots()) - { - for (const auto &curve : plot->Curves()) - { - auto c = curve.lock(); - if (!c) - continue; - - hasData = hasData || c->Size() > 0; - } - } - - if (hasData) - plots.push_back(canvas); - } - - // Display an error message if no plots have data. - if (plots.empty()) - { - QMessageBox msgBox( - QMessageBox::Information, - QString("Unable to export"), - QString( - "No data to export.\nAdd variables with data to a graph first."), - QMessageBox::Close, - this, - Qt::Window | Qt::WindowTitleHint | - Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); - msgBox.exec(); - } - else - { - ExportDialog *dialog = new ExportDialog(this, plots); - dialog->setModal(true); - dialog->show(); - } +void PlotWindow::OnExport() { + // Get the plots that have data. + std::list plots; + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { + bool hasData = false; + PlotCanvas *canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(i)); + + if (!canvas) + continue; + + for (const auto &plot : canvas->Plots()) { + for (const auto &curve : plot->Curves()) { + auto c = curve.lock(); + if (!c) + continue; + + hasData = hasData || c->Size() > 0; + } + } + + if (hasData) + plots.push_back(canvas); + } + + // Display an error message if no plots have data. + if (plots.empty()) { + QMessageBox msgBox(QMessageBox::Information, + QString("Unable to export"), + QString( + "No data to export.\nAdd variables with data to a graph first."), + QMessageBox::Close, this, + Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint + | Qt::CustomizeWindowHint); + msgBox.exec(); + } else { + ExportDialog *dialog = new ExportDialog(this, plots); + dialog->setModal(true); + dialog->show(); + } } ///////////////////////////////////////////////// -std::list PlotWindow::Plots() -{ - std::list plots; +std::list PlotWindow::Plots() { + std::list plots; - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) - { - PlotCanvas *canvas = qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { + PlotCanvas *canvas = qobject_cast( + this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - plots.push_back(canvas); - } + if (!canvas) + continue; + plots.push_back(canvas); + } - return plots; + return plots; } diff --git a/gazebo/gui/plot/VariablePillContainer.cc b/gazebo/gui/plot/VariablePillContainer.cc index 4d17ccb683..09ecaa98c8 100644 --- a/gazebo/gui/plot/VariablePillContainer.cc +++ b/gazebo/gui/plot/VariablePillContainer.cc @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. * -*/ + */ #include #include @@ -26,536 +26,463 @@ using namespace gazebo; using namespace gui; -namespace gazebo -{ - namespace gui - { - /// \internal - /// \brief VariablePillContainer private data - class VariablePillContainerPrivate - { - /// \brief Text label - public: QLabel *label; - - /// \brief Layout that contains all the variable pills. - public: QLayout *variableLayout; - - /// \brief Variables inside this container - public: std::map variables; - - /// \brief Container size. - public: int maxSize = -1; - - /// \brief Pointer to variable pill that is currently selected. - public: VariablePill *selectedVariable = nullptr; - }; - } +namespace gazebo { +namespace gui { +/// \internal +/// \brief VariablePillContainer private data +class VariablePillContainerPrivate { + /// \brief Text label +public: + QLabel *label; + + /// \brief Layout that contains all the variable pills. +public: + QLayout *variableLayout; + + /// \brief Variables inside this container +public: + std::map variables; + + /// \brief Container size. +public: + int maxSize = -1; + + /// \brief Pointer to variable pill that is currently selected. +public: + VariablePill *selectedVariable = nullptr; +}; +} } ///////////////////////////////////////////////// -VariablePillContainer::VariablePillContainer(QWidget *_parent) - : QWidget(_parent), - dataPtr(new VariablePillContainerPrivate) -{ - // label - this->dataPtr->label = new QLabel; - QHBoxLayout *labelLayout = new QHBoxLayout; - labelLayout->addWidget(this->dataPtr->label); - QMargins labelMargins = labelLayout->contentsMargins(); - labelMargins.setLeft(labelMargins.left() + 10); - labelLayout->setContentsMargins(labelMargins); - - // variable pills - this->dataPtr->variableLayout = new QHBoxLayout; - this->dataPtr->variableLayout->setAlignment(Qt::AlignLeft); - - QHBoxLayout *frameLayout = new QHBoxLayout; - frameLayout->addLayout(labelLayout); - frameLayout->addLayout(this->dataPtr->variableLayout); - frameLayout->setAlignment(Qt::AlignLeft); - frameLayout->setContentsMargins(8, 4, 8, 4); - QFrame *mainFrame = new QFrame; - mainFrame->setObjectName("variableContainerFrame"); - mainFrame->setLayout(frameLayout); - mainFrame->setFrameShape(QFrame::NoFrame); - mainFrame->setContentsMargins(0, 0, 0, 0); - - QHBoxLayout *mainLayout = new QHBoxLayout; - mainLayout->addWidget(mainFrame); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(0); - - this->setLayout(mainLayout); - this->setAcceptDrops(true); - - /*QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); - shadow->setBlurRadius(1); - shadow->setOffset(2, 2); - this->setGraphicsEffect(shadow); - */ +VariablePillContainer::VariablePillContainer(QWidget *_parent) : + QWidget(_parent), dataPtr(new VariablePillContainerPrivate) { + // label + this->dataPtr->label = new QLabel; + QHBoxLayout *labelLayout = new QHBoxLayout; + labelLayout->addWidget(this->dataPtr->label); + QMargins labelMargins = labelLayout->contentsMargins(); + labelMargins.setLeft(labelMargins.left() + 10); + labelLayout->setContentsMargins(labelMargins); + + // variable pills + this->dataPtr->variableLayout = new QHBoxLayout; + this->dataPtr->variableLayout->setAlignment(Qt::AlignLeft); + + QHBoxLayout *frameLayout = new QHBoxLayout; + frameLayout->addLayout(labelLayout); + frameLayout->addLayout(this->dataPtr->variableLayout); + frameLayout->setAlignment(Qt::AlignLeft); + frameLayout->setContentsMargins(8, 4, 8, 4); + QFrame *mainFrame = new QFrame; + mainFrame->setObjectName("variableContainerFrame"); + mainFrame->setLayout(frameLayout); + mainFrame->setFrameShape(QFrame::NoFrame); + mainFrame->setContentsMargins(0, 0, 0, 0); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(mainFrame); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + this->setLayout(mainLayout); + this->setAcceptDrops(true); + + /*QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); + shadow->setBlurRadius(1); + shadow->setOffset(2, 2); + this->setGraphicsEffect(shadow); + */ } ///////////////////////////////////////////////// -VariablePillContainer::~VariablePillContainer() -{ +VariablePillContainer::~VariablePillContainer() { } ///////////////////////////////////////////////// -void VariablePillContainer::SetText(const std::string &_text) -{ - this->dataPtr->label->setText(QString::fromStdString(_text)); +void VariablePillContainer::SetText(const std::string &_text) { + this->dataPtr->label->setText(QString::fromStdString(_text)); } ///////////////////////////////////////////////// -std::string VariablePillContainer::Text() const -{ - return this->dataPtr->label->text().toStdString(); +std::string VariablePillContainer::Text() const { + return this->dataPtr->label->text().toStdString(); } ///////////////////////////////////////////////// void VariablePillContainer::SetVariablePillLabel(const unsigned int _id, - const std::string &_label) -{ - VariablePill *variable = nullptr; - auto it = this->dataPtr->variables.find(_id); - if (it == this->dataPtr->variables.end()) - { - // look into children of multi-variable pills - for (auto v : this->dataPtr->variables) - { - auto &childVariables = v.second->VariablePills(); - auto childIt = childVariables.find(_id); - if (childIt != childVariables.end()) - variable = childIt->second; - } - } - else - variable = it->second; - - if (variable) - variable->SetText(_label); + const std::string &_label) { + VariablePill *variable = nullptr; + auto it = this->dataPtr->variables.find(_id); + if (it == this->dataPtr->variables.end()) { + // look into children of multi-variable pills + for (auto v : this->dataPtr->variables) { + auto &childVariables = v.second->VariablePills(); + auto childIt = childVariables.find(_id); + if (childIt != childVariables.end()) + variable = childIt->second; + } + } else + variable = it->second; + + if (variable) + variable->SetText(_label); } ///////////////////////////////////////////////// unsigned int VariablePillContainer::AddVariablePill(const std::string &_name, - const unsigned int _targetId) -{ - if (this->dataPtr->maxSize != -1 && - static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) - { - return VariablePill::EmptyVariable; - } - - if (_targetId != VariablePill::EmptyVariable && - !this->GetVariablePill(_targetId)) - { - gzerr << "Unable to add variable. Target variable not found" << std::endl; - return VariablePill::EmptyVariable; - } - - VariablePill *variable = new VariablePill; - variable->SetName(_name); - variable->SetText(_name); - - connect(variable, SIGNAL(VariableMoved(unsigned int)), - this, SLOT(OnMoveVariable(unsigned int))); - connect(variable, SIGNAL(VariableAdded(unsigned int, std::string)), - this, SLOT(OnAddVariable(unsigned int, std::string))); - connect(variable, SIGNAL(VariableRemoved(unsigned int)), - this, SLOT(OnRemoveVariable(unsigned int))); - connect(variable, SIGNAL(VariableLabelChanged(std::string)), - this, SLOT(OnSetVariableLabel(std::string))); - - this->AddVariablePill(variable, _targetId); - - return variable->Id(); + const unsigned int _targetId) { + if (this->dataPtr->maxSize != -1 + && static_cast(this->VariablePillCount()) + >= this->dataPtr->maxSize) { + return VariablePill::EmptyVariable; + } + + if (_targetId != VariablePill::EmptyVariable + && !this->GetVariablePill(_targetId)) { + gzerr << "Unable to add variable. Target variable not found" + << std::endl; + return VariablePill::EmptyVariable; + } + + VariablePill *variable = new VariablePill; + variable->SetName(_name); + variable->SetText(_name); + + connect(variable, SIGNAL(VariableMoved(unsigned int)), this, + SLOT(OnMoveVariable(unsigned int))); + connect(variable, SIGNAL(VariableAdded(unsigned int, std::string)), this, + SLOT(OnAddVariable(unsigned int, std::string))); + connect(variable, SIGNAL(VariableRemoved(unsigned int)), this, + SLOT(OnRemoveVariable(unsigned int))); + connect(variable, SIGNAL(VariableLabelChanged(std::string)), this, + SLOT(OnSetVariableLabel(std::string))); + + this->AddVariablePill(variable, _targetId); + + return variable->Id(); } ///////////////////////////////////////////////// void VariablePillContainer::AddVariablePill(VariablePill *_variable, - const unsigned int _targetId) -{ - if (!_variable) - return; - - // add to target variable if it's not empty - if (_targetId != VariablePill::EmptyVariable) - { - VariablePill *targetVariable = this->GetVariablePill(_targetId); - if (!targetVariable) - return; - - targetVariable->AddVariablePill(_variable); - return; - } - else - { - // check if variable already exists in this container - // check only top level variables - if (this->dataPtr->variables.find(_variable->Id()) != - this->dataPtr->variables.end()) - { - return; - } - } - - // otherwise add to the container - if (this->dataPtr->maxSize != -1 && - static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) - { - gzerr << "Unable to add variable to container. Container is full" << - std::endl; - return; - } - - _variable->SetContainer(this); - _variable->setVisible(true); - this->dataPtr->variableLayout->addWidget(_variable); - this->dataPtr->variables[_variable->Id()] = _variable; - - emit VariableAdded(_variable->Id(), _variable->Text(), _targetId); + const unsigned int _targetId) { + if (!_variable) + return; + + // add to target variable if it's not empty + if (_targetId != VariablePill::EmptyVariable) { + VariablePill *targetVariable = this->GetVariablePill(_targetId); + if (!targetVariable) + return; + + targetVariable->AddVariablePill(_variable); + return; + } else { + // check if variable already exists in this container + // check only top level variables + if (this->dataPtr->variables.find(_variable->Id()) + != this->dataPtr->variables.end()) { + return; + } + } + + // otherwise add to the container + if (this->dataPtr->maxSize != -1 + && static_cast(this->VariablePillCount()) + >= this->dataPtr->maxSize) { + gzerr << "Unable to add variable to container. Container is full" + << std::endl; + return; + } + + _variable->SetContainer(this); + _variable->setVisible(true); + this->dataPtr->variableLayout->addWidget(_variable); + this->dataPtr->variables[_variable->Id()] = _variable; + + emit VariableAdded(_variable->Id(), _variable->Text(), _targetId); } ///////////////////////////////////////////////// -void VariablePillContainer::SetMaxSize(const int _max) -{ - this->dataPtr->maxSize = _max; +void VariablePillContainer::SetMaxSize(const int _max) { + this->dataPtr->maxSize = _max; } ///////////////////////////////////////////////// -int VariablePillContainer::MaxSize() const -{ - return this->dataPtr->maxSize; +int VariablePillContainer::MaxSize() const { + return this->dataPtr->maxSize; } ///////////////////////////////////////////////// -void VariablePillContainer::RemoveVariablePill(const unsigned int _id) -{ - VariablePill *variable = nullptr; - auto it = this->dataPtr->variables.find(_id); - if (it == this->dataPtr->variables.end()) - { - // look into children of multi-variable pills - for (auto v : this->dataPtr->variables) - { - auto &childVariables = v.second->VariablePills(); - auto childIt = childVariables.find(_id); - if (childIt != childVariables.end()) - { - variable = childIt->second; - // remove from parent - if (variable->Parent()) - variable->Parent()->RemoveVariablePill(variable); - return; - } - } - } - else - variable = it->second; - - if (!variable) - return; - - int idx = this->dataPtr->variableLayout->indexOf(variable); - if (idx != -1) - { - this->dataPtr->variableLayout->takeAt(idx); - this->dataPtr->variables.erase(variable->Id()); - // remove from parent if any - if (variable->Parent()) - { - // remove and rely on callbacks to emit the VariableRemoved signal - variable->Parent()->RemoveVariablePill(variable); - } - else - emit VariableRemoved(variable->Id(), VariablePill::EmptyVariable); - } - - // otherwise remove from container - variable->SetContainer(nullptr); - variable->setVisible(false); +void VariablePillContainer::RemoveVariablePill(const unsigned int _id) { + VariablePill *variable = nullptr; + auto it = this->dataPtr->variables.find(_id); + if (it == this->dataPtr->variables.end()) { + // look into children of multi-variable pills + for (auto v : this->dataPtr->variables) { + auto &childVariables = v.second->VariablePills(); + auto childIt = childVariables.find(_id); + if (childIt != childVariables.end()) { + variable = childIt->second; + // remove from parent + if (variable->Parent()) + variable->Parent()->RemoveVariablePill(variable); + return; + } + } + } else + variable = it->second; + + if (!variable) + return; + + int idx = this->dataPtr->variableLayout->indexOf(variable); + if (idx != -1) { + this->dataPtr->variableLayout->takeAt(idx); + this->dataPtr->variables.erase(variable->Id()); + // remove from parent if any + if (variable->Parent()) { + // remove and rely on callbacks to emit the VariableRemoved signal + variable->Parent()->RemoveVariablePill(variable); + } else + emit VariableRemoved(variable->Id(), VariablePill::EmptyVariable); + } + + // otherwise remove from container + variable->SetContainer(nullptr); + variable->setVisible(false); } ///////////////////////////////////////////////// -void VariablePillContainer::RemoveVariablePill(VariablePill *_variable) -{ - if (!_variable) - return; +void VariablePillContainer::RemoveVariablePill(VariablePill *_variable) { + if (!_variable) + return; - this->RemoveVariablePill(_variable->Id()); + this->RemoveVariablePill(_variable->Id()); } ///////////////////////////////////////////////// -unsigned int VariablePillContainer::VariablePillCount() const -{ - unsigned int count = 0; - for (const auto v : this->dataPtr->variables) - { - count++; - count += v.second->VariablePillCount(); - } - return count; +unsigned int VariablePillContainer::VariablePillCount() const { + unsigned int count = 0; + for (const auto v : this->dataPtr->variables) { + count++; + count += v.second->VariablePillCount(); + } + return count; } ///////////////////////////////////////////////// -VariablePill *VariablePillContainer::GetVariablePill( - const unsigned int _id) const -{ - for (const auto &v : this->dataPtr->variables) - { - if (v.first == _id) - return v.second; - - for (const auto &child : v.second->VariablePills()) - { - if (child.first == _id) - return child.second; - } - } - return nullptr; +VariablePill* VariablePillContainer::GetVariablePill( + const unsigned int _id) const { + for (const auto &v : this->dataPtr->variables) { + if (v.first == _id) + return v.second; + + for (const auto &child : v.second->VariablePills()) { + if (child.first == _id) + return child.second; + } + } + return nullptr; } ///////////////////////////////////////////////// -void VariablePillContainer::SetSelected(VariablePill *_variable) -{ - if (this->dataPtr->selectedVariable) - this->dataPtr->selectedVariable->SetSelected(false); +void VariablePillContainer::SetSelected(VariablePill *_variable) { + if (this->dataPtr->selectedVariable) + this->dataPtr->selectedVariable->SetSelected(false); - this->dataPtr->selectedVariable = _variable; + this->dataPtr->selectedVariable = _variable; - if (this->dataPtr->selectedVariable) - this->dataPtr->selectedVariable->SetSelected(true); + if (this->dataPtr->selectedVariable) + this->dataPtr->selectedVariable->SetSelected(true); } ///////////////////////////////////////////////// -void VariablePillContainer::dragEnterEvent(QDragEnterEvent *_evt) -{ - if (!this->IsDragValid(_evt)) - { - _evt->ignore(); - return; - } - - if (_evt->mimeData()->hasFormat("application/x-item")) - { - _evt->setDropAction(Qt::LinkAction); - } - else if (_evt->mimeData()->hasFormat("application/x-pill-item")) - { - _evt->setDropAction(Qt::MoveAction); - } - else - { - _evt->ignore(); - return; - } - - _evt->acceptProposedAction(); +void VariablePillContainer::dragEnterEvent(QDragEnterEvent *_evt) { + if (!this->IsDragValid(_evt)) { + _evt->ignore(); + return; + } + + if (_evt->mimeData()->hasFormat("application/x-item")) { + _evt->setDropAction(Qt::LinkAction); + } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { + _evt->setDropAction(Qt::MoveAction); + } else { + _evt->ignore(); + return; + } + + _evt->acceptProposedAction(); } ///////////////////////////////////////////////// -void VariablePillContainer::dropEvent(QDropEvent *_evt) -{ - if (!this->IsDragValid(_evt)) - { - _evt->ignore(); - return; - } - - if (_evt->mimeData()->hasFormat("application/x-item")) - { - QString mimeData = _evt->mimeData()->data("application/x-item"); - std::string dataStr = mimeData.toStdString(); - this->AddVariablePill(dataStr); - } - else if (_evt->mimeData()->hasFormat("application/x-pill-item")) - { - VariablePill *variable = qobject_cast(_evt->source()); - if (!variable) - { - gzerr << "Variable is nullptr" << std::endl; - return; - } - - VariablePillContainer *container = variable->Container(); - - // moved to the same container - no op - if (!variable->Parent() && (container && container == this)) - return; - - // block signals and emit VariableMoved instead. - VariablePill *parentVariable = variable->Parent(); - if (parentVariable) - { - parentVariable->blockSignals(true); - parentVariable->RemoveVariablePill(variable); - parentVariable->blockSignals(false); - } - else - { - if (container) - { - container->blockSignals(true); - container->RemoveVariablePill(variable); - container->blockSignals(false); - } - } - - // case when the variable is dragged out from a muli-variable pill to - // the container - this->blockSignals(true); - this->AddVariablePill(variable); - this->blockSignals(false); - - emit VariableMoved(variable->Id(), VariablePill::EmptyVariable); - } +void VariablePillContainer::dropEvent(QDropEvent *_evt) { + if (!this->IsDragValid(_evt)) { + _evt->ignore(); + return; + } + + if (_evt->mimeData()->hasFormat("application/x-item")) { + QString mimeData = _evt->mimeData()->data("application/x-item"); + std::string dataStr = mimeData.toStdString(); + this->AddVariablePill(dataStr); + } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { + VariablePill *variable = qobject_cast(_evt->source()); + if (!variable) { + gzerr << "Variable is nullptr" << std::endl; + return; + } + + VariablePillContainer *container = variable->Container(); + + // moved to the same container - no op + if (!variable->Parent() && (container && container == this)) + return; + + // block signals and emit VariableMoved instead. + VariablePill *parentVariable = variable->Parent(); + if (parentVariable) { + parentVariable->blockSignals(true); + parentVariable->RemoveVariablePill(variable); + parentVariable->blockSignals(false); + } else { + if (container) { + container->blockSignals(true); + container->RemoveVariablePill(variable); + container->blockSignals(false); + } + } + + // case when the variable is dragged out from a muli-variable pill to + // the container + this->blockSignals(true); + this->AddVariablePill(variable); + this->blockSignals(false); + + emit VariableMoved(variable->Id(), VariablePill::EmptyVariable); + } } ///////////////////////////////////////////////// -bool VariablePillContainer::IsDragValid(QDropEvent *_evt) const -{ - if (this->dataPtr->maxSize != -1 && - static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) - { - return false; - } - - std::string dataStr; - if (_evt->mimeData()->hasFormat("application/x-item")) - { - QString mimeData = _evt->mimeData()->data("application/x-item"); - dataStr = mimeData.toStdString(); - } - else if (_evt->mimeData()->hasFormat("application/x-pill-item")) - { - QString mimeData = _evt->mimeData()->data("application/x-pill-item"); - dataStr = mimeData.toStdString(); - - VariablePill *dragVariable = qobject_cast(_evt->source()); - if (!dragVariable) - return false; - - // limit drag and drop to same container - if (dragVariable->Container() && dragVariable->Container() != this) - return false; - } - else - return false; - - if (dataStr.empty()) - return false; - - return true; +bool VariablePillContainer::IsDragValid(QDropEvent *_evt) const { + if (this->dataPtr->maxSize != -1 + && static_cast(this->VariablePillCount()) + >= this->dataPtr->maxSize) { + return false; + } + + std::string dataStr; + if (_evt->mimeData()->hasFormat("application/x-item")) { + QString mimeData = _evt->mimeData()->data("application/x-item"); + dataStr = mimeData.toStdString(); + } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { + QString mimeData = _evt->mimeData()->data("application/x-pill-item"); + dataStr = mimeData.toStdString(); + + VariablePill *dragVariable = qobject_cast( + _evt->source()); + if (!dragVariable) + return false; + + // limit drag and drop to same container + if (dragVariable->Container() && dragVariable->Container() != this) + return false; + } else + return false; + + if (dataStr.empty()) + return false; + + return true; } ///////////////////////////////////////////////// -void VariablePillContainer::keyPressEvent(QKeyEvent *_event) -{ - if (_event->key() == Qt::Key_Delete) - { - if (this->dataPtr->selectedVariable) - { - this->RemoveVariablePill(this->dataPtr->selectedVariable); - this->dataPtr->selectedVariable = nullptr; - } - } +void VariablePillContainer::keyPressEvent(QKeyEvent *_event) { + if (_event->key() == Qt::Key_Delete) { + if (this->dataPtr->selectedVariable) { + this->RemoveVariablePill(this->dataPtr->selectedVariable); + this->dataPtr->selectedVariable = nullptr; + } + } } ///////////////////////////////////////////////// -void VariablePillContainer::mouseReleaseEvent(QMouseEvent *_event) -{ - this->SetSelected(nullptr); - - bool selected = false; - for (const auto v : this->dataPtr->variables) - { - // look for the selected variable widget if not already found - QPoint point = v.second->mapFromParent(_event->pos()); - if (!selected) - { - ignition::math::Vector2i pt(point.x(), point.y()); - if (v.second->ContainsPoint(pt)) - { - this->SetSelected(v.second); - this->setFocus(); - selected = true; - } - else - { - v.second->SetSelected(false); - } - } - else - { - v.second->SetSelected(false); - } - - - // loop through children of multi-variable pills - for (const auto cv : v.second->VariablePills()) - { - VariablePill *child = cv.second; - - if (!selected) - { - QPoint childPoint = child->mapFromParent(point); - ignition::math::Vector2i childPt(childPoint.x(), childPoint.y()); - if (child->ContainsPoint(childPt)) - { - this->SetSelected(child); - this->setFocus(); - selected = true; - } - else - { - child->SetSelected(false); - } - } - else - { - child->SetSelected(false); - } - } - } +void VariablePillContainer::mouseReleaseEvent(QMouseEvent *_event) { + this->SetSelected(nullptr); + + bool selected = false; + for (const auto v : this->dataPtr->variables) { + // look for the selected variable widget if not already found + QPoint point = v.second->mapFromParent(_event->pos()); + if (!selected) { + ignition::math::Vector2i pt(point.x(), point.y()); + if (v.second->ContainsPoint(pt)) { + this->SetSelected(v.second); + this->setFocus(); + selected = true; + } else { + v.second->SetSelected(false); + } + } else { + v.second->SetSelected(false); + } + + // loop through children of multi-variable pills + for (const auto cv : v.second->VariablePills()) { + VariablePill *child = cv.second; + + if (!selected) { + QPoint childPoint = child->mapFromParent(point); + ignition::math::Vector2i childPt(childPoint.x(), + childPoint.y()); + if (child->ContainsPoint(childPt)) { + this->SetSelected(child); + this->setFocus(); + selected = true; + } else { + child->SetSelected(false); + } + } else { + child->SetSelected(false); + } + } + } } ///////////////////////////////////////////////// -void VariablePillContainer::OnMoveVariable(const unsigned int _id) -{ - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnMoveVariable(const unsigned int _id) { + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableMoved(_id, variable->Id()); + emit VariableMoved(_id, variable->Id()); } ///////////////////////////////////////////////// void VariablePillContainer::OnAddVariable(const unsigned int _id, - const std::string &_label) -{ - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; + const std::string &_label) { + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableAdded(_id, _label, variable->Id()); + emit VariableAdded(_id, _label, variable->Id()); } ///////////////////////////////////////////////// -void VariablePillContainer::OnRemoveVariable(const unsigned int _id) -{ - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnRemoveVariable(const unsigned int _id) { + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableRemoved(_id, variable->Id()); + emit VariableRemoved(_id, variable->Id()); } ///////////////////////////////////////////////// -void VariablePillContainer::OnSetVariableLabel(const std::string &_label) -{ - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnSetVariableLabel(const std::string &_label) { + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableLabelChanged(variable->Id(), _label); + emit VariableLabelChanged(variable->Id(), _label); } From 170bdcd7565b511f8efa0a5b983e37d97df4b43b Mon Sep 17 00:00:00 2001 From: Steve Peters Date: Wed, 10 Nov 2021 22:26:14 -0800 Subject: [PATCH 3/3] Revert whitespace changes Signed-off-by: Steve Peters --- gazebo/gui/plot/PlotCanvas.cc | 1617 ++++++++++++---------- gazebo/gui/plot/PlotCanvas.hh | 13 +- gazebo/gui/plot/PlotWindow.cc | 609 ++++---- gazebo/gui/plot/VariablePillContainer.cc | 831 ++++++----- 4 files changed, 1637 insertions(+), 1433 deletions(-) diff --git a/gazebo/gui/plot/PlotCanvas.cc b/gazebo/gui/plot/PlotCanvas.cc index 71ac030a3f..f673cad2bb 100644 --- a/gazebo/gui/plot/PlotCanvas.cc +++ b/gazebo/gui/plot/PlotCanvas.cc @@ -36,55 +36,49 @@ using namespace gazebo; using namespace gui; -namespace gazebo { -namespace gui { -/// \brief Helper data structure to store plot data -class PlotData { - /// \brief Unique id of the plot -public: - unsigned int id; +namespace gazebo +{ + namespace gui + { + /// \brief Helper data structure to store plot data + class PlotData + { + /// \brief Unique id of the plot + public: unsigned int id; - /// brief Pointer to the plot -public: - IncrementalPlot *plot = nullptr; + /// brief Pointer to the plot + public: IncrementalPlot *plot = nullptr; - /// \brief A map of container variable ids to their plot curve ids. -public: - std::map variableCurves; -}; + /// \brief A map of container variable ids to their plot curve ids. + public: std::map variableCurves; + }; -/// \internal -/// \brief PlotCanvas private data -class PlotCanvasPrivate { - /// \brief Text label -public: - EditableLabel *title; + /// \internal + /// \brief PlotCanvas private data + class PlotCanvasPrivate + { + /// \brief Text label + public: EditableLabel *title; - /// \brief Splitter that contains all the plots. -public: - QSplitter *plotSplitter; + /// \brief Splitter that contains all the plots. + public: QSplitter *plotSplitter; - /// \brief A map of plot id to plot data; -public: - std::map plotData; + /// \brief A map of plot id to plot data; + public: std::map plotData; - /// \brief Pointer to an empty plot. -public: - IncrementalPlot *emptyPlot = nullptr; + /// \brief Pointer to an empty plot. + public: IncrementalPlot *emptyPlot = nullptr; - /// \brief Container for all the variableCurves on the Y axis. -public: - VariablePillContainer *yVariableContainer = nullptr; + /// \brief Container for all the variableCurves on the Y axis. + public: VariablePillContainer *yVariableContainer = nullptr; - /// \brief Delete canvas Qt action -public: - QAction *deleteCanvasAct = nullptr; + /// \brief Delete canvas Qt action + public: QAction *deleteCanvasAct = nullptr; - /// \brief Global plot counter. -public: - static unsigned int globalPlotId; -}; -} + /// \brief Global plot counter. + public: static unsigned int globalPlotId; + }; + } } // empty plot id @@ -94,829 +88,930 @@ const unsigned int PlotCanvas::EmptyPlot = ignition::math::MAX_UI32; unsigned int PlotCanvasPrivate::globalPlotId = 0; ///////////////////////////////////////////////// -PlotCanvas::PlotCanvas(QWidget *_parent) : - QWidget(_parent), dataPtr(new PlotCanvasPrivate()) { - this->setObjectName("plotCanvas"); - - // Plot title - this->dataPtr->title = new EditableLabel("Plot Name"); - - QHBoxLayout *titleLayout = new QHBoxLayout; - titleLayout->addWidget(this->dataPtr->title); - titleLayout->setAlignment(Qt::AlignHCenter); - - // Settings - QMenu *settingsMenu = new QMenu; - settingsMenu->setObjectName("material"); - QAction *clearPlotAct = new QAction("Clear all fields", settingsMenu); - clearPlotAct->setStatusTip(tr("Clear variables and all plots on canvas")); - connect(clearPlotAct, SIGNAL(triggered()), this, SLOT(OnClearCanvas())); - - this->dataPtr->deleteCanvasAct = new QAction("Delete canvas", settingsMenu); - this->dataPtr->deleteCanvasAct->setStatusTip(tr("Delete entire canvas")); - connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, - SLOT(OnDeleteCanvas())); - - QAction *showGridAct = new QAction("Show grid", settingsMenu); - showGridAct->setStatusTip(tr("Show/hide grid lines on plot")); - showGridAct->setCheckable(true); - - QAction *showHoverLineAct = new QAction("Show hover line", settingsMenu); - showHoverLineAct->setStatusTip(tr("Show hover line")); - showHoverLineAct->setCheckable(true); - connect(showHoverLineAct, SIGNAL(triggered()), this, - SLOT(OnShowHoverLine())); - - settingsMenu->addAction(clearPlotAct); - settingsMenu->addAction(this->dataPtr->deleteCanvasAct); - settingsMenu->addAction(showGridAct); - settingsMenu->addAction(showHoverLineAct); - - QToolButton *settingsButton = new QToolButton(); - settingsButton->setObjectName("plotCanvasTitleTool"); - settingsButton->installEventFilter(this); - settingsButton->setToolTip(tr("Settings")); - settingsButton->setIcon(QIcon(":/images/settings.svg")); - settingsButton->setIconSize(QSize(25, 25)); - settingsButton->setFixedSize(QSize(45, 35)); - settingsButton->setToolButtonStyle(Qt::ToolButtonIconOnly); - settingsButton->setPopupMode(QToolButton::InstantPopup); - settingsButton->setMenu(settingsMenu); - - QHBoxLayout *settingsLayout = new QHBoxLayout; - settingsLayout->addWidget(settingsButton); - - QHBoxLayout *titleSettingsLayout = new QHBoxLayout; - titleSettingsLayout->addLayout(titleLayout); - titleSettingsLayout->addLayout(settingsLayout); - titleSettingsLayout->setContentsMargins(0, 0, 0, 0); - - QFrame *titleFrame = new QFrame; - titleFrame->setObjectName("plotCanvasTitleFrame"); - titleFrame->setLayout(titleSettingsLayout); - - // X and Y variable containers - VariablePillContainer *xVariableContainer = new VariablePillContainer(this); - xVariableContainer->SetText("x "); - xVariableContainer->SetMaxSize(1); - xVariableContainer->setSizePolicy(QSizePolicy::Expanding, - QSizePolicy::Fixed); - xVariableContainer->setContentsMargins(0, 0, 0, 0); - // \todo: fix hardcoded x axis - xVariableContainer->AddVariablePill("sim_time"); - xVariableContainer->setEnabled(false); - - this->dataPtr->yVariableContainer = new VariablePillContainer(this); - this->dataPtr->yVariableContainer->SetText("y "); - this->dataPtr->yVariableContainer->setSizePolicy(QSizePolicy::Expanding, - QSizePolicy::Fixed); - this->dataPtr->yVariableContainer->setContentsMargins(0, 0, 0, 0); - - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), - this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableRemoved(unsigned int, unsigned int)), - this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableMoved(unsigned int, unsigned int)), - this, SLOT(OnMoveVariable(unsigned int, unsigned int))); - connect(this->dataPtr->yVariableContainer, - SIGNAL(VariableLabelChanged(unsigned int, std::string)), - this, SLOT(OnSetVariableLabel(unsigned int, std::string))); - - QVBoxLayout *variableContainerLayout = new QVBoxLayout; - variableContainerLayout->addWidget(xVariableContainer); - variableContainerLayout->addWidget(this->dataPtr->yVariableContainer); - variableContainerLayout->setSpacing(0); - variableContainerLayout->setContentsMargins(0, 0, 0, 0); - - // plot - QScrollArea *plotScrollArea = new QScrollArea(this); - plotScrollArea->setObjectName("plotScrollArea"); - plotScrollArea->setLineWidth(0); - plotScrollArea->setFrameShape(QFrame::NoFrame); - plotScrollArea->setFrameShadow(QFrame::Plain); - plotScrollArea->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); - - plotScrollArea->setWidgetResizable(true); - plotScrollArea->viewport()->installEventFilter(this); - - QFrame *plotFrame = new QFrame(plotScrollArea); - plotFrame->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); - plotFrame->setObjectName("plotCanvasPlotFrame"); - QVBoxLayout *plotLayout = new QVBoxLayout; - plotFrame->setLayout(plotLayout); - - this->dataPtr->plotSplitter = new QSplitter(Qt::Vertical); - this->dataPtr->plotSplitter->setVisible(false); - plotLayout->addWidget(this->dataPtr->plotSplitter); - - plotScrollArea->setWidget(plotFrame); - - // empty plot - this->dataPtr->emptyPlot = new IncrementalPlot(this); - connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), this, - SLOT(OnAddVariable(std::string))); - plotLayout->addWidget(this->dataPtr->emptyPlot); - - // set initial show grid state - showGridAct->setChecked(this->dataPtr->emptyPlot->IsShowGrid()); - connect(showGridAct, SIGNAL(triggered()), this, SLOT(OnShowGrid())); - - QFrame *mainFrame = new QFrame; - mainFrame->setObjectName("plotCanvasFrame"); - QVBoxLayout *mainFrameLayout = new QVBoxLayout; - mainFrameLayout->addWidget(titleFrame); - mainFrameLayout->addLayout(variableContainerLayout); - mainFrameLayout->addWidget(plotScrollArea); - mainFrameLayout->setContentsMargins(0, 0, 0, 0); - mainFrame->setLayout(mainFrameLayout); - - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(mainFrame); - mainLayout->setContentsMargins(0, 0, 0, 0); - this->setLayout(mainLayout); -} - -///////////////////////////////////////////////// -PlotCanvas::~PlotCanvas() { - this->Clear(); +PlotCanvas::PlotCanvas(QWidget *_parent) + : QWidget(_parent), + dataPtr(new PlotCanvasPrivate()) +{ + this->setObjectName("plotCanvas"); + + // Plot title + this->dataPtr->title = new EditableLabel("Plot Name"); + + QHBoxLayout *titleLayout = new QHBoxLayout; + titleLayout->addWidget(this->dataPtr->title); + titleLayout->setAlignment(Qt::AlignHCenter); + + // Settings + QMenu *settingsMenu = new QMenu; + settingsMenu->setObjectName("material"); + QAction *clearPlotAct = new QAction("Clear all fields", settingsMenu); + clearPlotAct->setStatusTip(tr("Clear variables and all plots on canvas")); + connect(clearPlotAct, SIGNAL(triggered()), this, SLOT(OnClearCanvas())); + + this->dataPtr->deleteCanvasAct = new QAction("Delete canvas", settingsMenu); + this->dataPtr->deleteCanvasAct->setStatusTip(tr("Delete entire canvas")); + connect(this->dataPtr->deleteCanvasAct, SIGNAL(triggered()), this, + SLOT(OnDeleteCanvas())); + + QAction *showGridAct = new QAction("Show grid", settingsMenu); + showGridAct->setStatusTip(tr("Show/hide grid lines on plot")); + showGridAct->setCheckable(true); + + QAction *showHoverLineAct = new QAction("Show hover line", settingsMenu); + showHoverLineAct->setStatusTip(tr("Show hover line")); + showHoverLineAct->setCheckable(true); + connect(showHoverLineAct, SIGNAL(triggered()), this, SLOT(OnShowHoverLine())); + + settingsMenu->addAction(clearPlotAct); + settingsMenu->addAction(this->dataPtr->deleteCanvasAct); + settingsMenu->addAction(showGridAct); + settingsMenu->addAction(showHoverLineAct); + + QToolButton *settingsButton = new QToolButton(); + settingsButton->setObjectName("plotCanvasTitleTool"); + settingsButton->installEventFilter(this); + settingsButton->setToolTip(tr("Settings")); + settingsButton->setIcon(QIcon(":/images/settings.svg")); + settingsButton->setIconSize(QSize(25, 25)); + settingsButton->setFixedSize(QSize(45, 35)); + settingsButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + settingsButton->setPopupMode(QToolButton::InstantPopup); + settingsButton->setMenu(settingsMenu); + + QHBoxLayout *settingsLayout = new QHBoxLayout; + settingsLayout->addWidget(settingsButton); + + QHBoxLayout *titleSettingsLayout = new QHBoxLayout; + titleSettingsLayout->addLayout(titleLayout); + titleSettingsLayout->addLayout(settingsLayout); + titleSettingsLayout->setContentsMargins(0, 0, 0, 0); + + QFrame *titleFrame = new QFrame; + titleFrame->setObjectName("plotCanvasTitleFrame"); + titleFrame->setLayout(titleSettingsLayout); + + // X and Y variable containers + VariablePillContainer *xVariableContainer = new VariablePillContainer(this); + xVariableContainer->SetText("x "); + xVariableContainer->SetMaxSize(1); + xVariableContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + xVariableContainer->setContentsMargins(0, 0, 0, 0); + // \todo: fix hardcoded x axis + xVariableContainer->AddVariablePill("sim_time"); + xVariableContainer->setEnabled(false); + + this->dataPtr->yVariableContainer = new VariablePillContainer(this); + this->dataPtr->yVariableContainer->SetText("y "); + this->dataPtr->yVariableContainer->setSizePolicy( + QSizePolicy::Expanding, QSizePolicy::Fixed); + this->dataPtr->yVariableContainer->setContentsMargins(0, 0, 0, 0); + + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableAdded(unsigned int, std::string, unsigned int)), + this, SLOT(OnAddVariable(unsigned int, std::string, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableRemoved(unsigned int, unsigned int)), + this, SLOT(OnRemoveVariable(unsigned int, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableMoved(unsigned int, unsigned int)), + this, SLOT(OnMoveVariable(unsigned int, unsigned int))); + connect(this->dataPtr->yVariableContainer, + SIGNAL(VariableLabelChanged(unsigned int, std::string)), + this, SLOT(OnSetVariableLabel(unsigned int, std::string))); + + QVBoxLayout *variableContainerLayout = new QVBoxLayout; + variableContainerLayout->addWidget(xVariableContainer); + variableContainerLayout->addWidget(this->dataPtr->yVariableContainer); + variableContainerLayout->setSpacing(0); + variableContainerLayout->setContentsMargins(0, 0, 0, 0); + + // plot + QScrollArea *plotScrollArea = new QScrollArea(this); + plotScrollArea->setObjectName("plotScrollArea"); + plotScrollArea->setLineWidth(0); + plotScrollArea->setFrameShape(QFrame::NoFrame); + plotScrollArea->setFrameShadow(QFrame::Plain); + plotScrollArea->setSizePolicy(QSizePolicy::Minimum, + QSizePolicy::Expanding); + + plotScrollArea->setWidgetResizable(true); + plotScrollArea->viewport()->installEventFilter(this); + + QFrame *plotFrame = new QFrame(plotScrollArea); + plotFrame->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + plotFrame->setObjectName("plotCanvasPlotFrame"); + QVBoxLayout *plotLayout = new QVBoxLayout; + plotFrame->setLayout(plotLayout); + + this->dataPtr->plotSplitter = new QSplitter(Qt::Vertical); + this->dataPtr->plotSplitter->setVisible(false); + plotLayout->addWidget(this->dataPtr->plotSplitter); + + plotScrollArea->setWidget(plotFrame); + + // empty plot + this->dataPtr->emptyPlot = new IncrementalPlot(this); + connect(this->dataPtr->emptyPlot, SIGNAL(VariableAdded(std::string)), + this, SLOT(OnAddVariable(std::string))); + plotLayout->addWidget(this->dataPtr->emptyPlot); + + // set initial show grid state + showGridAct->setChecked(this->dataPtr->emptyPlot->IsShowGrid()); + connect(showGridAct, SIGNAL(triggered()), this, SLOT(OnShowGrid())); + + QFrame *mainFrame = new QFrame; + mainFrame->setObjectName("plotCanvasFrame"); + QVBoxLayout *mainFrameLayout = new QVBoxLayout; + mainFrameLayout->addWidget(titleFrame); + mainFrameLayout->addLayout(variableContainerLayout); + mainFrameLayout->addWidget(plotScrollArea); + mainFrameLayout->setContentsMargins(0, 0, 0, 0); + mainFrame->setLayout(mainFrameLayout); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(mainFrame); + mainLayout->setContentsMargins(0, 0, 0, 0); + this->setLayout(mainLayout); +} + +///////////////////////////////////////////////// +PlotCanvas::~PlotCanvas() +{ + this->Clear(); } ///////////////////////////////////////////////// void PlotCanvas::SetVariableLabel(const unsigned int _id, - const std::string &_label) { - // set new variable labeland let the signals/slots do the work on updating - // the plot curve - return this->dataPtr->yVariableContainer->SetVariablePillLabel(_id, _label); + const std::string &_label) +{ + // set new variable labeland let the signals/slots do the work on updating + // the plot curve + return this->dataPtr->yVariableContainer->SetVariablePillLabel(_id, _label); } ///////////////////////////////////////////////// unsigned int PlotCanvas::AddVariable(const std::string &_variable, - const unsigned int _plotId) { - unsigned int targetId = VariablePill::EmptyVariable; - - if (_plotId != EmptyPlot) { - // find a variable that belongs to the specified plotId and make that the - // the target variable that the new variable will be added to - auto it = this->dataPtr->plotData.find(_plotId); - if (it != this->dataPtr->plotData.end() - && !it->second->variableCurves.empty()) { - targetId = it->second->variableCurves.begin()->first; - } - } - - // add to container and let the signals/slots do the work on adding the - // a new plot with the curve in the overloaded AddVariable function - unsigned int _retVal = this->dataPtr->yVariableContainer->AddVariablePill( + const unsigned int _plotId) +{ + unsigned int targetId = VariablePill::EmptyVariable; + if (_plotId != EmptyPlot) + { + // find a variable that belongs to the specified plotId and make that the + // the target variable that the new variable will be added to + auto it = this->dataPtr->plotData.find(_plotId); + if (it != this->dataPtr->plotData.end() && + !it->second->variableCurves.empty()) + { + targetId = it->second->variableCurves.begin()->first; + } + } + + // add to container and let the signals/slots do the work on adding the + // a new plot with the curve in the overloaded AddVariable function + unsigned int _retVal = this->dataPtr->yVariableContainer->AddVariablePill( _variable, targetId); - return _retVal; + return _retVal; } ///////////////////////////////////////////////// void PlotCanvas::AddVariable(const unsigned int _id, - const std::string &_variable, const unsigned int _plotId) { - unsigned int plotId; - if (_plotId == EmptyPlot) { - // create new plot for the variable and add plot to canvas - plotId = this->AddPlot(); - } else - plotId = _plotId; - - // add variable to existing plot - auto it = this->dataPtr->plotData.find(plotId); - if (it == this->dataPtr->plotData.end()) - return; - - PlotData *p = it->second; - PlotCurveWeakPtr curve = p->plot->AddCurve(_variable); - auto c = curve.lock(); - if (c) { - p->variableCurves[_id] = c->Id(); - } else { - gzerr << "Unable to add curve to plot" << std::endl; - return; - } - - // hide initial empty plot - if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { - this->dataPtr->emptyPlot->setVisible(false); - this->dataPtr->plotSplitter->setVisible(true); - } - - if (common::URI::Valid(_variable)) { - common::URI uri(_variable); - std::string schemeStr = uri.Scheme(); - if (schemeStr == "data") { - PlotManager::Instance()->AddIntrospectionCurve(_variable, curve); - // give it a more compact, friendly name - // do this after PlotManager AddIntrospectionCurve call! - std::string label = PlotManager::Instance()->HumanReadableName( - _variable); - this->SetVariableLabel(_id, label); - } - } else { - PlotManager::Instance()->AddTopicCurve(_variable, curve); - } + const std::string &_variable, const unsigned int _plotId) +{ + unsigned int plotId; + if (_plotId == EmptyPlot) + { + // create new plot for the variable and add plot to canvas + plotId = this->AddPlot(); + } + else + plotId = _plotId; + + // add variable to existing plot + auto it = this->dataPtr->plotData.find(plotId); + if (it == this->dataPtr->plotData.end()) + return; + + PlotData *p = it->second; + PlotCurveWeakPtr curve = p->plot->AddCurve(_variable); + auto c = curve.lock(); + if (c) + { + p->variableCurves[_id] = c->Id(); + } + else + { + gzerr << "Unable to add curve to plot" << std::endl; + return; + } + + // hide initial empty plot + if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) + { + this->dataPtr->emptyPlot->setVisible(false); + this->dataPtr->plotSplitter->setVisible(true); + } + + if (common::URI::Valid(_variable)) + { + common::URI uri(_variable); + std::string schemeStr = uri.Scheme(); + if (schemeStr == "data") + { + PlotManager::Instance()->AddIntrospectionCurve(_variable, curve); + // give it a more compact, friendly name + // do this after PlotManager AddIntrospectionCurve call! + std::string label = PlotManager::Instance()->HumanReadableName(_variable); + this->SetVariableLabel(_id, label); + } + } + else + { + PlotManager::Instance()->AddTopicCurve(_variable, curve); + } } ///////////////////////////////////////////////// void PlotCanvas::AddVariable(const std::string &_variable, - IncrementalPlot *plot_in) { - if (!plot_in) - return; - - if (plot_in == this->dataPtr->emptyPlot) { - // add new variable to new plot - this->AddVariable(_variable); - } else { - for (const auto &it : this->dataPtr->plotData) { - if (plot_in == it.second->plot) { - // add to existing plot - this->AddVariable(_variable, it.second->id); - return; - } - } - } + IncrementalPlot *plot_in) +{ + if (!plot_in) + return; + + if (plot_in == this->dataPtr->emptyPlot) + { + // add new variable to new plot + this->AddVariable(_variable); + } + else + { + for (const auto &it : this->dataPtr->plotData) + { + if (plot_in == it.second->plot) + { + // add to existing plot + this->AddVariable(_variable, it.second->id); + return; + } + } + } } ///////////////////////////////////////////////// void PlotCanvas::RemoveVariable(const unsigned int _id, - const unsigned int _plotId) { - auto it = this->dataPtr->plotData.end(); - if (_plotId == EmptyPlot) { - // find which plot the variable belongs to - for (auto pIt = this->dataPtr->plotData.begin(); - pIt != this->dataPtr->plotData.end(); ++pIt) { - auto v = pIt->second->variableCurves.find(_id); - if (v != pIt->second->variableCurves.end()) { - it = pIt; - break; - } - } - } else { - // get the plot which the variable belongs to - it = this->dataPtr->plotData.find(_plotId); - } - - if (it == this->dataPtr->plotData.end()) - return; - - auto v = it->second->variableCurves.find(_id); - if (v == it->second->variableCurves.end()) - return; - - unsigned int curveId = v->second; - - // remove curve from manager - PlotCurveWeakPtr plotCurve = it->second->plot->Curve(curveId); - std::string curveLabel; - { - auto pc = plotCurve.lock(); - curveLabel = pc->Label(); - } - // assume topic name starts with '/' - if (!curveLabel.empty() && curveLabel[0] == '/') - PlotManager::Instance()->RemoveTopicCurve(plotCurve); - else - PlotManager::Instance()->RemoveIntrospectionCurve(plotCurve); - - // erase from map - it->second->variableCurves.erase(v); - - // delete whole plot if no more curves - if (it->second->variableCurves.empty()) { - it->second->plot->hide(); - it->second->plot->RemoveCurve(curveId); - delete it->second->plot; - delete it->second; - this->dataPtr->plotData.erase(it); - - this->UpdateAxisLabel(); - } else { - it->second->plot->RemoveCurve(curveId); - } - - if (this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { - this->dataPtr->emptyPlot->setVisible(true); - this->dataPtr->plotSplitter->setVisible(false); - } - - // remove from variable pill container - this->dataPtr->yVariableContainer->RemoveVariablePill(_id); -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::AddPlot() { - IncrementalPlot *plot = new IncrementalPlot(this); - plot->setAutoDelete(false); - plot->ShowGrid(this->dataPtr->emptyPlot->IsShowGrid()); - plot->ShowHoverLine(this->dataPtr->emptyPlot->IsShowHoverLine()); - connect(plot, SIGNAL(VariableAdded(std::string)), this, - SLOT(OnAddVariable(std::string))); - this->dataPtr->plotSplitter->addWidget(plot); - - PlotData *p = new PlotData; - p->id = this->dataPtr->globalPlotId++; - p->plot = plot; - this->dataPtr->plotData[p->id] = p; - - this->UpdateAxisLabel(); - - return p->id; -} - -///////////////////////////////////////////////// -void PlotCanvas::RemovePlot(const unsigned int _id) { - auto it = this->dataPtr->plotData.find(_id); - if (it == this->dataPtr->plotData.end()) - return; - - // remove the plot if it does not contain any variableCurves (curves) - if (it->second->variableCurves.empty()) { - it->second->plot->hide(); - delete it->second->plot; - delete it->second; - this->dataPtr->plotData.erase(it); - return; - } - - unsigned int plotId = it->first; - // remove all variableCurves except last one - while (it->second->variableCurves.size() > 1) { - auto v = it->second->variableCurves.begin(); - this->RemoveVariable(v->first, plotId); - } - - // remove last variable - this will also delete the plot which causes - // plot data iterator to be invalid. So do this last. - this->RemoveVariable(it->second->variableCurves.begin()->first, plotId); - - this->UpdateAxisLabel(); -} - -///////////////////////////////////////////////// -void PlotCanvas::Clear() { - while (!this->dataPtr->plotData.empty()) { - auto p = this->dataPtr->plotData.begin(); - this->RemovePlot(p->first); - } + const unsigned int _plotId) +{ + auto it = this->dataPtr->plotData.end(); + if (_plotId == EmptyPlot) + { + // find which plot the variable belongs to + for (auto pIt = this->dataPtr->plotData.begin(); + pIt != this->dataPtr->plotData.end(); ++pIt) + { + auto v = pIt->second->variableCurves.find(_id); + if (v != pIt->second->variableCurves.end()) + { + it = pIt; + break; + } + } + } + else + { + // get the plot which the variable belongs to + it = this->dataPtr->plotData.find(_plotId); + } + + if (it == this->dataPtr->plotData.end()) + return; + + auto v = it->second->variableCurves.find(_id); + if (v == it->second->variableCurves.end()) + return; + + unsigned int curveId = v->second; + + // remove curve from manager + PlotCurveWeakPtr plotCurve = it->second->plot->Curve(curveId); + std::string curveLabel; + { + auto pc = plotCurve.lock(); + curveLabel = pc->Label(); + } + // assume topic name starts with '/' + if (!curveLabel.empty() && curveLabel[0] == '/') + PlotManager::Instance()->RemoveTopicCurve(plotCurve); + else + PlotManager::Instance()->RemoveIntrospectionCurve(plotCurve); + + // erase from map + it->second->variableCurves.erase(v); + + // delete whole plot if no more curves + if (it->second->variableCurves.empty()) + { + it->second->plot->hide(); + it->second->plot->RemoveCurve(curveId); + delete it->second->plot; + delete it->second; + this->dataPtr->plotData.erase(it); + + this->UpdateAxisLabel(); + } + else + { + it->second->plot->RemoveCurve(curveId); + } + + if (this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) + { + this->dataPtr->emptyPlot->setVisible(true); + this->dataPtr->plotSplitter->setVisible(false); + } + + // remove from variable pill container + this->dataPtr->yVariableContainer->RemoveVariablePill(_id); +} + + +///////////////////////////////////////////////// +unsigned int PlotCanvas::AddPlot() +{ + IncrementalPlot *plot = new IncrementalPlot(this); + plot->setAutoDelete(false); + plot->ShowGrid(this->dataPtr->emptyPlot->IsShowGrid()); + plot->ShowHoverLine(this->dataPtr->emptyPlot->IsShowHoverLine()); + connect(plot, SIGNAL(VariableAdded(std::string)), this, + SLOT(OnAddVariable(std::string))); + this->dataPtr->plotSplitter->addWidget(plot); + + PlotData *p = new PlotData; + p->id = this->dataPtr->globalPlotId++; + p->plot = plot; + this->dataPtr->plotData[p->id] = p; + + this->UpdateAxisLabel(); + + return p->id; +} + +///////////////////////////////////////////////// +void PlotCanvas::RemovePlot(const unsigned int _id) +{ + auto it = this->dataPtr->plotData.find(_id); + if (it == this->dataPtr->plotData.end()) + return; + + // remove the plot if it does not contain any variableCurves (curves) + if (it->second->variableCurves.empty()) + { + it->second->plot->hide(); + delete it->second->plot; + delete it->second; + this->dataPtr->plotData.erase(it); + return; + } + + unsigned int plotId = it->first; + // remove all variableCurves except last one + while (it->second->variableCurves.size() > 1) + { + auto v = it->second->variableCurves.begin(); + this->RemoveVariable(v->first, plotId); + } + + // remove last variable - this will also delete the plot which causes + // plot data iterator to be invalid. So do this last. + this->RemoveVariable(it->second->variableCurves.begin()->first, plotId); + + this->UpdateAxisLabel(); +} + +///////////////////////////////////////////////// +void PlotCanvas::Clear() +{ + while (!this->dataPtr->plotData.empty()) + { + auto p = this->dataPtr->plotData.begin(); + this->RemovePlot(p->first); + } +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::PlotByVariable(const unsigned int _variableId) const +{ + for (const auto it : this->dataPtr->plotData) + { + const auto v = it.second->variableCurves.find(_variableId); + if (v != it.second->variableCurves.end()) + { + return it.first; + } + } + return EmptyPlot; } ///////////////////////////////////////////////// -unsigned int PlotCanvas::PlotByVariable(const unsigned int _variableId) const { - for (const auto it : this->dataPtr->plotData) { - const auto v = it.second->variableCurves.find(_variableId); - if (v != it.second->variableCurves.end()) { - return it.first; - } - } - return EmptyPlot; -} - -///////////////////////////////////////////////// -void PlotCanvas::OnAddVariable(const std::string &_variable) { +void PlotCanvas::OnAddVariable(const std::string &_variable) +{ // gzdbg << "PlotCanvas::OnAddVariable: " << _variable << std::endl; - IncrementalPlot *plot = qobject_cast(QObject::sender()); + IncrementalPlot *plot = + qobject_cast(QObject::sender()); - if (!plot) - return; + if (!plot) + return; - if (plot == this->dataPtr->emptyPlot) { - // add new variable to new plot - this->AddVariable(_variable); - } else { - for (const auto &it : this->dataPtr->plotData) { - if (plot == it.second->plot) { - // add to existing plot - this->AddVariable(_variable, it.second->id); - return; - } - } - } + if (plot == this->dataPtr->emptyPlot) + { + // add new variable to new plot + this->AddVariable(_variable); + } + else + { + for (const auto &it : this->dataPtr->plotData) + { + if (plot == it.second->plot) + { + // add to existing plot + this->AddVariable(_variable, it.second->id); + return; + } + } + } } ///////////////////////////////////////////////// void PlotCanvas::OnAddVariable(const unsigned int _id, - const std::string &_variable, const unsigned int _targetId) { - if (_targetId != VariablePill::EmptyVariable) { - // Add a variable to existing plot - for (const auto it : this->dataPtr->plotData) { - const auto v = it.second->variableCurves.find(_targetId); - if (v != it.second->variableCurves.end()) { - this->AddVariable(_id, _variable, it.second->id); - break; - } - } - } else { - // add variable to new plot - this->AddVariable(_id, _variable); - } + const std::string &_variable, const unsigned int _targetId) +{ + if (_targetId != VariablePill::EmptyVariable) + { + // Add a variable to existing plot + for (const auto it : this->dataPtr->plotData) + { + const auto v = it.second->variableCurves.find(_targetId); + if (v != it.second->variableCurves.end()) + { + this->AddVariable(_id, _variable, it.second->id); + break; + } + } + } + else + { + // add variable to new plot + this->AddVariable(_id, _variable); + } } ///////////////////////////////////////////////// void PlotCanvas::OnRemoveVariable(const unsigned int _id, - const unsigned int /*_targetId*/) { - this->RemoveVariable(_id); + const unsigned int /*_targetId*/) +{ + this->RemoveVariable(_id); } ///////////////////////////////////////////////// void PlotCanvas::OnMoveVariable(const unsigned int _id, - const unsigned int _targetId) { - auto plotIt = this->dataPtr->plotData.end(); - auto targetPlotIt = this->dataPtr->plotData.end(); - unsigned int curveId = 0; - - // find plot which the variable belongs to - // find target plot (if any) that the variable will be moved to - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) { - auto v = it->second->variableCurves.find(_id); - if (v != it->second->variableCurves.end()) { - plotIt = it; - curveId = v->second; - } - - if (it->second->variableCurves.find(_targetId) - != it->second->variableCurves.end()) { - targetPlotIt = it; - } - - if (plotIt != this->dataPtr->plotData.end() - && targetPlotIt != this->dataPtr->plotData.end()) - break; - } - - // detach from old plot and attach to new one - if (plotIt != this->dataPtr->plotData.end()) { - PlotData *p = plotIt->second; - - // detach variable from plot (qwt plot doesn't seem to do anything - // apart from setting the plot item to null) - PlotCurvePtr plotCurve = p->plot->DetachCurve(curveId); - p->variableCurves.erase(p->variableCurves.find(_id)); - - if (targetPlotIt != this->dataPtr->plotData.end()) { - // attach variable to target plot - targetPlotIt->second->plot->AttachCurve(plotCurve); - targetPlotIt->second->variableCurves[_id] = plotCurve->Id(); - } else { - // create new plot - unsigned int plotId = this->AddPlot(); - auto it = this->dataPtr->plotData.find(plotId); - GZ_ASSERT(it != this->dataPtr->plotData.end(), - "Failed to add new plot"); - PlotData *newPlotData = it->second; - // attach curve to plot - newPlotData->plot->AttachCurve(plotCurve); - newPlotData->variableCurves[_id] = plotCurve->Id(); - - // hide initial empty plot - if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) { - this->dataPtr->emptyPlot->setVisible(false); - this->dataPtr->plotSplitter->setVisible(true); - } - } - - // delete plot if empty - if (p->variableCurves.empty()) { - p->plot->hide(); - - // careful about deleting by iterator (plotIt) as it may have been - // changed if a new plot is added to the vector - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) { - if (it->second == p) { - this->dataPtr->plotData.erase(it); - break; - } - } - - p->plot->detachItems(QwtPlotItem::Rtti_PlotItem, false); - delete p->plot; - delete p; - - this->UpdateAxisLabel(); - } - } + const unsigned int _targetId) +{ + auto plotIt = this->dataPtr->plotData.end(); + auto targetPlotIt = this->dataPtr->plotData.end(); + unsigned int curveId = 0; + + // find plot which the variable belongs to + // find target plot (if any) that the variable will be moved to + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) + { + auto v = it->second->variableCurves.find(_id); + if (v != it->second->variableCurves.end()) + { + plotIt = it; + curveId = v->second; + } + + if (it->second->variableCurves.find(_targetId) != + it->second->variableCurves.end()) + { + targetPlotIt = it; + } + + if (plotIt != this->dataPtr->plotData.end() && + targetPlotIt != this->dataPtr->plotData.end()) + break; + } + + // detach from old plot and attach to new one + if (plotIt != this->dataPtr->plotData.end()) + { + PlotData *p = plotIt->second; + + // detach variable from plot (qwt plot doesn't seem to do anything + // apart from setting the plot item to null) + PlotCurvePtr plotCurve = p->plot->DetachCurve(curveId); + p->variableCurves.erase(p->variableCurves.find(_id)); + + if (targetPlotIt != this->dataPtr->plotData.end()) + { + // attach variable to target plot + targetPlotIt->second->plot->AttachCurve(plotCurve); + targetPlotIt->second->variableCurves[_id] = plotCurve->Id(); + } + else + { + // create new plot + unsigned int plotId = this->AddPlot(); + auto it = this->dataPtr->plotData.find(plotId); + GZ_ASSERT(it != this->dataPtr->plotData.end(), "Failed to add new plot"); + PlotData *newPlotData = it->second; + // attach curve to plot + newPlotData->plot->AttachCurve(plotCurve); + newPlotData->variableCurves[_id] = plotCurve->Id(); + + // hide initial empty plot + if (!this->dataPtr->plotData.empty() && this->dataPtr->emptyPlot) + { + this->dataPtr->emptyPlot->setVisible(false); + this->dataPtr->plotSplitter->setVisible(true); + } + } + + // delete plot if empty + if (p->variableCurves.empty()) + { + p->plot->hide(); + + // careful about deleting by iterator (plotIt) as it may have been + // changed if a new plot is added to the vector + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) + { + if (it->second == p) + { + this->dataPtr->plotData.erase(it); + break; + } + } + + p->plot->detachItems(QwtPlotItem::Rtti_PlotItem, false); + delete p->plot; + delete p; + + this->UpdateAxisLabel(); + } + } } ///////////////////////////////////////////////// void PlotCanvas::OnSetVariableLabel(const unsigned int _id, - const std::string &_label) { - // find plot which the variable belongs to - for (auto it = this->dataPtr->plotData.begin(); - it != this->dataPtr->plotData.end(); ++it) { - auto v = it->second->variableCurves.find(_id); - if (v != it->second->variableCurves.end()) { - it->second->plot->SetCurveLabel(v->second, _label); - break; - } - } -} - -///////////////////////////////////////////////// -void PlotCanvas::Update() { - // Update all the plots - for (auto p : this->dataPtr->plotData) - p.second->plot->Update(); -} - -///////////////////////////////////////////////// -void PlotCanvas::Restart() { - // tuple of original variable label, variable pointer, plot id to add - // variable to. - std::vector> variableCurvesToClone; - - // Restart all the plots - for (const auto &it : this->dataPtr->plotData) { - for (const auto &v : it.second->variableCurves) { - unsigned int variableId = v.first; - unsigned int curveId = v.second; - - // get variable pill - VariablePill *variablePill = - this->dataPtr->yVariableContainer->GetVariablePill( - variableId); - - if (!variablePill) - continue; - - PlotCurveWeakPtr curve = it.second->plot->Curve(curveId); - auto c = curve.lock(); - if (!c) - continue; - - if (c->Active()) { - c->SetActive(false); - // remove from manager so they don't get updated any more. - // assume topic name starts with '/' - std::string curveLabel = c->Label(); - if (!curveLabel.empty() && curveLabel[0] == '/') - PlotManager::Instance()->RemoveTopicCurve(c); - else - PlotManager::Instance()->RemoveIntrospectionCurve(c); - - // add to the list of variables to clone - variableCurvesToClone.push_back( - std::make_tuple(variablePill->Text(), variablePill, - it.first)); - } - // increment curve age. - c->SetAge(c->Age() + 1); - - // update the label of variable pill to show the age of the curve - std::string varText = variablePill->Text(); - std::stringstream ss; - if (c->Age() == 1u) { - // update curve label and append curve age to the end - ss << varText << "_1"; - this->SetVariableLabel(variableId, ss.str()); - } else { - // if it's more than one simulation-run old, update the age suffix - size_t idx = varText.rfind("_"); - if (idx != std::string::npos) { - int age = std::atoi(varText.substr(idx + 1).c_str()); - if (age > 0) { - ss << varText.substr(0, idx + 1) << c->Age(); - this->SetVariableLabel(variableId, ss.str()); - } - } - } - } - } + const std::string &_label) +{ + // find plot which the variable belongs to + for (auto it = this->dataPtr->plotData.begin(); + it != this->dataPtr->plotData.end(); ++it) + { + auto v = it->second->variableCurves.find(_id); + if (v != it->second->variableCurves.end()) + { + it->second->plot->SetCurveLabel(v->second, _label); + break; + } + } +} + +///////////////////////////////////////////////// +void PlotCanvas::Update() +{ + // Update all the plots + for (auto p : this->dataPtr->plotData) + p.second->plot->Update(); +} + +///////////////////////////////////////////////// +void PlotCanvas::Restart() +{ + // tuple of original variable label, variable pointer, plot id to add + // variable to. + std::vector> + variableCurvesToClone; + + // Restart all the plots + for (const auto &it : this->dataPtr->plotData) + { + for (const auto &v : it.second->variableCurves) + { + unsigned int variableId = v.first; + unsigned int curveId = v.second; + + // get variable pill + VariablePill *variablePill = + this->dataPtr->yVariableContainer->GetVariablePill(variableId); + + if (!variablePill) + continue; + + PlotCurveWeakPtr curve = it.second->plot->Curve(curveId); + auto c = curve.lock(); + if (!c) + continue; + + if (c->Active()) + { + c->SetActive(false); + // remove from manager so they don't get updated any more. + // assume topic name starts with '/' + std::string curveLabel = c->Label(); + if (!curveLabel.empty() && curveLabel[0] == '/') + PlotManager::Instance()->RemoveTopicCurve(c); + else + PlotManager::Instance()->RemoveIntrospectionCurve(c); + + // add to the list of variables to clone + variableCurvesToClone.push_back(std::make_tuple(variablePill->Text(), + variablePill, it.first)); + } + // increment curve age. + c->SetAge(c->Age()+1); + + // update the label of variable pill to show the age of the curve + std::string varText = variablePill->Text(); + std::stringstream ss; + if (c->Age() == 1u) + { + // update curve label and append curve age to the end + ss << varText << "_1"; + this->SetVariableLabel(variableId, ss.str()); + } + else + { + // if it's more than one simulation-run old, update the age suffix + size_t idx = varText.rfind("_"); + if (idx != std::string::npos) + { + int age = std::atoi(varText.substr(idx+1).c_str()); + if (age > 0) + { + ss << varText.substr(0, idx+1) << c->Age(); + this->SetVariableLabel(variableId, ss.str()); + } + } + } + } + } + + // add new copy of variable pill with original label + for (const auto &v : variableCurvesToClone) + { + std::string varText; + VariablePill *varPill; + unsigned int plotId; + std::tie(varText, varPill, plotId) = v; + + // add clones of the variable - this will also add it to the plot manager. + unsigned int id = this->AddVariable(varPill->Name(), plotId); + + // give it the original variable label + if (varText != varPill->Name()) + this->SetVariableLabel(id, varText); + } +} + +///////////////////////////////////////////////// +bool PlotCanvas::eventFilter(QObject *_o, QEvent *_e) +{ + if (_e->type() == QEvent::Wheel) + { + _e->ignore(); + return true; + } + + return QWidget::eventFilter(_o, _e); +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::PlotCount() const +{ + unsigned int plotCount = this->dataPtr->plotData.size(); + if (this->dataPtr->emptyPlot) + plotCount += (this->dataPtr->emptyPlot->isVisible() ? 1u : 0u); + + return plotCount; +} + +///////////////////////////////////////////////// +unsigned int PlotCanvas::VariableCount(const unsigned int _plotId) const +{ + auto it = this->dataPtr->plotData.find(_plotId); + if (it == this->dataPtr->plotData.end()) + return 0u; - // add new copy of variable pill with original label - for (const auto &v : variableCurvesToClone) { - std::string varText; - VariablePill *varPill; - unsigned int plotId; - std::tie(varText, varPill, plotId) = v; - - // add clones of the variable - this will also add it to the plot manager. - unsigned int id = this->AddVariable(varPill->Name(), plotId); - - // give it the original variable label - if (varText != varPill->Name()) - this->SetVariableLabel(id, varText); - } -} - -///////////////////////////////////////////////// -bool PlotCanvas::eventFilter(QObject *_o, QEvent *_e) { - if (_e->type() == QEvent::Wheel) { - _e->ignore(); - return true; - } - - return QWidget::eventFilter(_o, _e); -} - -///////////////////////////////////////////////// -unsigned int PlotCanvas::PlotCount() const { - unsigned int plotCount = this->dataPtr->plotData.size(); - if (this->dataPtr->emptyPlot) - plotCount += (this->dataPtr->emptyPlot->isVisible() ? 1u : 0u); - - return plotCount; + return it->second->variableCurves.size(); } - -///////////////////////////////////////////////// -unsigned int PlotCanvas::VariableCount(const unsigned int _plotId) const { - auto it = this->dataPtr->plotData.find(_plotId); - if (it == this->dataPtr->plotData.end()) - return 0u; - - return it->second->variableCurves.size(); -} - -///////////////////////////////////////////////// -PlotCurveWeakPtr PlotCanvas::PlotCurve(const unsigned int _variableId) { - for (const auto it : this->dataPtr->plotData) { - const auto v = it.second->variableCurves.find(_variableId); - if (v != it.second->variableCurves.end()) { - return it.second->plot->Curve(v->second); - } - } - return PlotCurveWeakPtr(); + +///////////////////////////////////////////////// +PlotCurveWeakPtr PlotCanvas::PlotCurve(const unsigned int _variableId) +{ + for (const auto it : this->dataPtr->plotData) + { + const auto v = it.second->variableCurves.find(_variableId); + if (v != it.second->variableCurves.end()) + { + return it.second->plot->Curve(v->second); + } + } + return PlotCurveWeakPtr(); } ///////////////////////////////////////////////// -std::vector PlotCanvas::Plots() const { - std::vector plots; - for (const auto it : this->dataPtr->plotData) - plots.push_back(it.second->plot); +std::vector PlotCanvas::Plots() const +{ + std::vector plots; + for (const auto it : this->dataPtr->plotData) + plots.push_back(it.second->plot); - return plots; + return plots; } ///////////////////////////////////////////////// -void PlotCanvas::OnClearCanvas() { - // Ask for confirmation - std::string msg = "Are you sure you want to clear all fields? \n\n" - "This will remove all the plots in this canvas. \n"; +void PlotCanvas::OnClearCanvas() +{ + // Ask for confirmation + std::string msg = "Are you sure you want to clear all fields? \n\n" + "This will remove all the plots in this canvas. \n"; - QMessageBox msgBox(QMessageBox::Warning, QString("Clear canvas?"), - QString(msg.c_str())); - msgBox.setWindowFlags( - Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint - | Qt::CustomizeWindowHint); + QMessageBox msgBox(QMessageBox::Warning, QString("Clear canvas?"), + QString(msg.c_str())); + msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); - QPushButton *cancelButton = msgBox.addButton("Cancel", - QMessageBox::RejectRole); - QPushButton *clearButton = msgBox.addButton("Clear", - QMessageBox::AcceptRole); - msgBox.setDefaultButton(clearButton); - msgBox.setEscapeButton(cancelButton); - msgBox.show(); - msgBox.move(this->mapToGlobal(this->pos())); - msgBox.exec(); - if (msgBox.clickedButton() != clearButton) - return; + QPushButton *cancelButton = + msgBox.addButton("Cancel", QMessageBox::RejectRole); + QPushButton *clearButton = msgBox.addButton("Clear", + QMessageBox::AcceptRole); + msgBox.setDefaultButton(clearButton); + msgBox.setEscapeButton(cancelButton); + msgBox.show(); + msgBox.move(this->mapToGlobal(this->pos())); + msgBox.exec(); + if (msgBox.clickedButton() != clearButton) + return; - this->Clear(); + this->Clear(); } ///////////////////////////////////////////////// -void PlotCanvas::OnDeleteCanvas() { - // Ask for confirmation - std::string msg = "Are you sure you want to delete the entire canvas? \n"; +void PlotCanvas::OnDeleteCanvas() +{ + // Ask for confirmation + std::string msg = "Are you sure you want to delete the entire canvas? \n"; - QMessageBox msgBox(QMessageBox::Warning, QString("Delete canvas?"), - QString(msg.c_str())); - msgBox.setWindowFlags( - Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint - | Qt::CustomizeWindowHint); + QMessageBox msgBox(QMessageBox::Warning, QString("Delete canvas?"), + QString(msg.c_str())); + msgBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); - QPushButton *cancelButton = msgBox.addButton("Cancel", - QMessageBox::RejectRole); - QPushButton *deleteButton = msgBox.addButton("Delete", - QMessageBox::AcceptRole); - msgBox.setDefaultButton(deleteButton); - msgBox.setEscapeButton(cancelButton); - msgBox.show(); - msgBox.move(this->mapToGlobal(this->pos())); - msgBox.exec(); - if (msgBox.clickedButton() != deleteButton) - return; + QPushButton *cancelButton = + msgBox.addButton("Cancel", QMessageBox::RejectRole); + QPushButton *deleteButton = msgBox.addButton("Delete", + QMessageBox::AcceptRole); + msgBox.setDefaultButton(deleteButton); + msgBox.setEscapeButton(cancelButton); + msgBox.show(); + msgBox.move(this->mapToGlobal(this->pos())); + msgBox.exec(); + if (msgBox.clickedButton() != deleteButton) + return; - emit CanvasDeleted(); + emit CanvasDeleted(); } ///////////////////////////////////////////////// -void PlotCanvas::OnShowGrid() { - this->dataPtr->emptyPlot->ShowGrid(!this->dataPtr->emptyPlot->IsShowGrid()); +void PlotCanvas::OnShowGrid() +{ + this->dataPtr->emptyPlot->ShowGrid(!this->dataPtr->emptyPlot->IsShowGrid()); - for (const auto &it : this->dataPtr->plotData) - it.second->plot->ShowGrid(!it.second->plot->IsShowGrid()); + for (const auto &it : this->dataPtr->plotData) + it.second->plot->ShowGrid(!it.second->plot->IsShowGrid()); } ///////////////////////////////////////////////// -void PlotCanvas::OnShowHoverLine() { - this->dataPtr->emptyPlot->ShowHoverLine( - !this->dataPtr->emptyPlot->IsShowHoverLine()); +void PlotCanvas::OnShowHoverLine() +{ + this->dataPtr->emptyPlot->ShowHoverLine( + !this->dataPtr->emptyPlot->IsShowHoverLine()); - for (const auto &it : this->dataPtr->plotData) - it.second->plot->ShowHoverLine(!it.second->plot->IsShowHoverLine()); + for (const auto &it : this->dataPtr->plotData) + it.second->plot->ShowHoverLine(!it.second->plot->IsShowHoverLine()); } ///////////////////////////////////////////////// -void PlotCanvas::UpdateAxisLabel() { - // show the x-axis label in the last plot only - for (int i = 0; i < this->dataPtr->plotSplitter->count(); ++i) { - IncrementalPlot *p = qobject_cast( - this->dataPtr->plotSplitter->widget(i)); +void PlotCanvas::UpdateAxisLabel() +{ + // show the x-axis label in the last plot only + for (int i = 0; i < this->dataPtr->plotSplitter->count(); ++i) + { + IncrementalPlot *p = + qobject_cast(this->dataPtr->plotSplitter->widget(i)); - if (p) { - p->ShowAxisLabel(IncrementalPlot::X_BOTTOM_AXIS, - i == (this->dataPtr->plotSplitter->count() - 1)); - } - } + if (p) + { + p->ShowAxisLabel(IncrementalPlot::X_BOTTOM_AXIS, + i == (this->dataPtr->plotSplitter->count()-1)); + } + } } ///////////////////////////////////////////////// -void PlotCanvas::SetDeleteCanvasEnabled(const bool _enable) { - if (this->dataPtr->deleteCanvasAct) - this->dataPtr->deleteCanvasAct->setEnabled(_enable); +void PlotCanvas::SetDeleteCanvasEnabled(const bool _enable) +{ + if (this->dataPtr->deleteCanvasAct) + this->dataPtr->deleteCanvasAct->setEnabled(_enable); } ///////////////////////////////////////////////// -std::string PlotCanvas::Title() const { - return this->dataPtr->title->Text(); +std::string PlotCanvas::Title() const +{ + return this->dataPtr->title->Text(); } ///////////////////////////////////////////////// void PlotCanvas::Export(const std::string &_dirName, - const FileType _type) const { - std::string title = this->Title(); + const FileType _type) const +{ + std::string title = this->Title(); - // Cleanup the title - std::replace(title.begin(), title.end(), '/', '_'); - std::replace(title.begin(), title.end(), '?', ':'); + // Cleanup the title + std::replace(title.begin(), title.end(), '/', '_'); + std::replace(title.begin(), title.end(), '?', ':'); - std::string filePrefix = _dirName + "/" + title; + std::string filePrefix = _dirName + "/" + title; - if (_type == FileType::PDFFile) - this->ExportPDF(filePrefix); - else if (_type == FileType::CSVFile) - this->ExportCSV(filePrefix); + if (_type == FileType::PDFFile) + this->ExportPDF(filePrefix); + else if (_type == FileType::CSVFile) + this->ExportCSV(filePrefix); } ///////////////////////////////////////////////// -void PlotCanvas::ExportPDF(const std::string &_filePrefix) const { - // Render the plot to a PDF - int index = 0; - for (const auto it : this->dataPtr->plotData) { - std::string suffix = - this->dataPtr->plotData.size() > 1 ? std::to_string(index) : ""; +void PlotCanvas::ExportPDF(const std::string &_filePrefix) const +{ + // Render the plot to a PDF + int index = 0; + for (const auto it : this->dataPtr->plotData) + { + std::string suffix = + this->dataPtr->plotData.size() > 1 ? std::to_string(index) : ""; - std::string filename = common::unique_file_path(_filePrefix + suffix, - "pdf"); + std::string filename = + common::unique_file_path(_filePrefix + suffix, "pdf"); - IncrementalPlot *plot = it.second->plot; + IncrementalPlot *plot = it.second->plot; - QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), - plot->canvas()->height()); + QSizeF docSize(plot->canvas()->width() + plot->legend()->width(), + plot->canvas()->height()); - QwtPlotRenderer renderer; - renderer.renderDocument(plot, QString(filename.c_str()), docSize, 20); + QwtPlotRenderer renderer; + renderer.renderDocument(plot, QString(filename.c_str()), docSize, 20); - gzmsg << "Plot exported to file [" << filename << "]" << std::endl; + gzmsg << "Plot exported to file [" << filename << "]" << std::endl; - index++; - } + index++; + } } ///////////////////////////////////////////////// -void PlotCanvas::ExportCSV(const std::string &_filePrefix) const { - // Save data from each curve into a separate file. - for (const auto it : this->dataPtr->plotData) { - for (const auto &curve : it.second->plot->Curves()) { - auto c = curve.lock(); - if (!c) - continue; +void PlotCanvas::ExportCSV(const std::string &_filePrefix) const +{ + // Save data from each curve into a separate file. + for (const auto it : this->dataPtr->plotData) + { + for (const auto &curve : it.second->plot->Curves()) + { + auto c = curve.lock(); + if (!c) + continue; - // Cleanup the label - std::string label = c->Label(); - std::replace(label.begin(), label.end(), '/', '_'); - std::replace(label.begin(), label.end(), '?', ':'); + // Cleanup the label + std::string label = c->Label(); + std::replace(label.begin(), label.end(), '/', '_'); + std::replace(label.begin(), label.end(), '?', ':'); - std::string filename = common::unique_file_path( - _filePrefix + "-" + label, "csv"); + std::string filename = + common::unique_file_path(_filePrefix + "-" + label, "csv"); - std::ofstream out(filename); - // \todo: fix hardcoded sim_time - out << "sim_time, " << c->Label() << std::endl; - for (unsigned int j = 0; j < c->Size(); ++j) { - ignition::math::Vector2d pt = c->Point(j); - out << pt.X() << ", " << pt.Y() << std::endl; - } - out.close(); + std::ofstream out(filename); + // \todo: fix hardcoded sim_time + out << "sim_time, " << c->Label() << std::endl; + for (unsigned int j = 0; j < c->Size(); ++j) + { + ignition::math::Vector2d pt = c->Point(j); + out << pt.X() << ", " << pt.Y() << std::endl; + } + out.close(); - gzmsg << "Plot exported to file [" << filename << "]" << std::endl; - } - } + gzmsg << "Plot exported to file [" << filename << "]" << std::endl; + } + } } diff --git a/gazebo/gui/plot/PlotCanvas.hh b/gazebo/gui/plot/PlotCanvas.hh index 511e5625a1..f58065ac6d 100644 --- a/gazebo/gui/plot/PlotCanvas.hh +++ b/gazebo/gui/plot/PlotCanvas.hh @@ -51,13 +51,15 @@ namespace gazebo /// \brief Set the label of a variable. /// \param[in] _id Unique id of the variable /// \param[in] _label New variable label. - public: void SetVariableLabel(const unsigned int _id, const std::string &_label); + public: void SetVariableLabel(const unsigned int _id, + const std::string &_label); /// \brief Add a new variable to a plot. /// \param[in] _variable Name of the variable. /// \param[in] _plotId Unique id of the plot to add the variable to. /// \return Unique id of the variable - public: unsigned int AddVariable(const std::string &_variable, const unsigned int _plotId = EmptyPlot); + public: unsigned int AddVariable(const std::string &_variable, + const unsigned int _plotId = EmptyPlot); /// \brief Add a new variable to a plot. /// \param[in] _variable Name of the variable. @@ -69,7 +71,8 @@ namespace gazebo /// \param[in] _plotId Unique id of plot to remove the variable from. /// If EmptyPlot is specified, the function will search through all /// plots for the variable and remove it from the plot if found. - public: void RemoveVariable(const unsigned int _id, const unsigned int _plotId = EmptyPlot); + public: void RemoveVariable(const unsigned int _id, + const unsigned int _plotId = EmptyPlot); /// \brief Add a new plot to the canvas. /// \return Unique id of the plot @@ -149,7 +152,9 @@ namespace gazebo /// \param[in] _variable Name of the variable /// \param[in] _plotId Unique id of the plot to add the variable to. /// EmptyPlot means add to a new plot. - private: void AddVariable(const unsigned int _id, const std::string &_variable, const unsigned int _plotId = EmptyPlot); + private: void AddVariable(const unsigned int _id, + const std::string &_variable, + const unsigned int _plotId = EmptyPlot); /// \brief Update the axis label for plots in the canvas. /// Currently used to determine which plot will display the x-axis label diff --git a/gazebo/gui/plot/PlotWindow.cc b/gazebo/gui/plot/PlotWindow.cc index e796efbbc4..bca01139b8 100644 --- a/gazebo/gui/plot/PlotWindow.cc +++ b/gazebo/gui/plot/PlotWindow.cc @@ -40,185 +40,188 @@ using namespace tinyxml2; } #endif -namespace gazebo { -namespace gui { -/// \brief Private data for the PlotWindow class -class PlotWindowPrivate { - /// \brief Splitter to hold all the canvases. -public: - QSplitter *canvasSplitter; - - /// \brief Mutex to protect the canvas updates -public: - std::mutex mutex; - - /// \brief Flag to indicate whether the plots should be restarted. -public: - bool restart = false; -}; -} +namespace gazebo +{ + namespace gui + { + /// \brief Private data for the PlotWindow class + class PlotWindowPrivate + { + /// \brief Splitter to hold all the canvases. + public: QSplitter *canvasSplitter; + + /// \brief Mutex to protect the canvas updates + public: std::mutex mutex; + + /// \brief Flag to indicate whether the plots should be restarted. + public: bool restart = false; + }; + } } // A special list widget that allows dragging of items from it to a // plot -class DragableListWidget: public QListWidget { -public: - explicit DragableListWidget(QWidget *_parent) : - QListWidget(_parent) { - } - -protected: - virtual void startDrag(Qt::DropActions /*_supportedActions*/) { - QListWidgetItem *currItem = this->currentItem(); - QMimeData *currMimeData = new QMimeData; - QByteArray ba; - ba = currItem->text().toLatin1().data(); - currMimeData->setData("application/x-item", ba); - QDrag *drag = new QDrag(this); - drag->setMimeData(currMimeData); - drag->exec(Qt::LinkAction); - } - -protected: - virtual Qt::DropActions supportedDropActions() const { - return Qt::LinkAction; - } +class DragableListWidget : public QListWidget +{ + public: explicit DragableListWidget(QWidget *_parent) + : QListWidget(_parent) + { + } + + protected: virtual void startDrag(Qt::DropActions /*_supportedActions*/) + { + QListWidgetItem *currItem = this->currentItem(); + QMimeData *currMimeData = new QMimeData; + QByteArray ba; + ba = currItem->text().toLatin1().data(); + currMimeData->setData("application/x-item", ba); + QDrag *drag = new QDrag(this); + drag->setMimeData(currMimeData); + drag->exec(Qt::LinkAction); + } + + protected: virtual Qt::DropActions supportedDropActions() const + { + return Qt::LinkAction; + } }; ///////////////////////////////////////////////// -PlotWindow::PlotWindow(QWidget *_parent) : - QWidget(_parent), dataPtr(new PlotWindowPrivate()) { - this->setWindowIcon(QIcon(":/images/gazebo.svg")); - this->setWindowTitle("Gazebo: Plotting Utility"); - this->setObjectName("plotWindow"); - this->setWindowFlags( - Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint - | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); - - // new empty canvas - this->dataPtr->canvasSplitter = new QSplitter(Qt::Vertical); - this->AddCanvas(); - - // export button - QPushButton *exportPlotButton = new QPushButton("Export"); - exportPlotButton->setIcon(QIcon(":/images/file_upload.svg")); - exportPlotButton->setObjectName("plotExport"); - exportPlotButton->setDefault(false); - exportPlotButton->setAutoDefault(false); - exportPlotButton->setToolTip("Export plot data"); - QGraphicsDropShadowEffect *exportPlotShadow = - new QGraphicsDropShadowEffect(); - exportPlotShadow->setBlurRadius(8); - exportPlotShadow->setOffset(0, 0); - exportPlotButton->setGraphicsEffect(exportPlotShadow); - connect(exportPlotButton, SIGNAL(clicked()), this, SLOT(OnExport())); - - // save button - QPushButton *saveCanvasButton = new QPushButton("Save"); - saveCanvasButton->setObjectName("plotSaveCanvas"); - saveCanvasButton->setDefault(false); - saveCanvasButton->setAutoDefault(false); - saveCanvasButton->setToolTip("Save canvas"); - QGraphicsDropShadowEffect *saveCanvasShadow = - new QGraphicsDropShadowEffect(); - saveCanvasShadow->setBlurRadius(8); - saveCanvasShadow->setOffset(0, 0); - saveCanvasButton->setGraphicsEffect(saveCanvasShadow); - connect(saveCanvasButton, SIGNAL(clicked()), this, SLOT(OnSaveCanvas())); - - // load button - QPushButton *loadCanvasButton = new QPushButton("Load"); - loadCanvasButton->setObjectName("plotLoadCanvas"); - loadCanvasButton->setDefault(false); - loadCanvasButton->setAutoDefault(false); - loadCanvasButton->setToolTip("Load canvas"); - QGraphicsDropShadowEffect *loadCanvasShadow = - new QGraphicsDropShadowEffect(); - loadCanvasShadow->setBlurRadius(8); - loadCanvasShadow->setOffset(0, 0); - loadCanvasButton->setGraphicsEffect(loadCanvasShadow); - connect(loadCanvasButton, SIGNAL(clicked()), this, SLOT(OnLoadCanvas())); - - // add button - QPushButton *addCanvasButton = new QPushButton("+"); - addCanvasButton->setObjectName("plotAddCanvas"); - addCanvasButton->setDefault(false); - addCanvasButton->setAutoDefault(false); - addCanvasButton->setToolTip("Add a new canvas"); - QGraphicsDropShadowEffect *addCanvasShadow = - new QGraphicsDropShadowEffect(); - addCanvasShadow->setBlurRadius(8); - addCanvasShadow->setOffset(0, 0); - addCanvasButton->setGraphicsEffect(addCanvasShadow); - connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); - - QHBoxLayout *addButtonLayout = new QHBoxLayout; - addButtonLayout->addWidget(exportPlotButton); - addButtonLayout->addStretch(); - addButtonLayout->addWidget(saveCanvasButton); - addButtonLayout->addWidget(loadCanvasButton); - addButtonLayout->addWidget(addCanvasButton); - addButtonLayout->setAlignment(Qt::AlignRight | Qt::AlignBottom); - addButtonLayout->setContentsMargins(0, 0, 0, 0); - addCanvasButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); - - // Plot layout - QVBoxLayout *plotLayout = new QVBoxLayout; - plotLayout->addWidget(this->dataPtr->canvasSplitter); - plotLayout->addLayout(addButtonLayout); - plotLayout->setStretchFactor(this->dataPtr->canvasSplitter, 1); - plotLayout->setStretchFactor(addButtonLayout, 0); - - auto plotFrame = new QFrame; - plotFrame->setLayout(plotLayout); - - // Palette - auto plotPalette = new Palette(this); - - auto splitter = new QSplitter(Qt::Horizontal, this); - splitter->addWidget(plotPalette); - splitter->addWidget(plotFrame); - splitter->setCollapsible(0, true); - splitter->setCollapsible(1, false); - - QList sizes; - sizes << 30 << 70; - splitter->setSizes(sizes); - - auto mainLayout = new QHBoxLayout; - mainLayout->addWidget(splitter); - mainLayout->setContentsMargins(0, 0, 0, 0); - - this->setLayout(mainLayout); - - QShortcut *space = new QShortcut(Qt::Key_Space, this); - QObject::connect(space, SIGNAL(activated()), this, SLOT(TogglePause())); - - QTimer *displayTimer = new QTimer(this); - connect(displayTimer, SIGNAL(timeout()), this, SLOT(Update())); - displayTimer->start(30); - - PlotManager::Instance()->AddWindow(this); - - this->setMinimumSize(640, 480); +PlotWindow::PlotWindow(QWidget *_parent) + : QWidget(_parent), + dataPtr(new PlotWindowPrivate()) +{ + this->setWindowIcon(QIcon(":/images/gazebo.svg")); + this->setWindowTitle("Gazebo: Plotting Utility"); + this->setObjectName("plotWindow"); + this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint | + Qt::CustomizeWindowHint); + + // new empty canvas + this->dataPtr->canvasSplitter = new QSplitter(Qt::Vertical); + this->AddCanvas(); + + // add button + QPushButton *addCanvasButton = new QPushButton("+"); + addCanvasButton->setObjectName("plotAddCanvas"); + addCanvasButton->setDefault(false); + addCanvasButton->setAutoDefault(false); + addCanvasButton->setToolTip("Add a new canvas"); + QGraphicsDropShadowEffect *addCanvasShadow = new QGraphicsDropShadowEffect(); + addCanvasShadow->setBlurRadius(8); + addCanvasShadow->setOffset(0, 0); + addCanvasButton->setGraphicsEffect(addCanvasShadow); + connect(addCanvasButton, SIGNAL(clicked()), this, SLOT(OnAddCanvas())); + + // export button + QPushButton *exportPlotButton = new QPushButton("Export"); + exportPlotButton->setIcon(QIcon(":/images/file_upload.svg")); + exportPlotButton->setObjectName("plotExport"); + exportPlotButton->setDefault(false); + exportPlotButton->setAutoDefault(false); + exportPlotButton->setToolTip("Export plot data"); + QGraphicsDropShadowEffect *exportPlotShadow = new QGraphicsDropShadowEffect(); + exportPlotShadow->setBlurRadius(8); + exportPlotShadow->setOffset(0, 0); + exportPlotButton->setGraphicsEffect(exportPlotShadow); + connect(exportPlotButton, SIGNAL(clicked()), this, SLOT(OnExport())); + + // save button + QPushButton *saveCanvasButton = new QPushButton("Save"); + saveCanvasButton->setObjectName("plotSaveCanvas"); + saveCanvasButton->setDefault(false); + saveCanvasButton->setAutoDefault(false); + saveCanvasButton->setToolTip("Save canvas"); + QGraphicsDropShadowEffect *saveCanvasShadow = + new QGraphicsDropShadowEffect(); + saveCanvasShadow->setBlurRadius(8); + saveCanvasShadow->setOffset(0, 0); + saveCanvasButton->setGraphicsEffect(saveCanvasShadow); + connect(saveCanvasButton, SIGNAL(clicked()), this, SLOT(OnSaveCanvas())); + + // load button + QPushButton *loadCanvasButton = new QPushButton("Load"); + loadCanvasButton->setObjectName("plotLoadCanvas"); + loadCanvasButton->setDefault(false); + loadCanvasButton->setAutoDefault(false); + loadCanvasButton->setToolTip("Load canvas"); + QGraphicsDropShadowEffect *loadCanvasShadow = + new QGraphicsDropShadowEffect(); + loadCanvasShadow->setBlurRadius(8); + loadCanvasShadow->setOffset(0, 0); + loadCanvasButton->setGraphicsEffect(loadCanvasShadow); + connect(loadCanvasButton, SIGNAL(clicked()), this, SLOT(OnLoadCanvas())); + + QHBoxLayout *addButtonLayout = new QHBoxLayout; + addButtonLayout->addWidget(exportPlotButton); + addButtonLayout->addStretch(); + addButtonLayout->addWidget(saveCanvasButton); + addButtonLayout->addWidget(loadCanvasButton); + addButtonLayout->addWidget(addCanvasButton); + addButtonLayout->setAlignment(Qt::AlignRight | Qt::AlignBottom); + addButtonLayout->setContentsMargins(0, 0, 0, 0); + addCanvasButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + // Plot layout + QVBoxLayout *plotLayout = new QVBoxLayout; + plotLayout->addWidget(this->dataPtr->canvasSplitter); + plotLayout->addLayout(addButtonLayout); + plotLayout->setStretchFactor(this->dataPtr->canvasSplitter, 1); + plotLayout->setStretchFactor(addButtonLayout, 0); + + auto plotFrame = new QFrame; + plotFrame->setLayout(plotLayout); + + // Palette + auto plotPalette = new Palette(this); + + auto splitter = new QSplitter(Qt::Horizontal, this); + splitter->addWidget(plotPalette); + splitter->addWidget(plotFrame); + splitter->setCollapsible(0, true); + splitter->setCollapsible(1, false); + + QList sizes; + sizes << 30 << 70; + splitter->setSizes(sizes); + + auto mainLayout = new QHBoxLayout; + mainLayout->addWidget(splitter); + mainLayout->setContentsMargins(0, 0, 0, 0); + + this->setLayout(mainLayout); + + QShortcut *space = new QShortcut(Qt::Key_Space, this); + QObject::connect(space, SIGNAL(activated()), this, SLOT(TogglePause())); + + QTimer *displayTimer = new QTimer(this); + connect(displayTimer, SIGNAL(timeout()), this, SLOT(Update())); + displayTimer->start(30); + + PlotManager::Instance()->AddWindow(this); + + this->setMinimumSize(640, 480); } ///////////////////////////////////////////////// -PlotWindow::~PlotWindow() { - PlotManager::Instance()->RemoveWindow(this); - this->Clear(); +PlotWindow::~PlotWindow() +{ + PlotManager::Instance()->RemoveWindow(this); + this->Clear(); } ///////////////////////////////////////////////// -PlotCanvas* PlotWindow::AddCanvas() { - PlotCanvas *canvas = new PlotCanvas(this); - connect(canvas, SIGNAL(CanvasDeleted()), this, SLOT(OnRemoveCanvas())); +PlotCanvas *PlotWindow::AddCanvas() +{ + PlotCanvas *canvas = new PlotCanvas(this); + connect(canvas, SIGNAL(CanvasDeleted()), this, SLOT(OnRemoveCanvas())); - this->dataPtr->canvasSplitter->addWidget(canvas); + this->dataPtr->canvasSplitter->addWidget(canvas); - this->UpdateCanvas(); + this->UpdateCanvas(); - return canvas; + return canvas; } ///////////////////////////////////////////////// @@ -353,169 +356,197 @@ void PlotWindow::LoadPlotLayout() { } // void PlotWindow::LoadPlotLayout() { ///////////////////////////////////////////////// -void PlotWindow::RemoveCanvas(PlotCanvas *_canvas) { - int idx = this->dataPtr->canvasSplitter->indexOf(_canvas); - if (idx < 0) - return; - - _canvas->hide(); - _canvas->setParent(nullptr); - _canvas->deleteLater(); +void PlotWindow::RemoveCanvas(PlotCanvas *_canvas) +{ + int idx = this->dataPtr->canvasSplitter->indexOf(_canvas); + if (idx < 0) + return; + + _canvas->hide(); + _canvas->setParent(nullptr); + _canvas->deleteLater(); } ///////////////////////////////////////////////// -void PlotWindow::Clear() { - while (this->CanvasCount() > 0u) { - PlotCanvas *canvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(0)); - this->RemoveCanvas(canvas); - } +void PlotWindow::Clear() +{ + while (this->CanvasCount() > 0u) + { + PlotCanvas *canvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(0)); + this->RemoveCanvas(canvas); + } } ///////////////////////////////////////////////// -unsigned int PlotWindow::CanvasCount() const { - return static_cast(this->dataPtr->canvasSplitter->count()); +unsigned int PlotWindow::CanvasCount() const +{ + return static_cast(this->dataPtr->canvasSplitter->count()); } ///////////////////////////////////////////////// -void PlotWindow::OnAddCanvas() { - this->AddCanvas(); +void PlotWindow::OnAddCanvas() +{ + this->AddCanvas(); } ///////////////////////////////////////////////// -void PlotWindow::OnSaveCanvas() { - this->SavePlotLayout(); +void PlotWindow::OnSaveCanvas() +{ + this->SavePlotLayout(); } ///////////////////////////////////////////////// -void PlotWindow::OnLoadCanvas() { - this->LoadPlotLayout(); +void PlotWindow::OnLoadCanvas() +{ + this->LoadPlotLayout(); } ///////////////////////////////////////////////// -void PlotWindow::OnRemoveCanvas() { - PlotCanvas *canvas = qobject_cast(QObject::sender()); - if (!canvas) - return; - - this->RemoveCanvas(canvas); - - // add an empty canvas if the plot window is now empty - if (this->dataPtr->canvasSplitter->count() == 0) - this->AddCanvas(); - else { - this->UpdateCanvas(); - } +void PlotWindow::OnRemoveCanvas() +{ + PlotCanvas *canvas = qobject_cast(QObject::sender()); + if (!canvas) + return; + + this->RemoveCanvas(canvas); + + // add an empty canvas if the plot window is now empty + if (this->dataPtr->canvasSplitter->count() == 0) + this->AddCanvas(); + else + { + this->UpdateCanvas(); + } } ///////////////////////////////////////////////// -void PlotWindow::UpdateCanvas() { - // disable Delete Canvas option in settings if there is only one - // canvas in the window - PlotCanvas *plotCanvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(0)); - if (plotCanvas) { - plotCanvas->SetDeleteCanvasEnabled( - this->dataPtr->canvasSplitter->count() != 1); - } +void PlotWindow::UpdateCanvas() +{ + // disable Delete Canvas option in settings if there is only one + // canvas in the window + PlotCanvas *plotCanvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(0)); + if (plotCanvas) + { + plotCanvas->SetDeleteCanvasEnabled( + this->dataPtr->canvasSplitter->count() != 1); + } } ///////////////////////////////////////////////// -void PlotWindow::Update() { - std::lock_guard lock(this->dataPtr->mutex); - if (this->dataPtr->restart) { - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - canvas->Restart(); - } - this->dataPtr->restart = false; - } - - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - canvas->Update(); - } +void PlotWindow::Update() +{ + std::lock_guard lock(this->dataPtr->mutex); + if (this->dataPtr->restart) + { + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) + { + PlotCanvas *canvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + if (!canvas) + continue; + canvas->Restart(); + } + this->dataPtr->restart = false; + } + + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) + { + PlotCanvas *canvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + if (!canvas) + continue; + canvas->Update(); + } } ///////////////////////////////////////////////// -void PlotWindow::Restart() { - std::lock_guard lock(this->dataPtr->mutex); - this->dataPtr->restart = true; +void PlotWindow::Restart() +{ + std::lock_guard lock(this->dataPtr->mutex); + this->dataPtr->restart = true; } ///////////////////////////////////////////////// -void PlotWindow::TogglePause() { - MainWindow *mainWindow = gui::get_main_window(); - if (!mainWindow) - return; - - if (mainWindow->IsPaused()) - mainWindow->Play(); - else - mainWindow->Pause(); +void PlotWindow::TogglePause() +{ + MainWindow *mainWindow = gui::get_main_window(); + if (!mainWindow) + return; + + if (mainWindow->IsPaused()) + mainWindow->Play(); + else + mainWindow->Pause(); } ///////////////////////////////////////////////// -void PlotWindow::OnExport() { - // Get the plots that have data. - std::list plots; - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - bool hasData = false; - PlotCanvas *canvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(i)); - - if (!canvas) - continue; - - for (const auto &plot : canvas->Plots()) { - for (const auto &curve : plot->Curves()) { - auto c = curve.lock(); - if (!c) - continue; - - hasData = hasData || c->Size() > 0; - } - } - - if (hasData) - plots.push_back(canvas); - } - - // Display an error message if no plots have data. - if (plots.empty()) { - QMessageBox msgBox(QMessageBox::Information, - QString("Unable to export"), - QString( - "No data to export.\nAdd variables with data to a graph first."), - QMessageBox::Close, this, - Qt::Window | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint - | Qt::CustomizeWindowHint); - msgBox.exec(); - } else { - ExportDialog *dialog = new ExportDialog(this, plots); - dialog->setModal(true); - dialog->show(); - } +void PlotWindow::OnExport() +{ + // Get the plots that have data. + std::list plots; + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) + { + bool hasData = false; + PlotCanvas *canvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(i)); + + if (!canvas) + continue; + + for (const auto &plot : canvas->Plots()) + { + for (const auto &curve : plot->Curves()) + { + auto c = curve.lock(); + if (!c) + continue; + + hasData = hasData || c->Size() > 0; + } + } + + if (hasData) + plots.push_back(canvas); + } + + // Display an error message if no plots have data. + if (plots.empty()) + { + QMessageBox msgBox( + QMessageBox::Information, + QString("Unable to export"), + QString( + "No data to export.\nAdd variables with data to a graph first."), + QMessageBox::Close, + this, + Qt::Window | Qt::WindowTitleHint | + Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint); + msgBox.exec(); + } + else + { + ExportDialog *dialog = new ExportDialog(this, plots); + dialog->setModal(true); + dialog->show(); + } } ///////////////////////////////////////////////// -std::list PlotWindow::Plots() { - std::list plots; +std::list PlotWindow::Plots() +{ + std::list plots; - for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) { - PlotCanvas *canvas = qobject_cast( - this->dataPtr->canvasSplitter->widget(i)); + for (int i = 0; i < this->dataPtr->canvasSplitter->count(); ++i) + { + PlotCanvas *canvas = + qobject_cast(this->dataPtr->canvasSplitter->widget(i)); - if (!canvas) - continue; - plots.push_back(canvas); - } + if (!canvas) + continue; + plots.push_back(canvas); + } - return plots; + return plots; } diff --git a/gazebo/gui/plot/VariablePillContainer.cc b/gazebo/gui/plot/VariablePillContainer.cc index 09ecaa98c8..fe75aa0bdc 100644 --- a/gazebo/gui/plot/VariablePillContainer.cc +++ b/gazebo/gui/plot/VariablePillContainer.cc @@ -26,463 +26,536 @@ using namespace gazebo; using namespace gui; -namespace gazebo { -namespace gui { -/// \internal -/// \brief VariablePillContainer private data -class VariablePillContainerPrivate { - /// \brief Text label -public: - QLabel *label; - - /// \brief Layout that contains all the variable pills. -public: - QLayout *variableLayout; - - /// \brief Variables inside this container -public: - std::map variables; - - /// \brief Container size. -public: - int maxSize = -1; - - /// \brief Pointer to variable pill that is currently selected. -public: - VariablePill *selectedVariable = nullptr; -}; -} +namespace gazebo +{ + namespace gui + { + /// \internal + /// \brief VariablePillContainer private data + class VariablePillContainerPrivate + { + /// \brief Text label + public: QLabel *label; + + /// \brief Layout that contains all the variable pills. + public: QLayout *variableLayout; + + /// \brief Variables inside this container + public: std::map variables; + + /// \brief Container size. + public: int maxSize = -1; + + /// \brief Pointer to variable pill that is currently selected. + public: VariablePill *selectedVariable = nullptr; + }; + } } ///////////////////////////////////////////////// -VariablePillContainer::VariablePillContainer(QWidget *_parent) : - QWidget(_parent), dataPtr(new VariablePillContainerPrivate) { - // label - this->dataPtr->label = new QLabel; - QHBoxLayout *labelLayout = new QHBoxLayout; - labelLayout->addWidget(this->dataPtr->label); - QMargins labelMargins = labelLayout->contentsMargins(); - labelMargins.setLeft(labelMargins.left() + 10); - labelLayout->setContentsMargins(labelMargins); - - // variable pills - this->dataPtr->variableLayout = new QHBoxLayout; - this->dataPtr->variableLayout->setAlignment(Qt::AlignLeft); - - QHBoxLayout *frameLayout = new QHBoxLayout; - frameLayout->addLayout(labelLayout); - frameLayout->addLayout(this->dataPtr->variableLayout); - frameLayout->setAlignment(Qt::AlignLeft); - frameLayout->setContentsMargins(8, 4, 8, 4); - QFrame *mainFrame = new QFrame; - mainFrame->setObjectName("variableContainerFrame"); - mainFrame->setLayout(frameLayout); - mainFrame->setFrameShape(QFrame::NoFrame); - mainFrame->setContentsMargins(0, 0, 0, 0); - - QHBoxLayout *mainLayout = new QHBoxLayout; - mainLayout->addWidget(mainFrame); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(0); - - this->setLayout(mainLayout); - this->setAcceptDrops(true); - - /*QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); - shadow->setBlurRadius(1); - shadow->setOffset(2, 2); - this->setGraphicsEffect(shadow); - */ +VariablePillContainer::VariablePillContainer(QWidget *_parent) + : QWidget(_parent), + dataPtr(new VariablePillContainerPrivate) +{ + // label + this->dataPtr->label = new QLabel; + QHBoxLayout *labelLayout = new QHBoxLayout; + labelLayout->addWidget(this->dataPtr->label); + QMargins labelMargins = labelLayout->contentsMargins(); + labelMargins.setLeft(labelMargins.left() + 10); + labelLayout->setContentsMargins(labelMargins); + + // variable pills + this->dataPtr->variableLayout = new QHBoxLayout; + this->dataPtr->variableLayout->setAlignment(Qt::AlignLeft); + + QHBoxLayout *frameLayout = new QHBoxLayout; + frameLayout->addLayout(labelLayout); + frameLayout->addLayout(this->dataPtr->variableLayout); + frameLayout->setAlignment(Qt::AlignLeft); + frameLayout->setContentsMargins(8, 4, 8, 4); + QFrame *mainFrame = new QFrame; + mainFrame->setObjectName("variableContainerFrame"); + mainFrame->setLayout(frameLayout); + mainFrame->setFrameShape(QFrame::NoFrame); + mainFrame->setContentsMargins(0, 0, 0, 0); + + QHBoxLayout *mainLayout = new QHBoxLayout; + mainLayout->addWidget(mainFrame); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + this->setLayout(mainLayout); + this->setAcceptDrops(true); + + /*QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(); + shadow->setBlurRadius(1); + shadow->setOffset(2, 2); + this->setGraphicsEffect(shadow); + */ } ///////////////////////////////////////////////// -VariablePillContainer::~VariablePillContainer() { +VariablePillContainer::~VariablePillContainer() +{ } ///////////////////////////////////////////////// -void VariablePillContainer::SetText(const std::string &_text) { - this->dataPtr->label->setText(QString::fromStdString(_text)); +void VariablePillContainer::SetText(const std::string &_text) +{ + this->dataPtr->label->setText(QString::fromStdString(_text)); } ///////////////////////////////////////////////// -std::string VariablePillContainer::Text() const { - return this->dataPtr->label->text().toStdString(); +std::string VariablePillContainer::Text() const +{ + return this->dataPtr->label->text().toStdString(); } ///////////////////////////////////////////////// void VariablePillContainer::SetVariablePillLabel(const unsigned int _id, - const std::string &_label) { - VariablePill *variable = nullptr; - auto it = this->dataPtr->variables.find(_id); - if (it == this->dataPtr->variables.end()) { - // look into children of multi-variable pills - for (auto v : this->dataPtr->variables) { - auto &childVariables = v.second->VariablePills(); - auto childIt = childVariables.find(_id); - if (childIt != childVariables.end()) - variable = childIt->second; - } - } else - variable = it->second; - - if (variable) - variable->SetText(_label); + const std::string &_label) +{ + VariablePill *variable = nullptr; + auto it = this->dataPtr->variables.find(_id); + if (it == this->dataPtr->variables.end()) + { + // look into children of multi-variable pills + for (auto v : this->dataPtr->variables) + { + auto &childVariables = v.second->VariablePills(); + auto childIt = childVariables.find(_id); + if (childIt != childVariables.end()) + variable = childIt->second; + } + } + else + variable = it->second; + + if (variable) + variable->SetText(_label); } ///////////////////////////////////////////////// unsigned int VariablePillContainer::AddVariablePill(const std::string &_name, - const unsigned int _targetId) { - if (this->dataPtr->maxSize != -1 - && static_cast(this->VariablePillCount()) - >= this->dataPtr->maxSize) { - return VariablePill::EmptyVariable; - } - - if (_targetId != VariablePill::EmptyVariable - && !this->GetVariablePill(_targetId)) { - gzerr << "Unable to add variable. Target variable not found" - << std::endl; - return VariablePill::EmptyVariable; - } - - VariablePill *variable = new VariablePill; - variable->SetName(_name); - variable->SetText(_name); - - connect(variable, SIGNAL(VariableMoved(unsigned int)), this, - SLOT(OnMoveVariable(unsigned int))); - connect(variable, SIGNAL(VariableAdded(unsigned int, std::string)), this, - SLOT(OnAddVariable(unsigned int, std::string))); - connect(variable, SIGNAL(VariableRemoved(unsigned int)), this, - SLOT(OnRemoveVariable(unsigned int))); - connect(variable, SIGNAL(VariableLabelChanged(std::string)), this, - SLOT(OnSetVariableLabel(std::string))); - - this->AddVariablePill(variable, _targetId); - - return variable->Id(); + const unsigned int _targetId) +{ + if (this->dataPtr->maxSize != -1 && + static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) + { + return VariablePill::EmptyVariable; + } + + if (_targetId != VariablePill::EmptyVariable && + !this->GetVariablePill(_targetId)) + { + gzerr << "Unable to add variable. Target variable not found" << std::endl; + return VariablePill::EmptyVariable; + } + + VariablePill *variable = new VariablePill; + variable->SetName(_name); + variable->SetText(_name); + + connect(variable, SIGNAL(VariableMoved(unsigned int)), + this, SLOT(OnMoveVariable(unsigned int))); + connect(variable, SIGNAL(VariableAdded(unsigned int, std::string)), + this, SLOT(OnAddVariable(unsigned int, std::string))); + connect(variable, SIGNAL(VariableRemoved(unsigned int)), + this, SLOT(OnRemoveVariable(unsigned int))); + connect(variable, SIGNAL(VariableLabelChanged(std::string)), + this, SLOT(OnSetVariableLabel(std::string))); + + this->AddVariablePill(variable, _targetId); + + return variable->Id(); } ///////////////////////////////////////////////// void VariablePillContainer::AddVariablePill(VariablePill *_variable, - const unsigned int _targetId) { - if (!_variable) - return; - - // add to target variable if it's not empty - if (_targetId != VariablePill::EmptyVariable) { - VariablePill *targetVariable = this->GetVariablePill(_targetId); - if (!targetVariable) - return; - - targetVariable->AddVariablePill(_variable); - return; - } else { - // check if variable already exists in this container - // check only top level variables - if (this->dataPtr->variables.find(_variable->Id()) - != this->dataPtr->variables.end()) { - return; - } - } - - // otherwise add to the container - if (this->dataPtr->maxSize != -1 - && static_cast(this->VariablePillCount()) - >= this->dataPtr->maxSize) { - gzerr << "Unable to add variable to container. Container is full" - << std::endl; - return; - } - - _variable->SetContainer(this); - _variable->setVisible(true); - this->dataPtr->variableLayout->addWidget(_variable); - this->dataPtr->variables[_variable->Id()] = _variable; - - emit VariableAdded(_variable->Id(), _variable->Text(), _targetId); + const unsigned int _targetId) +{ + if (!_variable) + return; + + // add to target variable if it's not empty + if (_targetId != VariablePill::EmptyVariable) + { + VariablePill *targetVariable = this->GetVariablePill(_targetId); + if (!targetVariable) + return; + + targetVariable->AddVariablePill(_variable); + return; + } + else + { + // check if variable already exists in this container + // check only top level variables + if (this->dataPtr->variables.find(_variable->Id()) != + this->dataPtr->variables.end()) + { + return; + } + } + + // otherwise add to the container + if (this->dataPtr->maxSize != -1 && + static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) + { + gzerr << "Unable to add variable to container. Container is full" << + std::endl; + return; + } + + _variable->SetContainer(this); + _variable->setVisible(true); + this->dataPtr->variableLayout->addWidget(_variable); + this->dataPtr->variables[_variable->Id()] = _variable; + + emit VariableAdded(_variable->Id(), _variable->Text(), _targetId); } ///////////////////////////////////////////////// -void VariablePillContainer::SetMaxSize(const int _max) { - this->dataPtr->maxSize = _max; +void VariablePillContainer::SetMaxSize(const int _max) +{ + this->dataPtr->maxSize = _max; } ///////////////////////////////////////////////// -int VariablePillContainer::MaxSize() const { - return this->dataPtr->maxSize; +int VariablePillContainer::MaxSize() const +{ + return this->dataPtr->maxSize; } ///////////////////////////////////////////////// -void VariablePillContainer::RemoveVariablePill(const unsigned int _id) { - VariablePill *variable = nullptr; - auto it = this->dataPtr->variables.find(_id); - if (it == this->dataPtr->variables.end()) { - // look into children of multi-variable pills - for (auto v : this->dataPtr->variables) { - auto &childVariables = v.second->VariablePills(); - auto childIt = childVariables.find(_id); - if (childIt != childVariables.end()) { - variable = childIt->second; - // remove from parent - if (variable->Parent()) - variable->Parent()->RemoveVariablePill(variable); - return; - } - } - } else - variable = it->second; - - if (!variable) - return; - - int idx = this->dataPtr->variableLayout->indexOf(variable); - if (idx != -1) { - this->dataPtr->variableLayout->takeAt(idx); - this->dataPtr->variables.erase(variable->Id()); - // remove from parent if any - if (variable->Parent()) { - // remove and rely on callbacks to emit the VariableRemoved signal - variable->Parent()->RemoveVariablePill(variable); - } else - emit VariableRemoved(variable->Id(), VariablePill::EmptyVariable); - } - - // otherwise remove from container - variable->SetContainer(nullptr); - variable->setVisible(false); +void VariablePillContainer::RemoveVariablePill(const unsigned int _id) +{ + VariablePill *variable = nullptr; + auto it = this->dataPtr->variables.find(_id); + if (it == this->dataPtr->variables.end()) + { + // look into children of multi-variable pills + for (auto v : this->dataPtr->variables) + { + auto &childVariables = v.second->VariablePills(); + auto childIt = childVariables.find(_id); + if (childIt != childVariables.end()) + { + variable = childIt->second; + // remove from parent + if (variable->Parent()) + variable->Parent()->RemoveVariablePill(variable); + return; + } + } + } + else + variable = it->second; + + if (!variable) + return; + + int idx = this->dataPtr->variableLayout->indexOf(variable); + if (idx != -1) + { + this->dataPtr->variableLayout->takeAt(idx); + this->dataPtr->variables.erase(variable->Id()); + // remove from parent if any + if (variable->Parent()) + { + // remove and rely on callbacks to emit the VariableRemoved signal + variable->Parent()->RemoveVariablePill(variable); + } + else + emit VariableRemoved(variable->Id(), VariablePill::EmptyVariable); + } + + // otherwise remove from container + variable->SetContainer(nullptr); + variable->setVisible(false); } ///////////////////////////////////////////////// -void VariablePillContainer::RemoveVariablePill(VariablePill *_variable) { - if (!_variable) - return; +void VariablePillContainer::RemoveVariablePill(VariablePill *_variable) +{ + if (!_variable) + return; - this->RemoveVariablePill(_variable->Id()); + this->RemoveVariablePill(_variable->Id()); } ///////////////////////////////////////////////// -unsigned int VariablePillContainer::VariablePillCount() const { - unsigned int count = 0; - for (const auto v : this->dataPtr->variables) { - count++; - count += v.second->VariablePillCount(); - } - return count; +unsigned int VariablePillContainer::VariablePillCount() const +{ + unsigned int count = 0; + for (const auto v : this->dataPtr->variables) + { + count++; + count += v.second->VariablePillCount(); + } + return count; } ///////////////////////////////////////////////// -VariablePill* VariablePillContainer::GetVariablePill( - const unsigned int _id) const { - for (const auto &v : this->dataPtr->variables) { - if (v.first == _id) - return v.second; - - for (const auto &child : v.second->VariablePills()) { - if (child.first == _id) - return child.second; - } - } - return nullptr; +VariablePill *VariablePillContainer::GetVariablePill( + const unsigned int _id) const +{ + for (const auto &v : this->dataPtr->variables) + { + if (v.first == _id) + return v.second; + + for (const auto &child : v.second->VariablePills()) + { + if (child.first == _id) + return child.second; + } + } + return nullptr; } ///////////////////////////////////////////////// -void VariablePillContainer::SetSelected(VariablePill *_variable) { - if (this->dataPtr->selectedVariable) - this->dataPtr->selectedVariable->SetSelected(false); +void VariablePillContainer::SetSelected(VariablePill *_variable) +{ + if (this->dataPtr->selectedVariable) + this->dataPtr->selectedVariable->SetSelected(false); - this->dataPtr->selectedVariable = _variable; + this->dataPtr->selectedVariable = _variable; - if (this->dataPtr->selectedVariable) - this->dataPtr->selectedVariable->SetSelected(true); + if (this->dataPtr->selectedVariable) + this->dataPtr->selectedVariable->SetSelected(true); } ///////////////////////////////////////////////// -void VariablePillContainer::dragEnterEvent(QDragEnterEvent *_evt) { - if (!this->IsDragValid(_evt)) { - _evt->ignore(); - return; - } - - if (_evt->mimeData()->hasFormat("application/x-item")) { - _evt->setDropAction(Qt::LinkAction); - } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { - _evt->setDropAction(Qt::MoveAction); - } else { - _evt->ignore(); - return; - } - - _evt->acceptProposedAction(); +void VariablePillContainer::dragEnterEvent(QDragEnterEvent *_evt) +{ + if (!this->IsDragValid(_evt)) + { + _evt->ignore(); + return; + } + + if (_evt->mimeData()->hasFormat("application/x-item")) + { + _evt->setDropAction(Qt::LinkAction); + } + else if (_evt->mimeData()->hasFormat("application/x-pill-item")) + { + _evt->setDropAction(Qt::MoveAction); + } + else + { + _evt->ignore(); + return; + } + + _evt->acceptProposedAction(); } ///////////////////////////////////////////////// -void VariablePillContainer::dropEvent(QDropEvent *_evt) { - if (!this->IsDragValid(_evt)) { - _evt->ignore(); - return; - } - - if (_evt->mimeData()->hasFormat("application/x-item")) { - QString mimeData = _evt->mimeData()->data("application/x-item"); - std::string dataStr = mimeData.toStdString(); - this->AddVariablePill(dataStr); - } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { - VariablePill *variable = qobject_cast(_evt->source()); - if (!variable) { - gzerr << "Variable is nullptr" << std::endl; - return; - } - - VariablePillContainer *container = variable->Container(); - - // moved to the same container - no op - if (!variable->Parent() && (container && container == this)) - return; - - // block signals and emit VariableMoved instead. - VariablePill *parentVariable = variable->Parent(); - if (parentVariable) { - parentVariable->blockSignals(true); - parentVariable->RemoveVariablePill(variable); - parentVariable->blockSignals(false); - } else { - if (container) { - container->blockSignals(true); - container->RemoveVariablePill(variable); - container->blockSignals(false); - } - } - - // case when the variable is dragged out from a muli-variable pill to - // the container - this->blockSignals(true); - this->AddVariablePill(variable); - this->blockSignals(false); - - emit VariableMoved(variable->Id(), VariablePill::EmptyVariable); - } +void VariablePillContainer::dropEvent(QDropEvent *_evt) +{ + if (!this->IsDragValid(_evt)) + { + _evt->ignore(); + return; + } + + if (_evt->mimeData()->hasFormat("application/x-item")) + { + QString mimeData = _evt->mimeData()->data("application/x-item"); + std::string dataStr = mimeData.toStdString(); + this->AddVariablePill(dataStr); + } + else if (_evt->mimeData()->hasFormat("application/x-pill-item")) + { + VariablePill *variable = qobject_cast(_evt->source()); + if (!variable) + { + gzerr << "Variable is nullptr" << std::endl; + return; + } + + VariablePillContainer *container = variable->Container(); + + // moved to the same container - no op + if (!variable->Parent() && (container && container == this)) + return; + + // block signals and emit VariableMoved instead. + VariablePill *parentVariable = variable->Parent(); + if (parentVariable) + { + parentVariable->blockSignals(true); + parentVariable->RemoveVariablePill(variable); + parentVariable->blockSignals(false); + } + else + { + if (container) + { + container->blockSignals(true); + container->RemoveVariablePill(variable); + container->blockSignals(false); + } + } + + // case when the variable is dragged out from a muli-variable pill to + // the container + this->blockSignals(true); + this->AddVariablePill(variable); + this->blockSignals(false); + + emit VariableMoved(variable->Id(), VariablePill::EmptyVariable); + } } ///////////////////////////////////////////////// -bool VariablePillContainer::IsDragValid(QDropEvent *_evt) const { - if (this->dataPtr->maxSize != -1 - && static_cast(this->VariablePillCount()) - >= this->dataPtr->maxSize) { - return false; - } - - std::string dataStr; - if (_evt->mimeData()->hasFormat("application/x-item")) { - QString mimeData = _evt->mimeData()->data("application/x-item"); - dataStr = mimeData.toStdString(); - } else if (_evt->mimeData()->hasFormat("application/x-pill-item")) { - QString mimeData = _evt->mimeData()->data("application/x-pill-item"); - dataStr = mimeData.toStdString(); - - VariablePill *dragVariable = qobject_cast( - _evt->source()); - if (!dragVariable) - return false; - - // limit drag and drop to same container - if (dragVariable->Container() && dragVariable->Container() != this) - return false; - } else - return false; - - if (dataStr.empty()) - return false; - - return true; +bool VariablePillContainer::IsDragValid(QDropEvent *_evt) const +{ + if (this->dataPtr->maxSize != -1 && + static_cast(this->VariablePillCount()) >= this->dataPtr->maxSize) + { + return false; + } + + std::string dataStr; + if (_evt->mimeData()->hasFormat("application/x-item")) + { + QString mimeData = _evt->mimeData()->data("application/x-item"); + dataStr = mimeData.toStdString(); + } + else if (_evt->mimeData()->hasFormat("application/x-pill-item")) + { + QString mimeData = _evt->mimeData()->data("application/x-pill-item"); + dataStr = mimeData.toStdString(); + + VariablePill *dragVariable = qobject_cast(_evt->source()); + if (!dragVariable) + return false; + + // limit drag and drop to same container + if (dragVariable->Container() && dragVariable->Container() != this) + return false; + } + else + return false; + + if (dataStr.empty()) + return false; + + return true; } ///////////////////////////////////////////////// -void VariablePillContainer::keyPressEvent(QKeyEvent *_event) { - if (_event->key() == Qt::Key_Delete) { - if (this->dataPtr->selectedVariable) { - this->RemoveVariablePill(this->dataPtr->selectedVariable); - this->dataPtr->selectedVariable = nullptr; - } - } +void VariablePillContainer::keyPressEvent(QKeyEvent *_event) +{ + if (_event->key() == Qt::Key_Delete) + { + if (this->dataPtr->selectedVariable) + { + this->RemoveVariablePill(this->dataPtr->selectedVariable); + this->dataPtr->selectedVariable = nullptr; + } + } } ///////////////////////////////////////////////// -void VariablePillContainer::mouseReleaseEvent(QMouseEvent *_event) { - this->SetSelected(nullptr); - - bool selected = false; - for (const auto v : this->dataPtr->variables) { - // look for the selected variable widget if not already found - QPoint point = v.second->mapFromParent(_event->pos()); - if (!selected) { - ignition::math::Vector2i pt(point.x(), point.y()); - if (v.second->ContainsPoint(pt)) { - this->SetSelected(v.second); - this->setFocus(); - selected = true; - } else { - v.second->SetSelected(false); - } - } else { - v.second->SetSelected(false); - } - - // loop through children of multi-variable pills - for (const auto cv : v.second->VariablePills()) { - VariablePill *child = cv.second; - - if (!selected) { - QPoint childPoint = child->mapFromParent(point); - ignition::math::Vector2i childPt(childPoint.x(), - childPoint.y()); - if (child->ContainsPoint(childPt)) { - this->SetSelected(child); - this->setFocus(); - selected = true; - } else { - child->SetSelected(false); - } - } else { - child->SetSelected(false); - } - } - } +void VariablePillContainer::mouseReleaseEvent(QMouseEvent *_event) +{ + this->SetSelected(nullptr); + + bool selected = false; + for (const auto v : this->dataPtr->variables) + { + // look for the selected variable widget if not already found + QPoint point = v.second->mapFromParent(_event->pos()); + if (!selected) + { + ignition::math::Vector2i pt(point.x(), point.y()); + if (v.second->ContainsPoint(pt)) + { + this->SetSelected(v.second); + this->setFocus(); + selected = true; + } + else + { + v.second->SetSelected(false); + } + } + else + { + v.second->SetSelected(false); + } + + + // loop through children of multi-variable pills + for (const auto cv : v.second->VariablePills()) + { + VariablePill *child = cv.second; + + if (!selected) + { + QPoint childPoint = child->mapFromParent(point); + ignition::math::Vector2i childPt(childPoint.x(), childPoint.y()); + if (child->ContainsPoint(childPt)) + { + this->SetSelected(child); + this->setFocus(); + selected = true; + } + else + { + child->SetSelected(false); + } + } + else + { + child->SetSelected(false); + } + } + } } ///////////////////////////////////////////////// -void VariablePillContainer::OnMoveVariable(const unsigned int _id) { - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnMoveVariable(const unsigned int _id) +{ + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableMoved(_id, variable->Id()); + emit VariableMoved(_id, variable->Id()); } ///////////////////////////////////////////////// void VariablePillContainer::OnAddVariable(const unsigned int _id, - const std::string &_label) { - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; + const std::string &_label) +{ + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableAdded(_id, _label, variable->Id()); + emit VariableAdded(_id, _label, variable->Id()); } ///////////////////////////////////////////////// -void VariablePillContainer::OnRemoveVariable(const unsigned int _id) { - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnRemoveVariable(const unsigned int _id) +{ + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableRemoved(_id, variable->Id()); + emit VariableRemoved(_id, variable->Id()); } ///////////////////////////////////////////////// -void VariablePillContainer::OnSetVariableLabel(const std::string &_label) { - VariablePill *variable = qobject_cast(QObject::sender()); - if (!variable) - return; +void VariablePillContainer::OnSetVariableLabel(const std::string &_label) +{ + VariablePill *variable = qobject_cast(QObject::sender()); + if (!variable) + return; - emit VariableLabelChanged(variable->Id(), _label); + emit VariableLabelChanged(variable->Id(), _label); }