diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
index 0a8bbb2f936f3ac863cbfb1dba62c0dcfc501591..bc81675822ad2ffa567ce29a1d40de523d5f8f9f 100644
--- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidget.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>522</width>
-    <height>378</height>
+    <width>621</width>
+    <height>350</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -56,7 +56,75 @@
       </attribute>
       <layout class="QHBoxLayout" name="horizontalLayout_2">
        <item>
-        <widget class="QTableWidget" name="objectsTable"/>
+        <widget class="QTreeWidget" name="objectsTree">
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <column>
+          <property name="text">
+           <string>ID</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Provider</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Type</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>OOBB</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Confidence</string>
+          </property>
+         </column>
+         <column>
+          <property name="text">
+           <string>Timestamp</string>
+          </property>
+         </column>
+         <item>
+          <property name="text">
+           <string>Provider2</string>
+          </property>
+          <item>
+           <property name="text">
+            <string>Dataset1/Object2</string>
+           </property>
+           <property name="text">
+            <string>Provider2</string>
+           </property>
+          </item>
+         </item>
+         <item>
+          <property name="text">
+           <string>Provider1</string>
+          </property>
+          <item>
+           <property name="text">
+            <string>Dataset2/Object2/instance1</string>
+           </property>
+           <property name="text">
+            <string>Provider1</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Dataset1/Object1</string>
+           </property>
+           <property name="text">
+            <string>Provider1</string>
+           </property>
+          </item>
+         </item>
+        </widget>
        </item>
       </layout>
      </widget>
@@ -72,18 +140,12 @@
            <property name="alternatingRowColors">
             <bool>true</bool>
            </property>
-           <property name="sortingEnabled">
-            <bool>true</bool>
-           </property>
            <property name="columnCount">
             <number>3</number>
            </property>
            <attribute name="headerCascadingSectionResizes">
             <bool>true</bool>
            </attribute>
-           <attribute name="headerShowSortIndicator" stdset="0">
-            <bool>true</bool>
-           </attribute>
            <attribute name="headerStretchLastSection">
             <bool>true</bool>
            </attribute>
diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
index ae76b958a2a21ea42459627413b7a81f5163567f..5742df60a925fb61a927772085dd66871caff540 100644
--- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
+++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp
@@ -26,6 +26,8 @@
 
 #include <QTimer>
 
+#include <ArmarXCore/core/time/TimeUtil.h>
+
 #include <RobotAPI/components/ObjectPoseObserver/ice_conversions.h>
 #include <RobotAPI/components/ObjectPoseObserver/ObjectPose.h>
 
@@ -37,10 +39,8 @@ namespace armarx
     {
         widget.setupUi(getWidget());
 
-        QStringList header = {"Dataset", "ClassName", "InstanceName", "Provider", "Type"};
-        widget.objectsTable->setColumnCount(header.size());
-        widget.objectsTable->setHorizontalHeaderLabels(header);
-
+        widget.objectsTree->clear();
+        widget.objectsTree->sortItems(0, Qt::SortOrder::AscendingOrder);
 
         widget.requestTree->clear();
         widget.requestTree->sortItems(0, Qt::SortOrder::AscendingOrder);
@@ -53,7 +53,20 @@ namespace armarx
 
         connect(widget.requestButton, &QPushButton::pressed, this, &This::requestSelectedObjects);
 
-        // QTimer* timer = new QTimer(this,);
+        QTimer* timer = new QTimer(this);
+        timer->setInterval(500);
+        connect(timer, &QTimer::timeout, this, &This::updateTab);
+        connect(widget.autoUpdateCheckBox, &QCheckBox::toggled, this, [timer](bool checked)
+        {
+            if (checked)
+            {
+                timer->start();
+            }
+            else
+            {
+                timer->stop();
+            }
+        });
     }
 
 
@@ -130,6 +143,196 @@ namespace armarx
         }
     }
 
+
+    template <class ContainerT>
+    struct TreeVisitor
+    {
+        using ElementT = typename ContainerT::value_type;
+
+        /// Return < 0 if `element < item`, 0 if `element == item`, and > 0 if `element > item`.
+        using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>;
+        using NameFn = std::function<std::string(const ElementT& element)>;
+        using MakeItemFn = std::function<QTreeWidgetItem*(const ElementT& element)>;
+        using UpdateItemFn = std::function<bool(const ElementT& element, QTreeWidgetItem* item)>;
+
+        TreeVisitor() = default;
+        TreeVisitor(const ContainerT&)
+        {}
+
+        static bool NoUpdate(const ElementT& element, QTreeWidgetItem* item)
+        {
+            (void) element, (void) item;
+            return true;
+        }
+
+        void enableSort(QTreeWidget* tree)
+        {
+            tree->sortItems(0, Qt::SortOrder::AscendingOrder);
+            tree->isSortingEnabled();
+        }
+
+        template <typename ParentT>
+        void updateTree(ParentT* parent, const ContainerT& elements,
+                        CompareFn compareFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
+        {
+            int currentIndex = 0;
+            for (const auto& element : elements)
+            {
+                QTreeWidgetItem* item = nullptr;
+                if (currentIndex >= getItemCount(parent))
+                {
+                    // Add elements to the end of the list.
+                    item = makeItemFn(element);
+                    insertItem(parent, getItemCount(parent), item);
+                    ++currentIndex;
+                }
+                else
+                {
+                    QTreeWidgetItem* currentItem = getItem(parent, currentIndex);
+                    while (currentItem != nullptr && compareFn(element, currentItem) > 0)
+                    {
+                        delete takeItem(parent, currentIndex);
+                        currentItem = getItem(parent, currentIndex);
+                    }
+                    if (currentItem == nullptr || compareFn(element, currentItem) < 0)
+                    {
+                        // Insert new item before child.
+                        item = makeItemFn(element);
+                        insertItem(parent, currentIndex, item);
+                        ++currentIndex;
+                    }
+                    else if (currentItem != nullptr && compareFn(element, currentItem) == 0)
+                    {
+                        // Already existing.
+                        item = currentItem;
+                        ++currentIndex;
+                    }
+                }
+                ARMARX_CHECK_NOT_NULL(item);
+                if (!updateItemFn(element, item))
+                {
+                    break;
+                }
+            }
+        }
+
+        template <typename ParentT>
+        void updateTree(ParentT* parent, const ContainerT& elements,
+                        NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
+        {
+            auto compare = [nameFn](const ElementT & element, QTreeWidgetItem * item)
+            {
+                std::string name = nameFn(element);
+                return name.compare(item->text(0).toStdString());
+            };
+            updateTree(parent, elements, compare, makeItemFn, updateItemFn);
+        }
+
+    private:
+
+        int getItemCount(QTreeWidget* tree)
+        {
+            return tree->topLevelItemCount();
+        }
+        QTreeWidgetItem* getItem(QTreeWidget* tree, int index)
+        {
+            return tree->topLevelItem(index);
+        }
+        void insertItem(QTreeWidget* tree, int index, QTreeWidgetItem* item)
+        {
+            tree->insertTopLevelItem(index, item);
+        }
+        QTreeWidgetItem* takeItem(QTreeWidget* tree, int index)
+        {
+            return tree->takeTopLevelItem(index);
+        }
+
+        int getItemCount(QTreeWidgetItem* parent)
+        {
+            return parent->childCount();
+        }
+        QTreeWidgetItem* getItem(QTreeWidgetItem* parent, int index)
+        {
+            return parent->child(index);
+        }
+        QTreeWidgetItem* takeItem(QTreeWidgetItem* parent, int index)
+        {
+            return parent->takeChild(index);
+        }
+        void insertItem(QTreeWidgetItem* parent, int index, QTreeWidgetItem* item)
+        {
+            parent->insertChild(index, item);
+        }
+    };
+
+    template <class KeyT, class ValueT>
+    struct MapTreeVisitor : private TreeVisitor<std::map<KeyT, ValueT>>
+    {
+        using MapT = std::map<KeyT, ValueT>;
+        using Base = TreeVisitor<MapT>;
+        using ElementT = typename Base::ElementT;
+
+        using NameFn = std::function<std::string(const KeyT& key, const ValueT& value)>;
+
+        using MakeItemFn = std::function<QTreeWidgetItem*(const KeyT& key, const ValueT& value)>;
+        using UpdateItemFn = std::function<bool(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)>;
+
+        MapTreeVisitor() = default;
+        MapTreeVisitor(const MapT&)
+        {}
+
+        static std::string KeyIsName(const KeyT& key, const ValueT& value)
+        {
+            (void) value;
+            if constexpr(std::is_same<KeyT, std::string>())
+            {
+                return key;
+            }
+            else
+            {
+                std::stringstream ss;
+                ss << key;
+                return ss.str();
+            }
+        }
+
+        static bool NoUpdate(const KeyT& key, const ValueT& value, QTreeWidgetItem* item)
+        {
+            (void) key, (void) value, (void) item;
+            return true;
+        }
+
+        void updateTree(QTreeWidget* tree, const MapT& elements,
+                        MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
+        {
+            updateTree(tree, elements, KeyIsName, makeItemFn, updateItemFn);
+        }
+
+        template <class ParentT>
+        void updateTree(ParentT* parent, const MapT& elements,
+                        NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate)
+        {
+            auto name = [nameFn](const ElementT & element)
+            {
+                const auto& [key, value] = element;
+                return nameFn(key, value);
+            };
+            auto makeItem = [makeItemFn](const ElementT & element)
+            {
+                const auto& [key, value] = element;
+                return makeItemFn(key, value);
+            };
+            auto updateItem = [updateItemFn](const ElementT & element, QTreeWidgetItem * item)
+            {
+                const auto& [key, value] = element;
+                return updateItemFn(key, value, item);
+            };
+
+            Base::updateTree(parent, elements, name, makeItem, updateItem);
+        }
+    };
+
+
     void ObjectPoseGuiWidgetController::updateObjectsTab()
     {
         if (!objectPoseObserver)
@@ -140,42 +343,72 @@ namespace armarx
 
         IceUtil::Time start = IceUtil::Time::now();
         ARMARX_INFO << "Getting object poses...";
-        // const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPoseObserver->getObjectPoses());
+        const objpose::data::ObjectPoseSeq objectPosesIce = objectPoseObserver->getObjectPoses();
+        ARMARX_INFO << "Got " << objectPosesIce.size() << " object poses. "
+                    << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.)";
 
-        const objpose::data::ObjectPoseSeq objectPoses = objectPoseObserver->getObjectPoses();
-        ARMARX_INFO << "Got " << objectPoses.size() << " object poses. "
-                    << "(Took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms.)";
+        const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPosesIce);
 
-        start = IceUtil::Time::now();
-        widget.objectsTable->setRowCount(int(objectPoses.size()));
-
-        for (int i = 0; i < int(objectPoses.size()); ++i)
-        {
-            const objpose::data::ObjectPose& pose = objectPoses.at(size_t(i));
-            int col = 0;
-
-            widget.objectsTable->setItem(
-                i, col++, new QTableWidgetItem(pose.objectID.dataset.c_str()));
-            widget.objectsTable->setItem(
-                i, col++, new QTableWidgetItem(pose.objectID.className.c_str()));
-            widget.objectsTable->setItem(
-                i, col++, new QTableWidgetItem(pose.objectID.instanceName.c_str()));
-            widget.objectsTable->setItem(
-                i, col++, new QTableWidgetItem(pose.providerName.c_str()));
-            widget.objectsTable->setItem(
-                i, col++, new QTableWidgetItem(objpose::ObjectTypeEnumNames.to_name(pose.objectType).c_str()));
+        std::map<std::string, objpose::ObjectPoseSeq> objectPosesByProvider;
+        for (const auto& pose : objectPoses)
+        {
+            objectPosesByProvider[pose.providerName].push_back(pose);
         }
-        ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms.";
 
-    }
+        start = IceUtil::Time::now();
+
+        QTreeWidget* tree = widget.objectsTree;
+        MapTreeVisitor visitor(objectPosesByProvider);
+        auto makeProviderItem = [](const std::string & provider, const objpose::ObjectPoseSeq&)
+        {
+            QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(provider)});
+            item->setExpanded(true);
+            return item;
+        };
+        auto updateItem = [](const std::string&, const objpose::ObjectPoseSeq & objectPoses, QTreeWidgetItem * item)
+        {
+            auto name = [](const objpose::ObjectPose & pose)
+            {
+                return pose.objectID.str();
+            };
+            auto makeItem = [](const objpose::ObjectPose&)
+            {
+                QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{});
+                return item;
+            };
+            auto updateItem = [](const objpose::ObjectPose & pose, QTreeWidgetItem * item)
+            {
+                int col = 0;
+                item->setText(col++, QString::fromStdString(pose.objectID.str()));
+                item->setText(col++, QString::fromStdString(pose.providerName));
+                item->setText(col++, QString::fromStdString(objpose::ObjectTypeEnumNames.to_name(pose.objectType)));
+
+                std::stringstream ss;
+                if (pose.localOOBB)
+                {
+                    static const Eigen::IOFormat iof(5, 0, "", " x ", "", "", "", "");
+                    ss << pose.localOOBB->dimensions().format(iof);
+                }
+                else
+                {
+                    ss << "None";
+                }
+                item->setText(col++, QString::fromStdString(ss.str()));
+                item->setText(col++, QString::number(double(pose.confidence), 'g', 2));
+                item->setText(col++, QString::fromStdString(TimeUtil::toStringDateTime(pose.timestamp)));
 
+                return true;
+            };
+            TreeVisitor visitor(objectPoses);
+            visitor.updateTree(item, objectPoses, name, makeItem, updateItem);
+
+            return true;
+        };
+        visitor.updateTree(tree, objectPosesByProvider, makeProviderItem, updateItem);
+
+        ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
+    }
 
-    struct ProviderInfo
-    {
-        std::string name;
-        objpose::ObjectPoseProviderPrx proxy;
-        objpose::ObjectTypeEnum objectType;
-    };
 
     void ObjectPoseGuiWidgetController::updateRequestTab()
     {
@@ -188,46 +421,66 @@ namespace armarx
         IceUtil::Time start = IceUtil::Time::now();
         objpose::ProviderInfoMap availableProvidersInfo = objectPoseObserver->getAvailableProvidersInfo();
         ARMARX_INFO << "Got infos of " << availableProvidersInfo.size() << " object pose providers. "
-                    << "(Took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms.)";
+                    << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.)";
 
 
         // Restructure data.
-        std::map<std::string, std::map<std::string, std::map<std::string, ProviderInfo>>> data;
+        std::map<std::string, std::set<std::pair<std::string, std::string>>> data;
         for (const auto& [providerName, info] : availableProvidersInfo)
         {
             for (const auto& id : info.supportedObjects)
             {
-                ProviderInfo& dinfo = data[id.dataset][id.className][providerName];
-                dinfo.name = providerName;
-                dinfo.proxy = info.proxy;
-                dinfo.objectType = info.objectType;
+                data[id.dataset].insert(std::make_pair(id.className, providerName));
             }
         }
 
+        start = IceUtil::Time::now();
+
         QTreeWidget* tree = widget.requestTree;
-        tree->clear();
 
-        start = IceUtil::Time::now();
-        for (const auto& [dataset, datasetData] : data)
+        MapTreeVisitor visitor(data);
+        auto makeDatasetItem = [](const std::string & dataset, const auto&)
         {
-            QTreeWidgetItem* datasetItem = new QTreeWidgetItem({QString::fromStdString(dataset)});
-            tree->addTopLevelItem(datasetItem);
+            QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(dataset)});
+            return item;
+        };
+        auto updateItem = [tree](const std::string & dataset, const auto & datasetData, QTreeWidgetItem * datasetItem)
+        {
+            (void) dataset;
 
-            for (const auto& [className, providerData] : datasetData)
+            auto compareFn = [](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem * item)
             {
-                for (const auto& [providerName, providerInfo] : providerData)
+                auto rhs = std::make_pair(item->text(0).toStdString(), item->text(1).toStdString());
+                if (lhs < rhs)
+                {
+                    return -1;
+                }
+                return lhs == rhs ? 0 : 1;
+            };
+            auto makeItemFn = [](const std::pair<std::string, std::string>& element)
+            {
+                QTreeWidgetItem* item = new QTreeWidgetItem({ QString::fromStdString(element.first), QString::fromStdString(element.second)});
+                return item;
+            };
+            auto updateItemFn = [tree](const std::pair<std::string, std::string>& element, QTreeWidgetItem * item)
+            {
+                (void) element;
+                if (!tree->itemWidget(item, 2))
                 {
-                    QTreeWidgetItem* classItem = new QTreeWidgetItem(
-                    { QString::fromStdString(className), QString::fromStdString(providerName)});
-                    datasetItem->addChild(classItem);
-
                     QCheckBox* requestCheckBox = new QCheckBox();
-                    tree->setItemWidget(classItem, 2, requestCheckBox);
+                    tree->setItemWidget(item, 2, requestCheckBox);
                 }
-            }
-        }
+                return true;
+            };
+
+            TreeVisitor visitor(datasetData);
+            visitor.updateTree(datasetItem, datasetData, compareFn, makeItemFn, updateItemFn);
+
+            return true;
+        };
+        visitor.updateTree(tree, data, makeDatasetItem, updateItem);
 
-        ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSeconds() << " ms.";
+        ARMARX_INFO << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
     }
 
     void ObjectPoseGuiWidgetController::requestSelectedObjects()