diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp index bfe6a2b3ceb3bf52788cf009dd97ff40601d16a9..adff35eac5ca796021a7f323381a88725f0731a6 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp @@ -71,9 +71,9 @@ namespace armarx::armem::gui updateWidget = new armem::gui::PeriodicUpdateWidget(2.0, 60); updateWidgetLayout->insertWidget(0, updateWidget); - processQueryResultTimer = new QTimer(this); - processQueryResultTimer->setInterval(1000 / 60); // Keep this stable. - processQueryResultTimer->start(); + periodicUpdateTimer = new QTimer(this); + periodicUpdateTimer->setInterval(1000 / 60); // Keep this stable. + periodicUpdateTimer->start(); // Memory View auto retrieveEntityInfo = [this](const MemoryID& entityID) -> PredictionWidget::EntityInfo @@ -149,7 +149,9 @@ namespace armarx::armem::gui connect(this, &This::connected, this, &This::startQueries); connect(updateWidget, &armem::gui::PeriodicUpdateWidget::update, this, &This::startQueries); - connect(processQueryResultTimer, &QTimer::timeout, this, &This::processQueryResults); + + connect(periodicUpdateTimer, &QTimer::timeout, this, &This::updateAvailableMemories); + connect(periodicUpdateTimer, &QTimer::timeout, this, &This::processQueryResults); connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::storeInLTM, this, &This::storeInLTM); connect(memoryGroup->predictionWidget(), @@ -418,8 +420,15 @@ namespace armarx::armem::gui // Can't use a structured binding here because you can't capture those in a lambda // according to the C++ standard. + auto enabledMemories = memoryGroup->queryWidget()->enabledMemories(); for (const auto& pair : readers) { + // skip if memory should not be queried + if (std::find(enabledMemories.begin(), enabledMemories.end(), pair.first) == enabledMemories.end()) + { + continue; + } + const auto& name = pair.first; const auto& reader = pair.second; if (queries.count(name) == 0) @@ -516,16 +525,19 @@ namespace armarx::armem::gui } // Drop all entries in memoryData which are not in memoryReaders anymore. - for (auto it = memoryData.begin(); it != memoryData.end();) + if (memoryGroup->queryWidget()->dropRemovedMemories()) { - auto name = it->second.name(); - if (memoryReaders.count(it->first) == 0) - { - it = memoryData.erase(it); - } - else + for (auto it = memoryData.begin(); it != memoryData.end();) { - ++it; + auto name = it->second.name(); + if (memoryReaders.count(it->first) == 0) + { + it = memoryData.erase(it); + } + else + { + ++it; + } } } @@ -665,6 +677,26 @@ namespace armarx::armem::gui } + void + MemoryViewer::updateAvailableMemories() + { + if (mns) + { + std::vector<std::string> convVec; + memoryReaders = mns.getAllReaders(true); // we only need the readers + std::transform(memoryReaders.begin(), memoryReaders.end(), std::back_inserter(convVec), [](const auto& p){return p.first;}); + + TIMING_START(GuiUpdateAvailableMemories) + memoryGroup->queryWidget()->update(convVec); + TIMING_END_STREAM(GuiUpdateAvailableMemories, ARMARX_VERBOSE) + } + else + { + ARMARX_VERBOSE << deactivateSpam() << "MNS not ready yet. Skip update of available memories in query widget."; + } + } + + void MemoryViewer::updateMemoryTree() { @@ -674,9 +706,9 @@ namespace armarx::armem::gui convMap[name] = &data; } - TIMING_START(GuiUpdate) + TIMING_START(GuiUpdateMemoryTree) memoryGroup->tree()->update(convMap); - TIMING_END_STREAM(GuiUpdate, ARMARX_VERBOSE) + TIMING_END_STREAM(GuiUpdateMemoryTree, ARMARX_VERBOSE) if (debugObserver) { @@ -684,7 +716,7 @@ namespace armarx::armem::gui { debugObserver->setDebugDatafield(Logging::tag.tagName, "GUI Update [ms]", - new Variant(GuiUpdate.toMilliSecondsDouble())); + new Variant(GuiUpdateMemoryTree.toMilliSecondsDouble())); } catch (const Ice::Exception&) { diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h index 34562204b070f763084b6d0d0a2946b99b2b8dc0..88d18ad853be7054cc0a3b17bc69a64043438d5a 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h @@ -106,6 +106,7 @@ namespace armarx::armem::gui void processQueryResults(); void updateMemoryTree(); + void updateAvailableMemories(); signals: @@ -149,8 +150,9 @@ namespace armarx::armem::gui std::map<std::string, armem::wm::Memory> memoryData; std::map<std::string, std::future<armem::query::data::Result>> runningQueries; - /// Periodically triggers query result collection. - QTimer* processQueryResultTimer; + + /// Periodically triggers query result collection and updates the available memories + QTimer* periodicUpdateTimer; QLayout* updateWidgetLayout = nullptr; diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp index ad07dc5b0de991b1e3296a94c66418caa20bf011..ea2e0e729d0a719f0aade293684a3d6bfdea98b2 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp @@ -1,6 +1,7 @@ #include "QueryWidget.h" #include <limits> +#include <mutex> #include <QCheckBox> #include <QGroupBox> @@ -11,6 +12,7 @@ #include <QTabWidget> #include <QVBoxLayout> #include <QWidget> +#include <QScrollArea> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> @@ -21,38 +23,111 @@ namespace armarx::armem::gui QueryWidget::QueryWidget() { auto* vlayout = new QVBoxLayout(); + auto* hlayout = new QHBoxLayout(); - _dataCheckBox = new QCheckBox("Get Data"); - _dataCheckBox->setChecked(true); - vlayout->addWidget(_dataCheckBox); - - auto* recDepthHLayout = new QHBoxLayout(); - vlayout->addLayout(recDepthHLayout); - - _recursionDepthSpinner = new QSpinBox(); // NOLINT - _recursionDepthSpinner->setRange(-1, std::numeric_limits<int>::max()); - _recursionDepthSpinner->setValue(0); - _recursionDepthSpinner->setEnabled(_dataCheckBox->isChecked()); - _recursionDepthSpinner->setSpecialValueText(QString("Unlimited")); - recDepthHLayout->addWidget(_recursionDepthSpinner); + { + _availableMemoriesGroupBox = new QGroupBox("Available memories"); + auto vboxlayout = new QVBoxLayout(); + vboxlayout->setMargin(1); + vboxlayout->setSizeConstraint(QLayout::SizeConstraint::SetMinAndMaxSize); + vboxlayout->setAlignment(Qt::AlignTop); + _availableMemoriesGroupBox->setLayout(vboxlayout); + hlayout->addWidget(_availableMemoriesGroupBox); + } - _recursionDepthLabel = new QLabel("Link resolution depth"); - recDepthHLayout->addWidget(_recursionDepthLabel); - recDepthHLayout->addStretch(); + QFrame* vFrame = new QFrame; + vFrame->setFrameShape(QFrame::VLine); + hlayout->addWidget(vFrame); - _storeInLTMButton = new QPushButton("Store query result in LTM"); - vlayout->addWidget(_storeInLTMButton); + { + _additionalSettingsGroupBox = new QGroupBox("Additional query settings"); + auto vboxlayout = new QVBoxLayout(); + vboxlayout->setMargin(1); + vboxlayout->setSizeConstraint(QLayout::SizeConstraint::SetMinAndMaxSize); + vboxlayout->setAlignment(Qt::AlignTop); + _additionalSettingsGroupBox->setLayout(vboxlayout); + + _dataCheckBox = new QCheckBox("Get Data"); + _dataCheckBox->setChecked(true); + vboxlayout->addWidget(_dataCheckBox); + + _dropRemovedCheckBox = new QCheckBox("Drop removed memories"); + _dropRemovedCheckBox->setChecked(true); + vboxlayout->addWidget(_dropRemovedCheckBox); + + auto* recDepthHLayout = new QHBoxLayout(); + + _recursionDepthSpinner = new QSpinBox(); // NOLINT + _recursionDepthSpinner->setRange(-1, std::numeric_limits<int>::max()); + _recursionDepthSpinner->setValue(0); + _recursionDepthSpinner->setEnabled(_dataCheckBox->isChecked()); + _recursionDepthSpinner->setSpecialValueText(QString("Unlimited")); + recDepthHLayout->addWidget(_recursionDepthSpinner); + + _recursionDepthLabel = new QLabel("Link resolution depth"); + recDepthHLayout->addWidget(_recursionDepthLabel); + recDepthHLayout->addStretch(); + vboxlayout->addLayout(recDepthHLayout); + + _storeInLTMButton = new QPushButton("Store query result in LTM"); + vboxlayout->addWidget(_storeInLTMButton); + + hlayout->addWidget(_additionalSettingsGroupBox); + } + vlayout->addLayout(hlayout); // Public connections. connect(_storeInLTMButton, &QPushButton::pressed, this, &This::storeInLTM); // Private connections. - connect( - _dataCheckBox, &QCheckBox::stateChanged, this, &This::setRecursionDepthSpinnerEnabled); + connect(_dataCheckBox, &QCheckBox::stateChanged, this, &This::setRecursionDepthSpinnerEnabled); setLayout(vlayout); } + void QueryWidget::update(const std::vector<std::string>& activeMemoryNames) + { + // since this is the only method that adds or hides elements of the groupbox after initialization, + // we can use a static mutex, only available to this method + //static std::mutex m; + + //std::scoped_lock l(m); + std::vector<std::string> alreadyPresentMemoryCheckboxes; + + // get information about memories already present and activate inactive ones if necessary + int maxIndex = _availableMemoriesGroupBox->layout()->count(); + for (int i = 0; i < maxIndex; ++i) + { + auto w = _availableMemoriesGroupBox->layout()->itemAt(i)->widget(); + QCheckBox* box = static_cast<QCheckBox*>(w); + std::string memoryName = box->text().toStdString(); + if (box->isVisible() && std::find(activeMemoryNames.begin(), activeMemoryNames.end(), memoryName) == activeMemoryNames.end()) + { + // checkbox is visible but memory is not available anymore + box->setVisible(false); + box->setChecked(false); + } + if (not(box->isVisible()) && std::find(activeMemoryNames.begin(), activeMemoryNames.end(), memoryName) != activeMemoryNames.end()) + { + // checkbox is hidden but memory is not available again + box->setVisible(true); + box->setChecked(true); + } + alreadyPresentMemoryCheckboxes.push_back(memoryName); + } + + // Add checkboxes for new memories + for (const auto& memoryName : activeMemoryNames) + { + if (std::find(alreadyPresentMemoryCheckboxes.begin(), alreadyPresentMemoryCheckboxes.end(), memoryName) == alreadyPresentMemoryCheckboxes.end()) + { + // new memory available + auto box = new QCheckBox(QString::fromStdString(memoryName)); + box->setChecked(true); + _availableMemoriesGroupBox->layout()->addWidget(box); + } + } + } armem::DataMode QueryWidget::dataMode() const { @@ -61,6 +136,30 @@ namespace armarx::armem::gui : armem::DataMode::NoData; } + bool QueryWidget::dropRemovedMemories() const + { + return _dropRemovedCheckBox->isChecked(); + } + + + std::vector<std::string> QueryWidget::enabledMemories() const + { + std::vector<std::string> enabledMemoryCheckboxes; + int maxIndex = _availableMemoriesGroupBox->layout()->count(); + for (int i = 0; i < maxIndex; ++i) + { + auto w = _availableMemoriesGroupBox->layout()->itemAt(i)->widget(); + QCheckBox* box = static_cast<QCheckBox*>(w); + std::string memoryName = box->text().toStdString(); + if (box->isVisible() && box->isChecked()) + { + // checkbox is visible and checked, so it is enabled + enabledMemoryCheckboxes.push_back(memoryName); + } + } + return enabledMemoryCheckboxes; + } + int QueryWidget::queryLinkRecursionDepth() const { return _recursionDepthSpinner->value(); diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h index e7a98a8026786a7c8e0c2acbfd8dc834cb72f16a..5d03cf90fd7f6f84bfa9f5d6677c9edd31e58617 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h @@ -8,6 +8,7 @@ class QWidget; class QTabWidget; class QPushButton; +class QGroupBox; namespace armarx::armem::gui { @@ -23,8 +24,14 @@ namespace armarx::armem::gui armem::DataMode dataMode() const; + bool dropRemovedMemories() const; + int queryLinkRecursionDepth() const; + std::vector<std::string> enabledMemories() const; + + void update(const std::vector<std::string>& memoryNames); + public slots: @@ -45,10 +52,13 @@ namespace armarx::armem::gui private: QCheckBox* _dataCheckBox; + QCheckBox* _dropRemovedCheckBox; QLabel* _recursionDepthLabel; QSpinBox* _recursionDepthSpinner; QPushButton* _storeInLTMButton; + QGroupBox* _additionalSettingsGroupBox; + QGroupBox* _availableMemoriesGroupBox; }; }