From 4e2ac806afff4ca3516614925b27f0e030a34778 Mon Sep 17 00:00:00 2001
From: Meixner <andre.meixner@kit.edu>
Date: Tue, 3 Dec 2024 17:56:45 +0100
Subject: [PATCH] Fixed errors with ArViz profiles; Added setting default
 profile

---
 .../ArViz/ArVizWidgetController.cpp           |  22 ++-
 .../ArViz/ArvizProfileManagerWidget.cpp       | 125 ++++++++++++++----
 .../ArViz/ArvizProfileManagerWidget.h         |  46 ++++++-
 3 files changed, 148 insertions(+), 45 deletions(-)

diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
index b1933de86..fc014c5ad 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp
@@ -22,16 +22,14 @@
 
 #include "ArVizWidgetController.h"
 
-#include <ArmarXCore/observers/variant/Variant.h>
-#include "RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.h"
-
 #include <SimoxUtility/algorithm/string/string_tools.h>
 
+#include <ArmarXCore/observers/variant/Variant.h>
+
 #include <QLineEdit>
 #include <QMessageBox>
 #include <QTimer>
 #include <QFileDialog>
-#include <qnamespace.h>
 
 
 namespace armarx
@@ -94,14 +92,7 @@ namespace armarx
         connect(this, &This::connectGui, this, &This::onConnectGui, Qt::QueuedConnection);
         connect(this, &This::disconnectGui, this, &This::onDisconnectGui, Qt::QueuedConnection);
 
-        arvizeProfileLayerWidget = new ArvizProfileManagerWidget();
-        widget.verticalLayout->addWidget(arvizeProfileLayerWidget);
-        connect(arvizeProfileLayerWidget, &ArvizProfileManagerWidget::publishUpdate, this, &This::updateLayersFromProfile);
-        connect(arvizeProfileLayerWidget, &ArvizProfileManagerWidget::fetchUpdate, this, &This::updateLayers);
-
-
         // Layer info tree.
-
         connect(widget.layerTree, &QTreeWidget::currentItemChanged, this, &This::updateSelectedLayer);
 
         connect(widget.layerInfoTreeGroupBox, &QGroupBox::toggled, &layerInfoTree, &LayerInfoTree::setEnabled);
@@ -120,6 +111,12 @@ namespace armarx
             layersChanged(layers);
         };
 
+        // Arviz Profile tree.
+        arvizeProfileLayerWidget = new ArvizProfileManagerWidget();
+        widget.verticalLayout->addWidget(arvizeProfileLayerWidget);
+        connect(arvizeProfileLayerWidget, &ArvizProfileManagerWidget::publishUpdate, this, &This::updateLayersFromProfile);
+        connect(arvizeProfileLayerWidget, &ArvizProfileManagerWidget::fetchUpdate, this, &This::updateLayers);
+
         replayTimer->start(33);
     }
 
@@ -304,9 +301,10 @@ namespace armarx
                 if (iter == currentComponents.end())
                 {
                     // Create a new item
+                    QSignalBlocker blocker(tree); // otherwise added as unchecked and directly updated
                     currentItem = new QTreeWidgetItem(tree);
                     currentItem->setText(0, QString::fromStdString(component));
-                    currentItem->setCheckState(0, arvizeProfileLayerWidget->isHidden(entry) ? Qt::Unchecked : Qt::Checked);
+                    currentItem->setCheckState(0, arvizeProfileLayerWidget->isHidden(component) ? Qt::Unchecked : Qt::Checked);
 
                     componentWasNew = true;
                 }
diff --git a/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.cpp b/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.cpp
index fc28f1901..4af5a0fd4 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.cpp
+++ b/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.cpp
@@ -1,6 +1,6 @@
 #include "ArvizProfileManagerWidget.h"
-#include <string>
 
+#include <string>
 
 #include <QVBoxLayout>
 #include <QMessageBox>
@@ -9,9 +9,8 @@
 #include <QFormLayout>
 #include <QDialogButtonBox>
 #include <QPushButton>
-#include <qboxlayout.h>
-#include <qchar.h>
-#include <qradiobutton.h>
+#include <QBoxLayout>
+#include <QRadioButton>
 
 #include <ArmarXCore/core/system/ArmarXDataPath.h>
 #include <ArmarXCore/core/logging/Logging.h>
@@ -19,10 +18,13 @@
 namespace armarx
 {
 
-ProfileDialog::ProfileDialog(QWidget *parent, const QString &name, bool additive, bool addDialog)
+const QString ArvizProfileManagerWidget::SETTINGS_DEFAULT_PROFILE_KEY = "defaultProfileName";
+
+
+ProfileDialog::ProfileDialog(QWidget *parent, const QString &name, bool additive, bool addDialog, bool isDefault)
     : QDialog(parent)
 {
-    setWindowTitle(addDialog ? "Add Profile" : "Edit Profile");
+    setWindowTitle(addDialog ? "Add ArViz Profile" : "Edit ArViz Profile");
 
     // Set up UI components
     nameInput = new QLineEdit(this);
@@ -33,10 +35,14 @@ ProfileDialog::ProfileDialog(QWidget *parent, const QString &name, bool additive
     substractiveRadioButton = new QRadioButton("Save removed layers", this);
     substractiveRadioButton->setChecked(not additive);
 
+    defaultCheckBox = new QCheckBox("Mark default", this);
+    defaultCheckBox->setChecked(isDefault);
+
     // Save/Cancel buttons
     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel, this);
     connect(buttonBox, &QDialogButtonBox::accepted, this, &ProfileDialog::accept);
     connect(buttonBox, &QDialogButtonBox::rejected, this, &ProfileDialog::reject);
+    buttonBox->setCenterButtons(true);
 
     // Delete button
     deleteButton = new QPushButton("Delete", this);
@@ -47,13 +53,13 @@ ProfileDialog::ProfileDialog(QWidget *parent, const QString &name, bool additive
     formLayout->addRow("Profile Name:", nameInput);
     formLayout->addRow(additiveRadioButton);
     formLayout->addRow(substractiveRadioButton);
+    formLayout->addRow(defaultCheckBox);
+    formLayout->addRow(buttonBox);
+    formLayout->addRow(deleteButton);
 
-    QHBoxLayout *mainLayout = new QHBoxLayout;
-    mainLayout->addLayout(formLayout);
-    mainLayout->addWidget(buttonBox);
-    mainLayout->addWidget(deleteButton);
-
-    setLayout(mainLayout);
+    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+    setLayout(formLayout);
+    adjustSize();
 }
 
 ProfileDialog::~ProfileDialog()
@@ -70,6 +76,11 @@ bool ProfileDialog::isAdditive() const
     return additiveRadioButton->isChecked();
 }
 
+bool ProfileDialog::isDefaultProfile() const
+{
+    return defaultCheckBox->isChecked();
+}
+
 void ProfileDialog::deleteProfile()
 {
     // Handle the profile deletion
@@ -89,14 +100,14 @@ ArvizProfileManagerWidget::ArvizProfileManagerWidget(QWidget* parent) :
 
     // Setup UI elements
     layersComboBox = new QComboBox(this);
-    connect(layersComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), 
-           this, &ArvizProfileManagerWidget::onLayersSelected);
+    connect(layersComboBox, QOverload<int>::of(&QComboBox::activated), 
+           this, &ArvizProfileManagerWidget::onProfileSelected);
 
     addButton = new QPushButton("Add", this);
     editButton = new QPushButton("Edit", this);
     saveButton = new QPushButton("Save", this);
 
-    layout->addWidget(new QLabel("Arviz Profile: "));
+    layout->addWidget(new QLabel("ArViz Profile: "));
     layout->addWidget(layersComboBox);
     layout->addWidget(addButton);
     layout->addWidget(editButton);
@@ -106,20 +117,35 @@ ArvizProfileManagerWidget::ArvizProfileManagerWidget(QWidget* parent) :
 
     connect(addButton, &QPushButton::clicked, this, &ArvizProfileManagerWidget::addProfile);
     connect(editButton, &QPushButton::clicked, this, &ArvizProfileManagerWidget::editProfile);
-    connect(saveButton, &QPushButton::clicked, this, &ArvizProfileManagerWidget::saveCurrentLayers);
+    connect(saveButton, &QPushButton::clicked, this, &ArvizProfileManagerWidget::saveCurrentProfile);
 
     loadLayerNames();
+
+    // Load default profile
+    const QString defaultProfileName 
+        = settings.value(SETTINGS_DEFAULT_PROFILE_KEY).toString();
+    if (int i = layersComboBox->findText(defaultProfileName); i >= 0)
+    {
+        layersComboBox->setCurrentText(defaultProfileName);
+        onProfileSelected(i);
+    }
+    else
+    {
+        // Not found. Load empty profile
+        layersComboBox->setCurrentIndex(-1);
+    }
 }
 
-void ArvizProfileManagerWidget::onLayersSelected(int index) 
+void ArvizProfileManagerWidget::onProfileSelected(int index) 
 {
-    const QString name = layersComboBox->currentText();
-
-    loadProfile(name);
-    emit publishUpdate();
+    if (index >= 0 and index < layersComboBox->count())
+    {
+        loadProfile(layersComboBox->itemText(index));
+        emit publishUpdate();
+    }
 }
 
-void ArvizProfileManagerWidget::saveCurrentLayers() 
+void ArvizProfileManagerWidget::saveCurrentProfile() 
 {
     const QString name = layersComboBox->currentText();
     if (name.isEmpty()) 
@@ -138,7 +164,7 @@ void ArvizProfileManagerWidget::saveCurrentLayers()
     }
 }
 
-void ArvizProfileManagerWidget::deleteCurrentLayersSave() 
+void ArvizProfileManagerWidget::deleteCurrentProfileSave() 
 {
     const QString name = layersComboBox->currentText();
     settings.remove(name);
@@ -168,16 +194,28 @@ void ArvizProfileManagerWidget::addProfile()
         {
             name = defaultName + QString::fromStdString(std::to_string(counter++));
         }
-        dialog = new ProfileDialog(this, name, defaultAdditive, true);
+        dialog = new ProfileDialog(this, name, defaultAdditive, true,
+                                   settings.value(SETTINGS_DEFAULT_PROFILE_KEY) == name);
         const int result = dialog->exec();
 
         if (result == QDialog::Accepted) 
         {
+            if (dialog->getName().isEmpty())
+            {
+                ARMARX_WARNING << "Empty name for profile set.";
+                return;
+            }
             if (layersComboBox->findText(dialog->getName()) >= 0)
             {
                 ARMARX_WARNING << "Profile with name " << dialog->getName().toStdString() << " already exists!";
                 return;
             }
+
+            if (dialog->isDefaultProfile())
+            {
+                settings.setValue(SETTINGS_DEFAULT_PROFILE_KEY, dialog->getName());
+            }
+
             currentProfile.layers = {};
             currentProfile.additive = dialog->isAdditive();
             emit fetchUpdate();
@@ -203,23 +241,52 @@ void ArvizProfileManagerWidget::editProfile()
     {
         if (dialog == nullptr || !dialog->isVisible()) 
         {
-            dialog = new ProfileDialog(this, layersComboBox->currentText(), currentProfile.additive, false);
-            connect(dialog, &ProfileDialog::deleteProfileSignal, this, &ArvizProfileManagerWidget::deleteCurrentLayersSave);
+            const auto name = layersComboBox->currentText();
+            dialog = new ProfileDialog(this, name, currentProfile.additive,
+                                       false, settings.value(SETTINGS_DEFAULT_PROFILE_KEY) == name);
+            connect(dialog, &ProfileDialog::deleteProfileSignal, this, &ArvizProfileManagerWidget::deleteCurrentProfileSave);
             const int result = dialog->exec();
 
             if (result == QDialog::Accepted) 
             {
+                const auto newName = dialog->getName();
+
+                if (newName.isEmpty())
+                {
+                    ARMARX_WARNING << "Empty name for profile set.";
+                    return;
+                }
+
+                if (newName != name)
+                {
+                    // Rename
+                    if (layersComboBox->findText(newName) >= 0)
+                    {
+                        ARMARX_WARNING << "Profile with name " << newName.toStdString() << " already exists!";
+                        return;
+                    }
+                    settings.remove(layersComboBox->currentText());
+                }
+
                 if (dialog->isAdditive() != currentProfile.additive)
                 {
                     currentProfile.layers.clear();
                     currentProfile.additive = dialog->isAdditive();
                     emit fetchUpdate();
                 }
+
+                if (dialog->isDefaultProfile())
+                {
+                    settings.setValue(SETTINGS_DEFAULT_PROFILE_KEY, newName);
+                }
+                else if (settings.value(SETTINGS_DEFAULT_PROFILE_KEY) == name)
+                {
+                    settings.setValue(SETTINGS_DEFAULT_PROFILE_KEY, "");
+                }
                 
-                settings.remove(layersComboBox->currentText());
                 saveProfile(dialog->getName(), currentProfile);
                 loadLayerNames();
-                layersComboBox->setCurrentText(dialog->getName());
+                layersComboBox->setCurrentText(newName);
             } 
             else 
             {
@@ -254,6 +321,8 @@ void ArvizProfileManagerWidget::loadLayerNames()
 
 void ArvizProfileManagerWidget::loadProfile(const QString& name)
 {
+    ARMARX_INFO << "Loading ArViz profile " << name.toStdString();
+
     std::scoped_lock lock(mtx);
     currentProfile.layers.clear();
     
diff --git a/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.h b/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.h
index 3f998ebd5..1f5a97151 100644
--- a/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.h
+++ b/source/RobotAPI/gui-plugins/ArViz/ArvizProfileManagerWidget.h
@@ -1,3 +1,24 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotAPI::gui-plugins::ArVizProfileManagerWidget
+ * @author     Andre Meixner ( andre dot meixner at kit dot edu )
+ * @date       2024
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
 #pragma once
 
 #include <QWidget>
@@ -8,9 +29,10 @@
 #include <QSettings>
 #include <QString>
 #include <QCheckBox>
-#include <qradiobutton.h>
+#include <QRadioButton>
 
 #include <mutex>
+#include <qcheckbox.h>
 #include <unordered_set>
 #include <string>
 #include <utility>
@@ -44,11 +66,13 @@ namespace armarx
         Q_OBJECT
 
     public:
-        explicit ProfileDialog(QWidget *parent = nullptr, const QString &name = "", bool additive = false, bool addDialog = true);
+        explicit ProfileDialog(QWidget *parent = nullptr, const QString &name = "",
+                               bool additive = false, bool addDialog = true, bool isDefault = false);
         ~ProfileDialog();
 
         QString getName() const;
         bool isAdditive() const;
+        bool isDefaultProfile() const;
 
     signals:
         void deleteProfileSignal();
@@ -61,6 +85,7 @@ namespace armarx
         QRadioButton *additiveRadioButton;
         QRadioButton *substractiveRadioButton;
         QPushButton *deleteButton;
+        QCheckBox *defaultCheckBox;
     };
 
 
@@ -108,14 +133,23 @@ namespace armarx
             }
         }
 
+        inline bool isHidden(const std::string& componentName) const
+        {
+            std::scoped_lock lock(mtx);
+
+            return currentProfile.additive == (currentProfile.layers.count({componentName, "*"}) == 0);
+        }
+
     signals:
         void publishUpdate();
         void fetchUpdate();
 
+    public slots:
+        void onProfileSelected(int index);
+
     private slots:
-        void onLayersSelected(int index);
-        void saveCurrentLayers();
-        void deleteCurrentLayersSave();
+        void saveCurrentProfile();
+        void deleteCurrentProfileSave();
 
         void addProfile();
         void editProfile();
@@ -134,6 +168,8 @@ namespace armarx
         mutable std::mutex mtx;
 
         Profile currentProfile;
+
+        static const QString SETTINGS_DEFAULT_PROFILE_KEY;
     };
 
 }
-- 
GitLab