From 05aa0912ed63010cf6325994d57640b642f7fe8a Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 26 Aug 2019 10:50:56 -0300 Subject: [PATCH 1/6] Make viewer capable to load rules files and tree visualization of phases --- .../visualizer/layer_selection_widget.cc | 55 ++++++++++++++++- .../visualizer/layer_selection_widget.hh | 15 ++++- .../visualizer/maliput_viewer_model.cc | 14 +++-- .../visualizer/maliput_viewer_model.hh | 60 +++++++++++++++++-- .../visualizer/maliput_viewer_widget.cc | 43 ++++++++----- .../visualizer/maliput_viewer_widget.hh | 9 ++- .../visualizer/rules_visualizer_widget.cc | 56 +++++++++++++++-- .../visualizer/rules_visualizer_widget.hh | 29 +++++++-- 8 files changed, 244 insertions(+), 37 deletions(-) diff --git a/delphyne-gui/visualizer/layer_selection_widget.cc b/delphyne-gui/visualizer/layer_selection_widget.cc index 03eb0d0c..4010dbe3 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.cc +++ b/delphyne-gui/visualizer/layer_selection_widget.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include using namespace delphyne; @@ -163,6 +164,9 @@ MaliputFileSelectionWidget::MaliputFileSelectionWidget(QWidget* parent) : QWidge this->Build(); // Connects all the check box events. QObject::connect(this->loadButton, SIGNAL(released()), this, SLOT(onLoadButtonPressed())); + QObject::connect(this->roadRulebookButton, SIGNAL(released()), this, SLOT(onRoadRulebookButtonPressed())); + QObject::connect(this->trafficLightRulesButton, SIGNAL(released()), this, SLOT(onTrafficLightrulesButtonPressed())); + QObject::connect(this->phaseRingButton, SIGNAL(released()), this, SLOT(onPhaseRingButtonPressed())); } /////////////////////////////////////////////////////// @@ -177,20 +181,67 @@ void MaliputFileSelectionWidget::SetFileNameLabel(const std::string& fileName) { void MaliputFileSelectionWidget::onLoadButtonPressed() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Maliput XODR or YAML"), QString::fromStdString(this->fileDialogOpenPath), - tr("XODR Files (*.XODR);;YAML files (*.YAML)")); + tr("XODR Files (*.xodr);;YAML files (*.yaml)")); if (!fileName.isEmpty()) { this->fileDialogOpenPath = fileName.toStdString(); } - emit maliputFileChanged(fileName.toStdString()); + emit maliputFileChanged(fileName.toStdString(), this->roadRulebookLineEdit->text().toStdString(), + this->trafficLightRulesLineEdit->text().toStdString(), + this->phaseRingLineEdit->text().toStdString()); +} + +void MaliputFileSelectionWidget::onRoadRulebookButtonPressed() { + QString fileName = QFileDialog::getOpenFileName( + this, tr("Open Road Rulebook YAML"), QString::fromStdString(this->fileDialogOpenPath), tr("YAML files (*.yaml)")); + this->roadRulebookLineEdit->setText(fileName); +} + +void MaliputFileSelectionWidget::onTrafficLightrulesButtonPressed() { + QString fileName = + QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook YAML"), + QString::fromStdString(this->fileDialogOpenPath), tr("YAML files (*.yaml)")); + this->trafficLightRulesLineEdit->setText(fileName); +} + +void MaliputFileSelectionWidget::onPhaseRingButtonPressed() { + QString fileName = + QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook YAML"), + QString::fromStdString(this->fileDialogOpenPath), tr("YAML files (*.yaml)")); + this->phaseRingLineEdit->setText(fileName); } /////////////////////////////////////////////////////// void MaliputFileSelectionWidget::Build() { this->loadButton = new QPushButton("Load", this); this->fileNameLabel = new QLabel("", this); + this->roadRulebookLineEdit = new QLineEdit(this); + this->trafficLightRulesLineEdit = new QLineEdit(this); + this->phaseRingLineEdit = new QLineEdit(this); + this->roadRulebookButton = new QPushButton("Select road rulebook", this); + this->trafficLightRulesButton = new QPushButton("Select traffic lights book", this); + this->phaseRingButton = new QPushButton("Select phase ring book", this); + + QWidget* roadRulebookWidgetContainer = new QWidget(this); + QHBoxLayout* roadRulebookLayout = new QHBoxLayout(roadRulebookWidgetContainer); + roadRulebookLayout->addWidget(roadRulebookLineEdit); + roadRulebookLayout->addWidget(roadRulebookButton); + + QWidget* trafficLightRulebookWidgetContainer = new QWidget(this); + QHBoxLayout* trafficLightRulesLayout = new QHBoxLayout(trafficLightRulebookWidgetContainer); + trafficLightRulesLayout->addWidget(trafficLightRulesLineEdit); + trafficLightRulesLayout->addWidget(trafficLightRulesButton); + + QWidget* phaseRingWidgetContainer = new QWidget(this); + QHBoxLayout* phaseRingLayout = new QHBoxLayout(phaseRingWidgetContainer); + phaseRingLayout->addWidget(phaseRingLineEdit); + phaseRingLayout->addWidget(phaseRingButton); auto* layout = new QVBoxLayout(this); layout->addWidget(fileNameLabel); layout->addWidget(loadButton); + layout->addWidget(roadRulebookWidgetContainer); + layout->addWidget(trafficLightRulebookWidgetContainer); + layout->addWidget(phaseRingWidgetContainer); + this->setLayout(layout); } diff --git a/delphyne-gui/visualizer/layer_selection_widget.hh b/delphyne-gui/visualizer/layer_selection_widget.hh index 932b1d81..01067ae9 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.hh +++ b/delphyne-gui/visualizer/layer_selection_widget.hh @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -95,7 +96,13 @@ class MaliputFileSelectionWidget : public QWidget { void onLoadButtonPressed(); signals: - void maliputFileChanged(const std::string& filePath); + void maliputFileChanged(const std::string& filePath, const std::string& roadRulebookFilePath, + const std::string& trafficLightRulesFilePath, const std::string& phaseRingFilePath); + + private slots: + void onRoadRulebookButtonPressed(); + void onTrafficLightrulesButtonPressed(); + void onPhaseRingButtonPressed(); private: /// \brief Builds the GUI with a button to load a file dialog and a label to @@ -104,6 +111,12 @@ class MaliputFileSelectionWidget : public QWidget { QPushButton* loadButton{nullptr}; QLabel* fileNameLabel{nullptr}; + QPushButton* roadRulebookButton{nullptr}; + QPushButton* trafficLightRulesButton{nullptr}; + QPushButton* phaseRingButton{nullptr}; + QLineEdit* roadRulebookLineEdit{nullptr}; + QLineEdit* trafficLightRulesLineEdit{nullptr}; + QLineEdit* phaseRingLineEdit{nullptr}; std::string fileDialogOpenPath{}; }; diff --git a/delphyne-gui/visualizer/maliput_viewer_model.cc b/delphyne-gui/visualizer/maliput_viewer_model.cc index edad063b..b346fcf0 100644 --- a/delphyne-gui/visualizer/maliput_viewer_model.cc +++ b/delphyne-gui/visualizer/maliput_viewer_model.cc @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -301,11 +303,12 @@ maliput::api::rules::RoadRulebook::QueryResults RoadNetworkQuery::FindRulesFor(c } ///////////////////////////////////////////////// -bool MaliputViewerModel::Load(const std::string& _maliputFilePath) { +bool MaliputViewerModel::Load(const std::string& _maliputFilePath, const std::string& _roadRulebookFilePath, + const std::string& _trafficLightBookFilePath, const std::string& _phaseRingFilePath) { this->Clear(); ignmsg << "About to load [" << _maliputFilePath << "] maliput file." << std::endl; - LoadRoadGeometry(_maliputFilePath); + LoadRoadGeometry(_maliputFilePath, _roadRulebookFilePath, _trafficLightBookFilePath, _phaseRingFilePath); const maliput::api::RoadGeometry* rg = roadGeometry == nullptr ? roadNetwork->road_geometry() : roadGeometry.get(); ignmsg << "Loaded [" << _maliputFilePath << "] maliput file." << std::endl; ignmsg << "Loading RoadGeometry meshes of " << rg->id().string() << std::endl; @@ -338,7 +341,9 @@ const std::map>& MaliputViewerModel::M const std::map>& MaliputViewerModel::Labels() const { return this->labels; } ///////////////////////////////////////////////// -void MaliputViewerModel::LoadRoadGeometry(const std::string& _maliputFilePath) { +void MaliputViewerModel::LoadRoadGeometry(const std::string& _maliputFilePath, const std::string& _roadRulebookFilePath, + const std::string& _trafficLightBookFilePath, + const std::string& _phaseRingFilePath) { std::ifstream fileStream(_maliputFilePath); if (!fileStream.is_open()) { throw std::runtime_error(_maliputFilePath + " doesn't exist or can't be opened."); @@ -348,7 +353,8 @@ void MaliputViewerModel::LoadRoadGeometry(const std::string& _maliputFilePath) { std::getline(fileStream, line); if (line.find("") != std::string::npos) { this->roadNetwork = delphyne::roads::CreateMalidriveFromFile( - _maliputFilePath.substr(_maliputFilePath.find_last_of("/") + 1), _maliputFilePath); + _maliputFilePath.substr(_maliputFilePath.find_last_of("/") + 1), _maliputFilePath, _roadRulebookFilePath, + _trafficLightBookFilePath, _phaseRingFilePath); return; } else if (line.find("maliput_multilane_builder:") != std::string::npos) { this->roadGeometry = maliput::multilane::LoadFile(maliput::multilane::BuilderFactory(), _maliputFilePath); diff --git a/delphyne-gui/visualizer/maliput_viewer_model.hh b/delphyne-gui/visualizer/maliput_viewer_model.hh index b3ffca5b..91b3c612 100644 --- a/delphyne-gui/visualizer/maliput_viewer_model.hh +++ b/delphyne-gui/visualizer/maliput_viewer_model.hh @@ -175,7 +175,9 @@ class MaliputViewerModel { /// Gets the file path from GlobalAttributes and loads the RoadGeometry into /// memory. Then, converts it into a map of meshes, loading each mesh material /// information. Meshes that are not available, are set to kDisabled. - bool Load(const std::string& _maliputFilePath); + bool Load(const std::string& _maliputFilePath, const std::string& _roadRulebookFilePath = std::string(), + const std::string& _trafficLightBook = std::string(), + const std::string& _phaseRingFilePath = std::string()); /// \brief Getter of the map of meshes. /// \return The map of meshes. @@ -230,7 +232,11 @@ class MaliputViewerModel { /// \return rules separated by brackets. /// Ex: [Right of way Rule]\n. template - StringType GetRulesOfLane(const std::string& _laneId) const; + StringType GetRulesOfLane(const std::string& _phaseRingId, const std::string& _phaseId, + const std::string& _laneId) const; + + template + std::unordered_map> GetPhaseRings() const; private: /// \brief Loads a maliput RoadGeometry of multilane from @@ -240,7 +246,8 @@ class MaliputViewerModel { /// If the key doesn't exist in the file, it's not valid. /// Otherwise the correct loader will be called to parse the file. /// \param _maliputFilePath The YAML file path to parse. - void LoadRoadGeometry(const std::string& _maliputFilePath); + void LoadRoadGeometry(const std::string& _maliputFilePath, const std::string& _roadRulebookFilePath, + const std::string& _trafficLightBookFilePath, const std::string& _phaseRingFilePath); /// \brief Converts @p _geoMeshes into a /// std::map> @@ -281,6 +288,10 @@ class MaliputViewerModel { template StringType GetDirectionUsageRules(const maliput::api::LaneId& _laneId) const; + template + StringType GetPhaseRightOfWayRules(const maliput::api::rules::PhaseRing::Id& _phaseRingId, + const maliput::api::rules::Phase::Id& _phaseId) const; + // To support both malidrive and multilane files, we have both. roadNetwork // has a pointer to a RoadGeometry. @@ -327,7 +338,31 @@ ContainerType MaliputViewerModel::GetAllLaneIds() const { } template -StringType MaliputViewerModel::GetRulesOfLane(const std::string& _laneId) const { +std::unordered_map> MaliputViewerModel::GetPhaseRings() const { + if (this->roadNetwork == nullptr) { + return std::unordered_map>(); + } + std::unordered_map> phase_rings; + const maliput::api::rules::PhaseRingBook* phase_ring_book = this->roadNetwork->phase_ring_book(); + std::vector phase_ring_ids = phase_ring_book->GetPhaseRings(); + phase_rings.reserve(phase_ring_ids.size()); + for (const auto& phase_ring_id : phase_ring_ids) { + drake::optional phase_ring = phase_ring_book->GetPhaseRing(phase_ring_id); + DELPHYNE_DEMAND(phase_ring.has_value()); + const std::unordered_map& phases = + phase_ring.value().phases(); + std::vector phase_ids; + phase_ids.reserve(phases.size()); + std::transform(phases.begin(), phases.end(), std::back_inserter(phase_ids), + [](const auto& key_value) { return StringType(key_value.first.string().c_str()); }); + phase_rings[phase_ring_id.string()] = std::move(phase_ids); + } + return phase_rings; +} + +template +StringType MaliputViewerModel::GetRulesOfLane(const std::string& _phaseRingId, const std::string& _phaseId, + const std::string& _laneId) const { if (this->roadNetwork == nullptr) { return StringType("There are no rules for this road"); } @@ -337,6 +372,14 @@ StringType MaliputViewerModel::GetRulesOfLane(const std::string& _laneId) const StringType rules = "[Right of way rules]\n" + GetRightOfWayRules(laneSRange) + "\n" + "[Max speed limit rules]\n" + GetMaxSpeedLimitRules(id) + "\n" + "[Direction usage rules]\n" + GetDirectionUsageRules(id) + "\n"; + + if (!_phaseRingId.empty() && !_phaseId.empty()) { + rules += "[Right of way rules by phase ring id and phase id]\n" + + GetPhaseRightOfWayRules(maliput::api::rules::PhaseRing::Id(_phaseRingId), + maliput::api::rules::Phase::Id(_phaseId)) + + "\n"; + } + return rules; } @@ -364,6 +407,15 @@ StringType MaliputViewerModel::GetDirectionUsageRules(const maliput::api::LaneId return StringType(directionUsageRules.str().c_str()); } +template +StringType MaliputViewerModel::GetPhaseRightOfWayRules(const maliput::api::rules::PhaseRing::Id& _phaseRingId, + const maliput::api::rules::Phase::Id& _phaseId) const { + std::ostringstream rightOfWayRulesPhaseRing; + RoadNetworkQuery query(&rightOfWayRulesPhaseRing, roadNetwork.get()); + query.GetPhaseRightOfWay(_phaseRingId, _phaseId); + return StringType(rightOfWayRulesPhaseRing.str().c_str()); +} + } // namespace gui } // namespace delphyne diff --git a/delphyne-gui/visualizer/maliput_viewer_widget.cc b/delphyne-gui/visualizer/maliput_viewer_widget.cc index ebfdacf1..ecffd930 100644 --- a/delphyne-gui/visualizer/maliput_viewer_widget.cc +++ b/delphyne-gui/visualizer/maliput_viewer_widget.cc @@ -32,8 +32,10 @@ MaliputViewerWidget::MaliputViewerWidget(QWidget* parent) : Plugin() { QObject::connect(this->labelSelectionWidget, SIGNAL(valueChanged(const std::string&, bool)), this, SLOT(OnTextLabelChanged(const std::string&, bool))); - QObject::connect(this->maliputFileSelectionWidget, SIGNAL(maliputFileChanged(const std::string&)), this, - SLOT(OnNewMultilaneFile(const std::string&))); + QObject::connect( + this->maliputFileSelectionWidget, + SIGNAL(maliputFileChanged(const std::string&, const std::string&, const std::string&, const std::string&)), this, + SLOT(OnNewMultilaneFile(const std::string&, const std::string&, const std::string&, const std::string&))); } ///////////////////////////////////////////////// @@ -53,7 +55,9 @@ void MaliputViewerWidget::OnTextLabelChanged(const std::string& key, bool newVal } ///////////////////////////////////////////////// -void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath) { +void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath, const std::string& roadRulebookFilePath, + const std::string& trafficLightRulesFilePath, + const std::string& phaseRingFilePath) { if (filePath.empty()) { return; } @@ -63,12 +67,13 @@ void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath) { // Loads the new file. this->model = std::make_unique(); - this->model->Load(filePath); - this->rulesVisualizerWiget->ClearText(); - this->rulesVisualizerWiget->ClearLaneList(); + this->model->Load(filePath, roadRulebookFilePath, trafficLightRulesFilePath, phaseRingFilePath); + this->rulesVisualizerWidget->ClearText(); + this->rulesVisualizerWidget->ClearLaneList(); + this->rulesVisualizerWidget->ConstructPhaseRingTree(this->model->GetPhaseRings()); const auto lane_ids = this->model->GetAllLaneIds>(); - for (size_t i = 0; i < lane_ids.size(); ++i) { - this->rulesVisualizerWiget->AddLaneId(lane_ids[i]); + for (const QString& lane_id : lane_ids) { + this->rulesVisualizerWidget->AddLaneId(lane_id); } this->renderWidget->RenderArrow(); @@ -93,8 +98,12 @@ void MaliputViewerWidget::OnVisualClicked(ignition::rendering::RayQueryResult ra if (lane) { const std::string& lane_id = lane->id().string(); ignmsg << "Clicked lane ID: " << lane_id << "\n"; - OnRulesForLaneRequested(QString(lane_id.c_str())); this->renderWidget->Outline(lane); + PhaseRingPhaseIds phaseRingIdAndPhaseIdSelected = this->rulesVisualizerWidget->GetSelectedPhaseRingAndPhaseId(); + emit this->rulesVisualizerWidget->ReceiveRules( + QString(lane_id.c_str()), + this->model->GetRulesOfLane(phaseRingIdAndPhaseIdSelected.phase_ring_id.toStdString(), + phaseRingIdAndPhaseIdSelected.phase_id.toStdString(), lane_id)); this->renderWidget->PutArrowAt(rayResult.distance, rayResult.point); this->renderWidget->SetArrowVisibility(true); } else { @@ -121,19 +130,18 @@ void MaliputViewerWidget::BuildGUI() { this->layerSelectionWidget = new LayerSelectionWidget(this); this->labelSelectionWidget = new LabelSelectionWidget(this); this->renderWidget = new RenderMaliputWidget(this); - this->rulesVisualizerWiget = new RulesVisualizerWidget(this); + this->rulesVisualizerWidget = new RulesVisualizerWidget(this); QObject::connect(this->renderWidget, SIGNAL(VisualClicked(ignition::rendering::RayQueryResult)), this, SLOT(OnVisualClicked(ignition::rendering::RayQueryResult))); - QObject::connect(this->rulesVisualizerWiget, SIGNAL(RequestRulesForLaneId(QString)), this, - SLOT(OnRulesForLaneRequested(QString))); + QObject::connect(this->rulesVisualizerWidget, SIGNAL(RequestRules()), this, SLOT(OnRulesForLaneRequested())); auto verticalLayout = new QVBoxLayout(this); verticalLayout->addWidget(this->maliputFileSelectionWidget); verticalLayout->addWidget(this->layerSelectionWidget); verticalLayout->addWidget(this->labelSelectionWidget); - verticalLayout->addWidget(this->rulesVisualizerWiget); + verticalLayout->addWidget(this->rulesVisualizerWidget); auto controlGroup = new QGroupBox("Control panel", this); controlGroup->setLayout(verticalLayout); controlGroup->setSizePolicy(QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed); @@ -160,9 +168,14 @@ void MaliputViewerWidget::paintEvent(QPaintEvent* _e) { this->renderWidget->paintEvent(_e); } -void MaliputViewerWidget::OnRulesForLaneRequested(QString laneId) { +void MaliputViewerWidget::OnRulesForLaneRequested() { this->renderWidget->Outline(this->model->GetLaneFromId(laneId.toStdString())); - emit this->rulesVisualizerWiget->ReceiveRules(laneId, this->model->GetRulesOfLane(laneId.toStdString())); + PhaseRingPhaseIds phaseRingPhaseIds = this->rulesVisualizerWidget->GetSelectedPhaseRingAndPhaseId(); + emit this->rulesVisualizerWidget->ReceiveRules( + this->rulesVisualizerWidget->GetSelectedLaneId(), + this->model->GetRulesOfLane(phaseRingPhaseIds.phase_ring_id.toStdString(), + phaseRingPhaseIds.phase_id.toStdString(), + this->rulesVisualizerWidget->GetSelectedLaneId().toStdString())); } ///////////////////////////////////////////////// diff --git a/delphyne-gui/visualizer/maliput_viewer_widget.hh b/delphyne-gui/visualizer/maliput_viewer_widget.hh index cf384f02..07c0b61f 100644 --- a/delphyne-gui/visualizer/maliput_viewer_widget.hh +++ b/delphyne-gui/visualizer/maliput_viewer_widget.hh @@ -54,13 +54,16 @@ class MaliputViewerWidget : public ignition::gui::Plugin { /// \brief Clears the visualizer, loads the new set of meshes and text labels. /// \param filePath The path to the YAML file to load and visualize. - void OnNewMultilaneFile(const std::string& filePath); + void OnNewMultilaneFile(const std::string& filePath, const std::string& roadRulebookFilePath, + const std::string& trafficLightRulesFilePath, const std::string& phaseRingFilePath); /// \brief Prints the ID of the lane if any was selected. /// \param[in] rayResult Ray that contains the point where the click hit. void OnVisualClicked(ignition::rendering::RayQueryResult rayResult); - void OnRulesForLaneRequested(QString laneId); + /// \brief Emits rulesVisualizerWidget's ReceiveRules signal with all the rules related + /// to the selected lane, phase ring and phase if any. + void OnRulesForLaneRequested(); protected: /// \brief Overridden method to receive Qt paint event. @@ -91,7 +94,7 @@ class MaliputViewerWidget : public ignition::gui::Plugin { RenderMaliputWidget* renderWidget{nullptr}; /// \brief Rules visualizer widget. - RulesVisualizerWidget* rulesVisualizerWiget{nullptr}; + RulesVisualizerWidget* rulesVisualizerWidget{nullptr}; /// \brief Model that holds the meshes and the visualization status. std::unique_ptr model{}; diff --git a/delphyne-gui/visualizer/rules_visualizer_widget.cc b/delphyne-gui/visualizer/rules_visualizer_widget.cc index 48f7126e..02cf789e 100644 --- a/delphyne-gui/visualizer/rules_visualizer_widget.cc +++ b/delphyne-gui/visualizer/rules_visualizer_widget.cc @@ -2,11 +2,12 @@ #include "rules_visualizer_widget.hh" -#include - #include #include +#include #include +#include +#include #include namespace delphyne { @@ -18,12 +19,16 @@ RulesVisualizerWidget::RulesVisualizerWidget(QWidget* parent) : QWidget(parent) this->rules_label = new QLabel("Rules", this); this->lanes_label = new QLabel("Lanes", this); this->lanes_list = new QListWidget(this); + this->phase_tree = new QTreeWidget(this); + this->phase_tree->setColumnCount(1); + this->phase_tree->setHeaderLabel("Phase ring"); QObject::connect(this->lanes_list, SIGNAL(itemClicked(QListWidgetItem*)), this, - SLOT(OnItemClicked(QListWidgetItem*))); + SLOT(OnLaneItemClicked(QListWidgetItem*))); QObject::connect(this, SIGNAL(ReceiveRules(QString, QString)), this, SLOT(OnRulesReceived(QString, QString))); + layout->addWidget(this->phase_tree); layout->addWidget(this->lanes_label); layout->addWidget(this->lanes_list); layout->addWidget(this->rules_label); @@ -52,7 +57,50 @@ void RulesVisualizerWidget::ClearText() { this->rules_log_text_browser->clear(); } -void RulesVisualizerWidget::OnItemClicked(QListWidgetItem* item) { emit RequestRulesForLaneId(item->text()); } +void RulesVisualizerWidget::ConstructPhaseRingTree( + std::unordered_map>&& phases_per_ring) { + DELPHYNE_DEMAND(this->phase_tree != nullptr); + this->phase_tree->clear(); + for (auto& phase_ring_n_phases : phases_per_ring) { + QTreeWidgetItem* phase_ring_item = new QTreeWidgetItem(this->phase_tree); + phase_ring_item->setText(0, QString::fromStdString(phase_ring_n_phases.first)); + phase_ring_item->setFlags(phase_ring_item->flags() & (~Qt::ItemIsSelectable)); + this->phase_tree->addTopLevelItem(phase_ring_item); + std::vector& phases = phase_ring_n_phases.second; + for (size_t i = 0; i < phases.size(); ++i) { + QTreeWidgetItem* phase_item = new QTreeWidgetItem(); + phase_item->setText(0, std::move(phases[i])); + phase_ring_item->addChild(phase_item); + } + } +} + +QString RulesVisualizerWidget::GetSelectedLaneId() const { + QListWidgetItem* currentItem = this->lanes_list->currentItem(); + if (currentItem) { + return currentItem->text(); + } + return QString(); +} + +PhaseRingPhaseIds RulesVisualizerWidget::GetSelectedPhaseRingAndPhaseId() const { + PhaseRingPhaseIds phase_ring_phase_ids; + QList selected_items = this->phase_tree->selectedItems(); + if (selected_items.size() > 0) { + // We asume that we only have 1 level of depth in the tree. Example: + // |_PhaseRing1 + // |__Phase1 + // |__Phase2 + // |_PhaseRing2 + // |__Phase1 + QTreeWidgetItem* parent = selected_items[0]->parent(); + phase_ring_phase_ids.phase_ring_id = parent->text(0); + phase_ring_phase_ids.phase_id = selected_items[0]->text(0); + } + return phase_ring_phase_ids; +} + +void RulesVisualizerWidget::OnLaneItemClicked(QListWidgetItem* item) { emit RequestRules(); } void RulesVisualizerWidget::OnRulesReceived(QString lane_id, QString rules) { QList items = this->lanes_list->findItems(lane_id, Qt::MatchExactly); diff --git a/delphyne-gui/visualizer/rules_visualizer_widget.hh b/delphyne-gui/visualizer/rules_visualizer_widget.hh index 9afdb5d1..daca92e3 100644 --- a/delphyne-gui/visualizer/rules_visualizer_widget.hh +++ b/delphyne-gui/visualizer/rules_visualizer_widget.hh @@ -8,11 +8,19 @@ #include #include #include +#include #include namespace delphyne { namespace gui { +/// \brief Struct containing the phase ring id and a phase id that belongs to the phase ring. +/// An empty QString represents no item selection. +struct PhaseRingPhaseIds { + QString phase_ring_id; + QString phase_id; +}; + /// \class RulesVisualizerWidget /// \brief A class that implements a simple visualizer for loaded lanes /// with their rules associated. @@ -34,19 +42,31 @@ class RulesVisualizerWidget : public QWidget { void ClearLaneList(); /// \brief Clear the text in TextBrowser. void ClearText(); + /// \brief Construct the tree for the given phase_ring. + /// \param[in] phases Dictionary containing all the phases for all the possible phase rings of a road geometry. + /// \warning phases will be moved. + void ConstructPhaseRingTree(std::unordered_map>&& phases); + + /// \brief Get the select lane id from the list widget. + /// \returns QString containing the selected lane id + QString GetSelectedLaneId() const; + + /// \brief Get the selected phase ring id and phase id from the TreeWidget. + /// \returns PhaseRingPhaseIds containing selected phase ring id and phase id (if any) + PhaseRingPhaseIds GetSelectedPhaseRingAndPhaseId() const; signals: /// \brief Signal used to request rules for a given lane id - void RequestRulesForLaneId(QString lane_id); + void RequestRules(); /// \brief Signal connected internally to handle rules for a given lane id void ReceiveRules(QString lane_id, QString rules); private slots: - /// \brief Slot connected when the user clicks an item from the ListWidget. - /// Emits RequestRulesForLaneId signal - void OnItemClicked(QListWidgetItem* item); + /// \brief Slot connected when the user clicks a lane item from the ListWidget. + /// Emits RequestRules signal + void OnLaneItemClicked(QListWidgetItem* item); /// \brief Slot connected to ReceiveRules signal. Clears the text browser /// and populates it with the rules for the requested lane id. void OnRulesReceived(QString lane_id, QString rules); @@ -56,6 +76,7 @@ class RulesVisualizerWidget : public QWidget { QLabel* rules_label{nullptr}; QListWidget* lanes_list{nullptr}; QTextBrowser* rules_log_text_browser{nullptr}; + QTreeWidget* phase_tree{nullptr}; }; } // namespace gui } // namespace delphyne From a62cb1af35dc5b767a2998019e23e4a2fe78f505 Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 29 Aug 2019 10:49:32 -0300 Subject: [PATCH 2/6] Keep file names of rules in line edits as placeholders --- .../visualizer/layer_selection_widget.cc | 23 +++++++++++++++++++ .../visualizer/layer_selection_widget.hh | 9 ++++++++ .../visualizer/maliput_viewer_widget.cc | 9 ++++---- .../visualizer/maliput_viewer_widget.hh | 4 ++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/delphyne-gui/visualizer/layer_selection_widget.cc b/delphyne-gui/visualizer/layer_selection_widget.cc index 4010dbe3..e53d3c4b 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.cc +++ b/delphyne-gui/visualizer/layer_selection_widget.cc @@ -177,6 +177,21 @@ void MaliputFileSelectionWidget::SetFileNameLabel(const std::string& fileName) { this->fileNameLabel->setText(QString::fromStdString(fileName)); } +void MaliputFileSelectionWidget::ClearLineEdits(bool keepOldTextsAsPlaceholders) { + if (keepOldTextsAsPlaceholders) { + this->roadRulebookLineEdit->setPlaceholderText(GetFileName(this->roadRulebookLineEdit->text())); + this->trafficLightRulesLineEdit->setPlaceholderText(GetFileName(this->trafficLightRulesLineEdit->text())); + this->phaseRingLineEdit->setPlaceholderText(GetFileName(this->phaseRingLineEdit->text())); + } else { + this->roadRulebookLineEdit->setPlaceholderText(QString()); + this->trafficLightRulesLineEdit->setPlaceholderText(QString()); + this->phaseRingLineEdit->setPlaceholderText(QString()); + } + this->roadRulebookLineEdit->clear(); + this->trafficLightRulesLineEdit->clear(); + this->phaseRingLineEdit->clear(); +} + /////////////////////////////////////////////////////// void MaliputFileSelectionWidget::onLoadButtonPressed() { QString fileName = QFileDialog::getOpenFileName(this, tr("Open Maliput XODR or YAML"), @@ -245,3 +260,11 @@ void MaliputFileSelectionWidget::Build() { this->setLayout(layout); } + +QString MaliputFileSelectionWidget::GetFileName(const QString& filePath) { + int lastIndexOf = filePath.lastIndexOf("/"); + if (lastIndexOf != -1) { + return filePath.mid(lastIndexOf + 1); + } + return QString(); +} diff --git a/delphyne-gui/visualizer/layer_selection_widget.hh b/delphyne-gui/visualizer/layer_selection_widget.hh index 01067ae9..056267fa 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.hh +++ b/delphyne-gui/visualizer/layer_selection_widget.hh @@ -92,6 +92,11 @@ class MaliputFileSelectionWidget : public QWidget { /// Sets @p fileName into the label to display the loaded file. void SetFileNameLabel(const std::string& fileName); + /// \brief Clear rules line edits. + /// \param[in] keepOldTextsAsPlaceholders Use the last file opened as a placeholder to remember the user which file + /// he used. + void ClearLineEdits(bool keepOldTextsAsPlaceholders = true); + public slots: void onLoadButtonPressed(); @@ -109,6 +114,10 @@ class MaliputFileSelectionWidget : public QWidget { /// display the name of the loaded file. void Build(); + /// \brief Get the file name for a given path. + /// \returns QString containing the file name or empty QString if file name couldn't be gathered from filePath + QString GetFileName(const QString& filePath); + QPushButton* loadButton{nullptr}; QLabel* fileNameLabel{nullptr}; QPushButton* roadRulebookButton{nullptr}; diff --git a/delphyne-gui/visualizer/maliput_viewer_widget.cc b/delphyne-gui/visualizer/maliput_viewer_widget.cc index ecffd930..7cc1487f 100644 --- a/delphyne-gui/visualizer/maliput_viewer_widget.cc +++ b/delphyne-gui/visualizer/maliput_viewer_widget.cc @@ -35,7 +35,7 @@ MaliputViewerWidget::MaliputViewerWidget(QWidget* parent) : Plugin() { QObject::connect( this->maliputFileSelectionWidget, SIGNAL(maliputFileChanged(const std::string&, const std::string&, const std::string&, const std::string&)), this, - SLOT(OnNewMultilaneFile(const std::string&, const std::string&, const std::string&, const std::string&))); + SLOT(OnNewRoadNetwork(const std::string&, const std::string&, const std::string&, const std::string&))); } ///////////////////////////////////////////////// @@ -55,9 +55,9 @@ void MaliputViewerWidget::OnTextLabelChanged(const std::string& key, bool newVal } ///////////////////////////////////////////////// -void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath, const std::string& roadRulebookFilePath, - const std::string& trafficLightRulesFilePath, - const std::string& phaseRingFilePath) { +void MaliputViewerWidget::OnNewRoadNetwork(const std::string& filePath, const std::string& roadRulebookFilePath, + const std::string& trafficLightRulesFilePath, + const std::string& phaseRingFilePath) { if (filePath.empty()) { return; } @@ -81,6 +81,7 @@ void MaliputViewerWidget::OnNewMultilaneFile(const std::string& filePath, const this->renderWidget->RenderLabels(this->model->Labels()); this->VisualizeFileName(filePath); + this->maliputFileSelectionWidget->ClearLineEdits(true); } ///////////////////////////////////////////////// diff --git a/delphyne-gui/visualizer/maliput_viewer_widget.hh b/delphyne-gui/visualizer/maliput_viewer_widget.hh index 07c0b61f..48cc25c6 100644 --- a/delphyne-gui/visualizer/maliput_viewer_widget.hh +++ b/delphyne-gui/visualizer/maliput_viewer_widget.hh @@ -54,8 +54,8 @@ class MaliputViewerWidget : public ignition::gui::Plugin { /// \brief Clears the visualizer, loads the new set of meshes and text labels. /// \param filePath The path to the YAML file to load and visualize. - void OnNewMultilaneFile(const std::string& filePath, const std::string& roadRulebookFilePath, - const std::string& trafficLightRulesFilePath, const std::string& phaseRingFilePath); + void OnNewRoadNetwork(const std::string& filePath, const std::string& roadRulebookFilePath, + const std::string& trafficLightRulesFilePath, const std::string& phaseRingFilePath); /// \brief Prints the ID of the lane if any was selected. /// \param[in] rayResult Ray that contains the point where the click hit. From a9dbe13a2af93508224d1497cc084c4084bfca94 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 2 Sep 2019 09:53:14 -0300 Subject: [PATCH 3/6] Address pr last comments --- .../visualizer/layer_selection_widget.cc | 21 +++++++++---------- .../visualizer/layer_selection_widget.hh | 4 ---- .../visualizer/rules_visualizer_widget.cc | 6 +++--- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/delphyne-gui/visualizer/layer_selection_widget.cc b/delphyne-gui/visualizer/layer_selection_widget.cc index e53d3c4b..968e6e77 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.cc +++ b/delphyne-gui/visualizer/layer_selection_widget.cc @@ -1,6 +1,7 @@ // Copyright 2018 Toyota Research Institute #include "layer_selection_widget.hh" +#include #include #include @@ -179,9 +180,15 @@ void MaliputFileSelectionWidget::SetFileNameLabel(const std::string& fileName) { void MaliputFileSelectionWidget::ClearLineEdits(bool keepOldTextsAsPlaceholders) { if (keepOldTextsAsPlaceholders) { - this->roadRulebookLineEdit->setPlaceholderText(GetFileName(this->roadRulebookLineEdit->text())); - this->trafficLightRulesLineEdit->setPlaceholderText(GetFileName(this->trafficLightRulesLineEdit->text())); - this->phaseRingLineEdit->setPlaceholderText(GetFileName(this->phaseRingLineEdit->text())); + std::string roadRulebookFileName = ignition::common::basename(this->roadRulebookLineEdit->text().toStdString()); + this->roadRulebookLineEdit->setPlaceholderText(QString::fromStdString(roadRulebookFileName)); + + std::string trafficLightFileName = + ignition::common::basename(this->trafficLightRulesLineEdit->text().toStdString()); + this->trafficLightRulesLineEdit->setPlaceholderText(QString::fromStdString(trafficLightFileName)); + + std::string phaseRingFileName = ignition::common::basename(this->phaseRingLineEdit->text().toStdString()); + this->phaseRingLineEdit->setPlaceholderText(QString::fromStdString(phaseRingFileName)); } else { this->roadRulebookLineEdit->setPlaceholderText(QString()); this->trafficLightRulesLineEdit->setPlaceholderText(QString()); @@ -260,11 +267,3 @@ void MaliputFileSelectionWidget::Build() { this->setLayout(layout); } - -QString MaliputFileSelectionWidget::GetFileName(const QString& filePath) { - int lastIndexOf = filePath.lastIndexOf("/"); - if (lastIndexOf != -1) { - return filePath.mid(lastIndexOf + 1); - } - return QString(); -} diff --git a/delphyne-gui/visualizer/layer_selection_widget.hh b/delphyne-gui/visualizer/layer_selection_widget.hh index 056267fa..5b7a5bcd 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.hh +++ b/delphyne-gui/visualizer/layer_selection_widget.hh @@ -114,10 +114,6 @@ class MaliputFileSelectionWidget : public QWidget { /// display the name of the loaded file. void Build(); - /// \brief Get the file name for a given path. - /// \returns QString containing the file name or empty QString if file name couldn't be gathered from filePath - QString GetFileName(const QString& filePath); - QPushButton* loadButton{nullptr}; QLabel* fileNameLabel{nullptr}; QPushButton* roadRulebookButton{nullptr}; diff --git a/delphyne-gui/visualizer/rules_visualizer_widget.cc b/delphyne-gui/visualizer/rules_visualizer_widget.cc index 02cf789e..e24cd0fd 100644 --- a/delphyne-gui/visualizer/rules_visualizer_widget.cc +++ b/delphyne-gui/visualizer/rules_visualizer_widget.cc @@ -76,9 +76,9 @@ void RulesVisualizerWidget::ConstructPhaseRingTree( } QString RulesVisualizerWidget::GetSelectedLaneId() const { - QListWidgetItem* currentItem = this->lanes_list->currentItem(); - if (currentItem) { - return currentItem->text(); + QListWidgetItem* selected_item = this->lanes_list->currentItem(); + if (selected_item) { + return selected_item->text(); } return QString(); } From 09e00d213a5850e3cc3876f2dc450c87f66548e7 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 2 Sep 2019 12:02:00 -0300 Subject: [PATCH 4/6] Make use of QFileInfo for file paths --- delphyne-gui/visualizer/layer_selection_widget.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/delphyne-gui/visualizer/layer_selection_widget.cc b/delphyne-gui/visualizer/layer_selection_widget.cc index 968e6e77..6a26fd83 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.cc +++ b/delphyne-gui/visualizer/layer_selection_widget.cc @@ -3,6 +3,7 @@ #include "layer_selection_widget.hh" #include +#include #include #include #include @@ -180,15 +181,14 @@ void MaliputFileSelectionWidget::SetFileNameLabel(const std::string& fileName) { void MaliputFileSelectionWidget::ClearLineEdits(bool keepOldTextsAsPlaceholders) { if (keepOldTextsAsPlaceholders) { - std::string roadRulebookFileName = ignition::common::basename(this->roadRulebookLineEdit->text().toStdString()); - this->roadRulebookLineEdit->setPlaceholderText(QString::fromStdString(roadRulebookFileName)); + QFileInfo ruleFile(this->roadRulebookLineEdit->text()); + this->roadRulebookLineEdit->setPlaceholderText(ruleFile.baseName()); - std::string trafficLightFileName = - ignition::common::basename(this->trafficLightRulesLineEdit->text().toStdString()); - this->trafficLightRulesLineEdit->setPlaceholderText(QString::fromStdString(trafficLightFileName)); + ruleFile.setFile(this->trafficLightRulesLineEdit->text()); + this->trafficLightRulesLineEdit->setPlaceholderText(ruleFile.baseName()); - std::string phaseRingFileName = ignition::common::basename(this->phaseRingLineEdit->text().toStdString()); - this->phaseRingLineEdit->setPlaceholderText(QString::fromStdString(phaseRingFileName)); + ruleFile.setFile(this->phaseRingLineEdit->text()); + this->phaseRingLineEdit->setPlaceholderText(ruleFile.baseName()); } else { this->roadRulebookLineEdit->setPlaceholderText(QString()); this->trafficLightRulesLineEdit->setPlaceholderText(QString()); From 396ce2e2dc852f1720d22c6da2eb209032c6157b Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 2 Sep 2019 14:47:05 -0300 Subject: [PATCH 5/6] Run clang format --- delphyne-gui/visualizer/layer_selection_widget.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delphyne-gui/visualizer/layer_selection_widget.cc b/delphyne-gui/visualizer/layer_selection_widget.cc index 6a26fd83..3bf2ca4e 100644 --- a/delphyne-gui/visualizer/layer_selection_widget.cc +++ b/delphyne-gui/visualizer/layer_selection_widget.cc @@ -3,8 +3,8 @@ #include "layer_selection_widget.hh" #include -#include #include +#include #include #include #include From 0a32fa42801157e0f03531e6d7d748c58e82087f Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 3 Sep 2019 14:07:28 -0300 Subject: [PATCH 6/6] Updated with upstream changes --- delphyne-gui/visualizer/maliput_viewer_widget.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/delphyne-gui/visualizer/maliput_viewer_widget.cc b/delphyne-gui/visualizer/maliput_viewer_widget.cc index 7cc1487f..b65cb4f0 100644 --- a/delphyne-gui/visualizer/maliput_viewer_widget.cc +++ b/delphyne-gui/visualizer/maliput_viewer_widget.cc @@ -170,13 +170,13 @@ void MaliputViewerWidget::paintEvent(QPaintEvent* _e) { } void MaliputViewerWidget::OnRulesForLaneRequested() { - this->renderWidget->Outline(this->model->GetLaneFromId(laneId.toStdString())); + const std::string lane_id = this->rulesVisualizerWidget->GetSelectedLaneId().toStdString(); + this->renderWidget->Outline(this->model->GetLaneFromId(lane_id)); PhaseRingPhaseIds phaseRingPhaseIds = this->rulesVisualizerWidget->GetSelectedPhaseRingAndPhaseId(); emit this->rulesVisualizerWidget->ReceiveRules( this->rulesVisualizerWidget->GetSelectedLaneId(), this->model->GetRulesOfLane(phaseRingPhaseIds.phase_ring_id.toStdString(), - phaseRingPhaseIds.phase_id.toStdString(), - this->rulesVisualizerWidget->GetSelectedLaneId().toStdString())); + phaseRingPhaseIds.phase_id.toStdString(), lane_id)); } /////////////////////////////////////////////////