From f5931bf680ae0958fc4aa9611f8b6264f5bf4183 Mon Sep 17 00:00:00 2001 From: benjinne Date: Tue, 1 Mar 2022 20:52:33 -0500 Subject: [PATCH 1/3] Naive subtree support: by allowing save to parent tree. This doesn't load subtrees or save subtrees. Manual local import statements are persisted --- bt_editor/mainwindow.cpp | 62 +++++++++++++++++++++++++++++++++++++--- bt_editor/mainwindow.h | 2 ++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/bt_editor/mainwindow.cpp b/bt_editor/mainwindow.cpp index ad8a8ed9..1e498372 100644 --- a/bt_editor/mainwindow.cpp +++ b/bt_editor/mainwindow.cpp @@ -335,6 +335,20 @@ void MainWindow::loadFromXML(const QString& xml_text) const QSignalBlocker blocker( currentTabInfo() ); + for (auto include_path = document_root.firstChildElement("include"); + !include_path.isNull(); + include_path = include_path.nextSiblingElement("include")) + { + if(include_path.hasAttribute("path")) + { + auto path = include_path.attribute("path"); + if(!path.isNull()){ + _includes.push_back(path.toStdString().c_str()); + } + } + + } + for (auto bt_root = document_root.firstChildElement("BehaviorTree"); !bt_root.isNull(); bt_root = bt_root.nextSiblingElement("BehaviorTree")) @@ -450,11 +464,35 @@ QString MainWindow::saveToXML() const root.setAttribute("main_tree_to_execute", _main_tree.toStdString().c_str()); } + if(!_includes.empty()) + { + root.appendChild( doc.createComment(COMMENT_SEPARATOR) ); + } + + for (std::string path : _includes) + { + QDomElement include_element = doc.createElement("include"); + include_element.setAttribute("path", path.c_str()); + root.appendChild(include_element); + } + for (auto& it: _tab_info) { auto& container = it.second; auto scene = container->scene(); + //if only one node, and it's a subtree don't save + if(container->scene()->nodes().size() == 1) + { + auto node = container->scene()->nodes().begin(); + auto model = dynamic_cast(node->second->nodeDataModel()); + auto type = model->nodeType(); + if(type == NodeType::SUBTREE) + { + continue; + } + } + auto abs_tree = BuildTreeFromScene(container->scene()); auto abs_root = abs_tree.rootNode(); if( abs_root->children_index.size() == 1 && @@ -616,10 +654,24 @@ void MainWindow::on_actionSave_triggered() auto& container = it.second; if( !container->containsValidTree() ) { - QMessageBox::warning(this, tr("Oops!"), - tr("Malformed behavior tree. File can not be saved"), - QMessageBox::Cancel); - return; + // get first node type + auto node = container->scene()->nodes().begin(); + auto model = dynamic_cast(node->second->nodeDataModel()); + auto type = model->nodeType(); + // if only one node, and it's a subtree continue saving + if(container->scene()->nodes().size() == 1 && type == NodeType::SUBTREE) + { + QMessageBox::warning(this, tr("Oops!"), + tr("A subtree is empty. Make sure it's included!"), + QMessageBox::Cancel); + } + else + { + QMessageBox::warning(this, tr("Oops!"), + tr("Malformed behavior tree. File can not be saved"), + QMessageBox::Cancel); + return; + } } } @@ -1227,6 +1279,8 @@ void MainWindow::onTreeNodeEdited(QString prev_ID, QString new_ID) void MainWindow::onActionClearTriggered(bool create_new) { + _includes.clear(); + for (auto& it: _tab_info) { it.second->clearScene(); diff --git a/bt_editor/mainwindow.h b/bt_editor/mainwindow.h index ebfd2c70..f1562c75 100644 --- a/bt_editor/mainwindow.h +++ b/bt_editor/mainwindow.h @@ -181,6 +181,8 @@ private slots: std::map _tab_info; + std::vector _includes; + std::mutex _mutex; std::deque _undo_stack; From 886b2d3de5d942cd37a5d411859e293464608c2e Mon Sep 17 00:00:00 2001 From: benjinne Date: Thu, 3 Mar 2022 15:23:44 -0500 Subject: [PATCH 2/3] subtree support: add ros_pkg attribute --- bt_editor/mainwindow.cpp | 28 ++++++++++++++++++---------- bt_editor/mainwindow.h | 3 ++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/bt_editor/mainwindow.cpp b/bt_editor/mainwindow.cpp index 1e498372..27909e9b 100644 --- a/bt_editor/mainwindow.cpp +++ b/bt_editor/mainwindow.cpp @@ -339,14 +339,16 @@ void MainWindow::loadFromXML(const QString& xml_text) !include_path.isNull(); include_path = include_path.nextSiblingElement("include")) { + std::array pkg_path; + if(include_path.hasAttribute("ros_pkg")) + { + pkg_path[0] = include_path.attribute("ros_pkg").toStdString().c_str(); + } if(include_path.hasAttribute("path")) { - auto path = include_path.attribute("path"); - if(!path.isNull()){ - _includes.push_back(path.toStdString().c_str()); - } + pkg_path[1] = include_path.attribute("path").toStdString().c_str(); } - + _includes.push_back(pkg_path); } for (auto bt_root = document_root.firstChildElement("BehaviorTree"); @@ -468,11 +470,15 @@ QString MainWindow::saveToXML() const { root.appendChild( doc.createComment(COMMENT_SEPARATOR) ); } - - for (std::string path : _includes) + + for (std::array pkg_path : _includes) { QDomElement include_element = doc.createElement("include"); - include_element.setAttribute("path", path.c_str()); + if(!pkg_path[0].empty()) + { + include_element.setAttribute("ros_pkg", pkg_path[0].c_str()); + } + include_element.setAttribute("path", pkg_path[1].c_str()); root.appendChild(include_element); } @@ -662,8 +668,9 @@ void MainWindow::on_actionSave_triggered() if(container->scene()->nodes().size() == 1 && type == NodeType::SUBTREE) { QMessageBox::warning(this, tr("Oops!"), - tr("A subtree is empty. Make sure it's included!"), - QMessageBox::Cancel); + tr("A subtree is empty. Save anyways? \n" + "Sub-trees imported with are allowed to be empty"), + QMessageBox::Ok); } else { @@ -700,6 +707,7 @@ void MainWindow::on_actionSave_triggered() if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << xml_text << endl; + file.close(); } directory_path = QFileInfo(fileName).absolutePath(); diff --git a/bt_editor/mainwindow.h b/bt_editor/mainwindow.h index f1562c75..140b4094 100644 --- a/bt_editor/mainwindow.h +++ b/bt_editor/mainwindow.h @@ -181,7 +181,8 @@ private slots: std::map _tab_info; - std::vector _includes; + // 1st string is ros_pkg (empty if ros isn't used), 2nd is path + std::vector> _includes; std::mutex _mutex; From 1e53c4c8fcba85812057aec3b889cd88ab5c8a8c Mon Sep 17 00:00:00 2001 From: benjinne Date: Thu, 3 Mar 2022 15:24:37 -0500 Subject: [PATCH 3/3] XML: save attributes in order requested, not alphabetically --- bt_editor/mainwindow.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/bt_editor/mainwindow.cpp b/bt_editor/mainwindow.cpp index 27909e9b..dfa6f444 100644 --- a/bt_editor/mainwindow.cpp +++ b/bt_editor/mainwindow.cpp @@ -600,15 +600,9 @@ void MainWindow::streamElementAttributes(QXmlStreamWriter &stream, const QDomEle for (int i = 0; i < attributes_map.count(); ++i) { - auto attribute = attributes_map.item(i); - attributes.insert(attribute.nodeName(), attribute.nodeValue()); - } - - auto i = attributes.constBegin(); - while (i != attributes.constEnd()) - { - stream.writeAttribute(i.key(), i.value()); - ++i; + QString name = attributes_map.item(i).nodeName(); + QString value = attributes_map.item(i).nodeValue(); + stream.writeAttribute(name, value); } } }