From f54b3c4fc62805480fee9b874e695ef9fdb680e3 Mon Sep 17 00:00:00 2001 From: Meixner <andre.meixner@kit.edu> Date: Tue, 26 Nov 2024 20:34:00 +0100 Subject: [PATCH] Added first implemntation of ArvizProfile --- .../ArViz/ArVizWidgetController.cpp | 48 +++++- .../gui-plugins/ArViz/ArVizWidgetController.h | 6 + .../RobotAPI/gui-plugins/ArViz/CMakeLists.txt | 2 + .../ArViz/HiddenCoinLayerManagerWidget.cpp | 153 ++++++++++++++++++ .../ArViz/HiddenCoinLayerManagerWidget.h | 92 +++++++++++ 5 files changed, 295 insertions(+), 6 deletions(-) create mode 100644 source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.cpp create mode 100644 source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.h diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp index 945013a99..1633da37a 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp @@ -30,6 +30,7 @@ #include <QMessageBox> #include <QTimer> #include <QFileDialog> +#include <qnamespace.h> namespace armarx @@ -92,6 +93,10 @@ namespace armarx connect(this, &This::connectGui, this, &This::onConnectGui, Qt::QueuedConnection); connect(this, &This::disconnectGui, this, &This::onDisconnectGui, Qt::QueuedConnection); + hiddenCoinLayerWidget = new HiddenCoinLayerManagerWidget(); + widget.verticalLayout->addWidget(hiddenCoinLayerWidget); + connect(hiddenCoinLayerWidget, &HiddenCoinLayerManagerWidget::hiddenLayersReset, this, &This::updateHiddenLayers); + // Layer info tree. connect(widget.layerTree, &QTreeWidget::currentItemChanged, this, &This::updateSelectedLayer); @@ -105,7 +110,6 @@ namespace armarx layerInfoTree.setEnabled(widget.layerInfoTreeGroupBox->isChecked()); layerInfoTree.registerVisualizerCallbacks(visualizer); - // We need a callback from the visualizer, when the layers have changed // So we can update the tree accordingly visualizer.layersChangedCallback = [this](std::vector<viz::CoinLayerID> const & layers) @@ -167,6 +171,32 @@ namespace armarx emit disconnectGui(); } + void ArVizWidgetController::updateHiddenLayers() + { + { + QSignalBlocker blocker(widget.layerTree); + + // Iterate over all items and activate/deactivate layers accordingly + for (int compIndex = 0; compIndex < widget.layerTree->topLevelItemCount(); ++compIndex) + { + QTreeWidgetItem* componentItem = widget.layerTree->topLevelItem(compIndex); + bool parentChecked = true; + const std::string component = componentItem->text(0).toStdString(); + for (int layerIndex = 0; layerIndex < componentItem->childCount(); ++layerIndex) + { + QTreeWidgetItem* layerItem = componentItem->child(layerIndex); + const std::string layer = layerItem->text(0).toStdString(); + const viz::CoinLayerID layerID(component, layer); + const bool visible = not hiddenCoinLayerWidget->isHidden(layerID); + layerItem->setCheckState(0, visible ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + parentChecked &= visible; + visualizer.showLayer(layerID, visible); + } + componentItem->setCheckState(0, parentChecked ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + } + } + } + void ArVizWidgetController::onConnectGui() { onRefreshRecordings(); @@ -205,6 +235,7 @@ namespace armarx { layerItem->setCheckState(0, componentCheck); } + hiddenCoinLayerWidget->update(component, componentCheck == Qt::Checked); } return; } @@ -217,6 +248,9 @@ namespace armarx ARMARX_VERBOSE << "Layer: " << layer << ", Visible: " << layerVisible; viz::CoinLayerID layerID(component, layer); + + hiddenCoinLayerWidget->update(layerID, layerVisible); + visualizer.showLayer(layerID, layerVisible); } } @@ -263,8 +297,7 @@ namespace armarx // Create a new item currentItem = new QTreeWidgetItem(tree); currentItem->setText(0, QString::fromStdString(component)); - // A new item is visible by default - currentItem->setCheckState(0, Qt::Checked); + currentItem->setCheckState(0, hiddenCoinLayerWidget->isHidden(entry) ? Qt::Unchecked : Qt::Checked); componentWasNew = true; } @@ -286,8 +319,7 @@ namespace armarx std::string const& layer = entry.second; QTreeWidgetItem* layerItem = new QTreeWidgetItem; layerItem->setText(0, QString::fromStdString(layer)); - // A new item is visible by default - layerItem->setCheckState(0, Qt::Checked); + layerItem->setCheckState(0, hiddenCoinLayerWidget->isHidden(entry) ? Qt::Unchecked : Qt::Checked); if (currentItem) { @@ -308,7 +340,11 @@ namespace armarx } else { - // Item exists already ==> nothing to be done + // Item exists already ==> make sure that it is not shown if necessary + if (hiddenCoinLayerWidget->isHidden(iter->first) or iter->second->checkState(0) == Qt::Unchecked) + { + visualizer.showLayer(iter->first, false); + } } } } diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h index cb10e285b..d81dc69bc 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h @@ -21,6 +21,8 @@ */ #pragma once +#include <unordered_set> + #include <RobotAPI/gui-plugins/ArViz/ui_ArVizWidget.h> #include <RobotAPI/components/ArViz/Coin/Visualizer.h> @@ -34,6 +36,7 @@ #include <ArmarXCore/core/system/ImportExportComponent.h> #include "LayerInfoTree.h" +#include "HiddenCoinLayerManagerWidget.h" namespace armarx @@ -108,6 +111,7 @@ namespace armarx public slots: /* QT slot declarations */ + void updateHiddenLayers(); signals: /* QT signal declarations */ @@ -224,6 +228,8 @@ namespace armarx std::vector<double> timings; + HiddenCoinLayerManagerWidget* hiddenCoinLayerWidget; + public: static QIcon GetWidgetIcon() { diff --git a/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt b/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt index c9f30ffe5..318b4d9bd 100644 --- a/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt +++ b/source/RobotAPI/gui-plugins/ArViz/CMakeLists.txt @@ -10,6 +10,7 @@ armarx_build_if(ArmarXGui_FOUND "ArmarXGui not available") set(SOURCES ArVizGuiPlugin.cpp ArVizWidgetController.cpp + HiddenCoinLayerManagerWidget.cpp LayerInfoTree.cpp ) @@ -18,6 +19,7 @@ set(SOURCES set(HEADERS ArVizGuiPlugin.h ArVizWidgetController.h + HiddenCoinLayerManagerWidget.h LayerInfoTree.h ) diff --git a/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.cpp b/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.cpp new file mode 100644 index 000000000..95ca8de3d --- /dev/null +++ b/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.cpp @@ -0,0 +1,153 @@ +#include "HiddenCoinLayerManagerWidget.h" + +#include <ArmarXCore/core/logging/Logging.h> +#include <QVBoxLayout> +#include <QMessageBox> + +#include <ArmarXCore/core/system/ArmarXDataPath.h> +#include <qboxlayout.h> +#include <qlabel.h> + +namespace armarx +{ + +HiddenCoinLayerManagerWidget::HiddenCoinLayerManagerWidget(QWidget* parent) : + QWidget(parent), + settings(QString((armarx::ArmarXDataPath::GetDefaultUserConfigPath() + "/ArvizHiddenCoinLayerManager.conf").c_str()), + QSettings::NativeFormat), + hiddenLayers(HiddenLayers{}) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + + // Setup UI elements + hiddenLayersComboBox = new QComboBox(this); + hiddenLayersComboBox->addItem("default"); + connect(hiddenLayersComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &HiddenCoinLayerManagerWidget::onHiddenLayersSelected); + + nameEdit = new QLineEdit(this); + saveButton = new QPushButton("Save", this); + deleteButton = new QPushButton("Delete", this); + + layout->addWidget(new QLabel("Arviz Layer Saves: ")); + layout->addWidget(hiddenLayersComboBox); + layout->addWidget(nameEdit); + layout->addWidget(saveButton); + layout->addWidget(deleteButton); + setLayout(layout); + + connect(saveButton, &QPushButton::clicked, this, &HiddenCoinLayerManagerWidget::saveCurrentHiddenLayers); + connect(deleteButton, &QPushButton::clicked, this, &HiddenCoinLayerManagerWidget::deleteCurrentHiddenLayersSave); + + loadHiddenLayerNames(); + loadDefaultHiddenLayer(); +} + +void HiddenCoinLayerManagerWidget::onHiddenLayersSelected(int index) +{ + const QString name = hiddenLayersComboBox->currentText(); + + loadHiddenLayers(name); + emit hiddenLayersReset(); +} + +void HiddenCoinLayerManagerWidget::saveCurrentHiddenLayers() +{ + const QString name = nameEdit->text(); + if (name.isEmpty() or name == "default") + { + QMessageBox::warning(this, "Warning", "Please enter a valid name."); + return; + } + + if (saveHiddenLayers(name, hiddenLayers)) + { + if (hiddenLayersComboBox->findText(name) == -1) + { + hiddenLayersComboBox->addItem(name); + } + hiddenLayersComboBox->setCurrentText(name); + } +} + +void HiddenCoinLayerManagerWidget::deleteCurrentHiddenLayersSave() +{ + const QString name = hiddenLayersComboBox->currentText(); + if (name == "default") + { + QMessageBox::warning(this, "Warning", "Cannot delete the default."); + return; + } + + settings.remove(name); + + int index = hiddenLayersComboBox->currentIndex(); + hiddenLayersComboBox->removeItem(index); + loadDefaultHiddenLayer(); + emit hiddenLayersReset(); +} + +void HiddenCoinLayerManagerWidget::loadHiddenLayerNames() +{ + for (const QString& groups : settings.childGroups()) + { + hiddenLayersComboBox->addItem(groups); + } +} + +void HiddenCoinLayerManagerWidget::loadDefaultHiddenLayer() +{ + hiddenLayersComboBox->setCurrentText("default"); + hiddenLayers.clear(); +} + +void HiddenCoinLayerManagerWidget::loadHiddenLayers(const QString& name) +{ + std::scoped_lock lock(mtx); + hiddenLayers.clear(); + + if (name != "default") + { + settings.beginGroup(name); + const int size = settings.beginReadArray("layers"); + for (int i = 0; i < size; ++i) + { + settings.setArrayIndex(i); + QString first = settings.value("component").toString(); + QString second = settings.value("layer").toString(); + hiddenLayers.insert({first.toStdString(), second.toStdString()}); + } + settings.endArray(); + settings.endGroup(); + + nameEdit->setText(name); + deleteButton->setEnabled(true); + } + else + { + deleteButton->setEnabled(false); + } +} + +bool HiddenCoinLayerManagerWidget::saveHiddenLayers(const QString& name, const HiddenLayers& hiddenLayers) +{ + if (name != "default") + { + settings.beginGroup(name); + settings.remove("layers"); + settings.beginWriteArray("layers", hiddenLayers.size()); + int i = 0; + for (const auto& pair : hiddenLayers) + { + settings.setArrayIndex(i++); + settings.setValue("component", QString::fromStdString(pair.first)); + settings.setValue("layer", QString::fromStdString(pair.second)); + } + settings.endArray(); + settings.endGroup(); + return true; + } + return false; +} + +} \ No newline at end of file diff --git a/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.h b/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.h new file mode 100644 index 000000000..f10140d09 --- /dev/null +++ b/source/RobotAPI/gui-plugins/ArViz/HiddenCoinLayerManagerWidget.h @@ -0,0 +1,92 @@ +#pragma once + +#include <QWidget> +#include <QComboBox> +#include <QPushButton> +#include <QLineEdit> +#include <QSettings> +#include <QString> + +#include <mutex> +#include <qobjectdefs.h> +#include <shared_mutex> +#include <unordered_set> +#include <string> +#include <utility> + +#include <RobotAPI/components/ArViz/Coin/Visualizer.h> + +namespace armarx +{ + struct pair_hash + { + template <typename T1, typename T2> + std::size_t operator ()(const std::pair<T1, T2>& p) const + { + auto h1 = std::hash<T1>{}(p.first); + auto h2 = std::hash<T2>{}(p.second); + return h1 ^ (h2 << 1); + } + }; + + using HiddenLayers = std::unordered_set<viz::CoinLayerID, pair_hash>; + + class HiddenCoinLayerManagerWidget : public QWidget + { + Q_OBJECT + + public: + explicit HiddenCoinLayerManagerWidget(QWidget* parent = nullptr); + + inline void update(const viz::CoinLayerID& layerID, bool visible) + { + std::scoped_lock lock(mtx); + + if (visible and hiddenLayers.count(layerID) > 0) + { + hiddenLayers.erase(layerID); + hiddenLayers.erase({layerID.first, "*"}); + } + else if (not visible) + { + hiddenLayers.insert(layerID); + } + } + + inline void update(const std::string& componentName, bool visible) + { + update({componentName, "*"}, visible); + } + + inline bool isHidden(const viz::CoinLayerID& layerID) const + { + std::scoped_lock lock(mtx); + + return hiddenLayers.count(layerID) > 0 or hiddenLayers.count({layerID.first, "*"}) > 0; + } + + signals: + // Signal emitted when the saveHiddenLayer is reset (reselected) + void hiddenLayersReset(); + + private slots: + void onHiddenLayersSelected(int index); + void saveCurrentHiddenLayers(); + void deleteCurrentHiddenLayersSave(); + + private: + void loadHiddenLayerNames(); + void loadDefaultHiddenLayer(); + void loadHiddenLayers(const QString& name); + bool saveHiddenLayers(const QString& name, const HiddenLayers& hiddenLayers); + + QComboBox* hiddenLayersComboBox; + QLineEdit* nameEdit; + QPushButton* saveButton; + QPushButton* deleteButton; + QSettings settings; + mutable std::mutex mtx; + HiddenLayers hiddenLayers; + }; + +} \ No newline at end of file -- GitLab