From fc29bde83854311eaef276adb04ffa753aa31ee3 Mon Sep 17 00:00:00 2001 From: Fabian Paus <fabian.paus@kit.edu> Date: Mon, 14 Oct 2019 14:14:52 +0200 Subject: [PATCH] Implement tree view to select layers to show --- .../components/ArViz/ArVizStorage.cpp | 4 +- .../components/ArViz/Coin/Visualizer.cpp | 58 ++++++++++++- .../components/ArViz/Coin/Visualizer.h | 38 ++++++++ .../RobotAPI/gui-plugins/ArViz/ArVizWidget.ui | 22 ++++- .../ArViz/ArVizWidgetController.cpp | 86 +++++++++++++++++++ .../gui-plugins/ArViz/ArVizWidgetController.h | 4 + 6 files changed, 205 insertions(+), 7 deletions(-) diff --git a/source/RobotAPI/components/ArViz/ArVizStorage.cpp b/source/RobotAPI/components/ArViz/ArVizStorage.cpp index f52f5cfc5..610ed669a 100644 --- a/source/RobotAPI/components/ArViz/ArVizStorage.cpp +++ b/source/RobotAPI/components/ArViz/ArVizStorage.cpp @@ -179,8 +179,8 @@ void ArVizStorage::compressHistory() updateHistory.push_back(std::move(layer)); } - ARMARX_INFO << "Compressed history from " << currentHistorySize - << " to " << newHistorySize; + ARMARX_VERBOSE << "Compressed history from " << currentHistorySize + << " to " << newHistorySize; long updateCounterAfterCompression = updateCounterBase + newHistorySize; diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp index 834e43bd8..25c01e3c0 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.cpp @@ -101,9 +101,7 @@ namespace armarx coinNode->ref(); root->addChild(coinNode); - CoinLayer newLayer; - newLayer.node = coinNode; - layerIt = layers.emplace(layerID, std::move(newLayer)).first; + layerIt = layers.emplace(layerID, CoinLayer(coinNode)).first; } CoinLayer& layer = layerIt->second; @@ -232,12 +230,20 @@ namespace armarx // We never block the GUI thread with Ice calls: Only get result when call is completed if (pullUpdateResult->isCompleted()) { + auto layerIDsBefore = getLayerIDs(); + LayerUpdates pulledUpdates = storage->end_pullUpdatesSince(pullUpdateResult); for (LayerUpdate const& update : pulledUpdates.updates) { apply(update); } updateCounter = pulledUpdates.updateCounter; + + auto layerIDsAfter = getLayerIDs(); + if (layerIDsAfter != layerIDsBefore) + { + emitLayersChanged(layerIDsAfter); + } } else { @@ -258,5 +264,51 @@ namespace armarx } } + void CoinVisualizer::showLayer(CoinLayerID const& id, bool visible) + { + auto iter = layers.find(id); + if (iter == layers.end()) + { + return; + } + + viz::CoinLayer& layer = iter->second; + int childIndex = root->findChild(layer.node); + if (childIndex < 0) + { + // Layer is currently not visible + if (visible) + { + root->addChild(layer.node); + } + } + else + { + // Layer is currently visible + if (!visible) + { + root->removeChild(childIndex); + } + } + } + + std::vector<CoinLayerID> CoinVisualizer::getLayerIDs() + { + std::vector<CoinLayerID> result; + for (auto& entry : layers) + { + result.push_back(entry.first); + } + return result; + } + + void CoinVisualizer::emitLayersChanged(std::vector<CoinLayerID> const& layerIDs) + { + if (layersChangedCallback) + { + layersChangedCallback(layerIDs); + } + } + } } diff --git a/source/RobotAPI/components/ArViz/Coin/Visualizer.h b/source/RobotAPI/components/ArViz/Coin/Visualizer.h index 25906d711..24aa055ce 100644 --- a/source/RobotAPI/components/ArViz/Coin/Visualizer.h +++ b/source/RobotAPI/components/ArViz/Coin/Visualizer.h @@ -18,6 +18,37 @@ namespace armarx::viz struct CoinLayer { + CoinLayer(SoSeparator* node) + : node(node) + { + // Increase ref-count, so we can add/remove this node without it being deleted + // This is needed for showing/hiding layers + node->ref(); + } + + CoinLayer(CoinLayer&& other) + : node(other.node) + { + other.node = nullptr; + } + + ~CoinLayer() + { + if (node) + { + node->unref(); + } + } + + CoinLayer(CoinLayer const&) = delete; + void operator= (CoinLayer const&) = delete; + + void operator= (CoinLayer&& other) + { + node = other.node; + other.node = nullptr; + } + SoSeparator* node = nullptr; std::map<std::string, std::unique_ptr<coin::ElementVisualization>> elements; }; @@ -56,6 +87,11 @@ namespace armarx::viz elementVisualizers.emplace_back(typeid(ElementT), std::make_unique<VisualizerT>()); } + void showLayer(CoinLayerID const& id, bool visible); + + std::vector<CoinLayerID> getLayerIDs(); + void emitLayersChanged(std::vector<CoinLayerID> const& layerIDs); + std::mutex storageMutex; viz::StorageInterfacePrx storage; Ice::AsyncResultPtr pullUpdateResult; @@ -72,5 +108,7 @@ namespace armarx::viz std::atomic<CoinVisualizerState> state{CoinVisualizerState::STOPPED}; viz::StorageInterfacePrx stateStorage; + std::function<void(std::vector<CoinLayerID> const&)> layersChangedCallback; + }; } diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui b/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui index 855fb9993..75deb8dc0 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidget.ui @@ -6,13 +6,31 @@ <rect> <x>0</x> <y>0</y> - <width>400</width> - <height>300</height> + <width>576</width> + <height>400</height> </rect> </property> <property name="windowTitle"> <string>ArVizWidget</string> </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeWidget" name="layerTree"> + <column> + <property name="text"> + <string>Layer</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Layer Information</string> + </property> + </widget> + </item> + </layout> </widget> <resources/> <connections/> diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp index 78611085d..a8716ed3f 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.cpp @@ -31,6 +31,15 @@ using namespace armarx; ArVizWidgetController::ArVizWidgetController() { widget.setupUi(getWidget()); + + connect(widget.layerTree, &QTreeWidget::itemChanged, this, &ArVizWidgetController::layerTreeChanged); + + // 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) + { + layersChanged(layers); + }; } ArVizWidgetController::~ArVizWidgetController() @@ -44,6 +53,7 @@ void ArVizWidgetController::onInitComponent() { usingProxy(storageName); } + ARMARX_IMPORTANT << "OnInit: " << storageName; } void armarx::ArVizWidgetController::onExitComponent() @@ -54,11 +64,87 @@ void ArVizWidgetController::onConnectComponent() { getProxy(storage, storageName); visualizer.startAsync(storage); + + ARMARX_IMPORTANT << "OnConnect: " << storageName; } void armarx::ArVizWidgetController::onDisconnectComponent() { visualizer.stop(); + + ARMARX_IMPORTANT << "OnDisconnect: " << storageName; +} + +void ArVizWidgetController::layerTreeChanged(QTreeWidgetItem* item, int /*column*/) +{ + // Iterate over all items and activate/deactivate layers accordingly + for (QTreeWidgetItemIterator compIter(widget.layerTree); *compIter; ++compIter) + { + QTreeWidgetItem* componentItem = *compIter; + std::string component = componentItem->text(0).toStdString(); + Qt::CheckState componentCheck = componentItem->checkState(0); + + if (componentItem == item) + { + // The parent was selected or deselected, so all children should be set accordingly + for (QTreeWidgetItemIterator layerIter(componentItem); *layerIter; ++layerIter) + { + QTreeWidgetItem* layerItem = *layerIter; + if (layerItem->checkState(0) != componentCheck) + { + layerItem->setCheckState(0, componentCheck); + } + } + return; + } + + for (QTreeWidgetItemIterator layerIter(componentItem); *layerIter; ++layerIter) + { + QTreeWidgetItem* layerItem = *layerIter; + std::string layer = layerItem->text(0).toStdString(); + bool layerVisible = (layerItem->checkState(0) == Qt::Checked); + + bool visible = layerVisible; + + viz::CoinLayerID layerID(component, layer); + visualizer.showLayer(layerID, visible); + } + } +} + +void ArVizWidgetController::layersChanged(std::vector<viz::CoinLayerID> const& layers) +{ + QTreeWidget* tree = widget.layerTree; + QTreeWidgetItem* currentItem = nullptr; + + // TODO: We need to keep the state of the old tree items + // HACK: Just remove all the children before adding them again + tree->clear(); + + std::string currentComponent; + for (auto& entry : layers) + { + std::string const& component = entry.first; + if (component != currentComponent) + { + // 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); + + currentComponent = component; + } + + std::string const& layer = entry.second; + QTreeWidgetItem* layerItem = new QTreeWidgetItem; + layerItem->setText(0, QString::fromStdString(layer)); + // A new item is visible by default + // TODO: Take into account the old state! + layerItem->setCheckState(0, Qt::Checked); + + currentItem->addChild(layerItem); + } } SoNode* ArVizWidgetController::getScene() diff --git a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h index a596a42da..b1d4e3c3a 100644 --- a/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h +++ b/source/RobotAPI/gui-plugins/ArViz/ArVizWidgetController.h @@ -100,10 +100,14 @@ namespace armarx public slots: /* QT slot declarations */ + void layerTreeChanged(QTreeWidgetItem* item, int column); signals: /* QT signal declarations */ + private: + void layersChanged(std::vector<viz::CoinLayerID> const& layers); + private: Ui::ArVizWidget widget; -- GitLab