-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make viewer capable to load rules files and tree visualization of phases #224
Conversation
311ba5f
to
bc5542d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass. We should get base PRs going.
} | ||
|
||
void MaliputFileSelectionWidget::onTrafficLightrulesButtonPressed() { | ||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi nit: consider
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook"), | |
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook YAML"), |
as you did above.
void MaliputFileSelectionWidget::onTrafficLightrulesButtonPressed() { | ||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook"), | ||
QString::fromStdString(this->fileDialogOpenPath), | ||
tr("All files (*.*)")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi shouldn't it be YAML files
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely sure if all files should be YAML to be honest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are, at least for the time being and the foreseeable future.
} | ||
|
||
void MaliputFileSelectionWidget::onPhaseRingButtonPressed() { | ||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi Phase Ring Rulebook YAML
?
void MaliputFileSelectionWidget::onPhaseRingButtonPressed() { | ||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Traffic Lights Rulebook"), | ||
QString::fromStdString(this->fileDialogOpenPath), | ||
tr("All files (*.*)")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi shouldn't it be YAML files
?
_maliputFilePath.substr(_maliputFilePath.find_last_of("/") + 1), _maliputFilePath); | ||
_maliputFilePath.substr(_maliputFilePath.find_last_of("/") + 1), _maliputFilePath, _roadRulebookFilePath, | ||
_trafficLightBookFilePath, _phaseRingFilePath); | ||
fileStream.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi there's no need to explicitly close the stream, its destructor will.
std::vector<QString>>&& phases_per_ring) { | ||
DELPHYNE_DEMAND(this->phase_tree != nullptr); | ||
this->phase_tree->clear(); | ||
ignerr << phases_per_ring.size() << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi leftover?
ignerr << phases_per_ring.size() << std::endl; | ||
for (auto& phase_ring_it : phases_per_ring) { | ||
QTreeWidgetItem* phase_ring_item = new QTreeWidgetItem(this->phase_tree); | ||
ignerr << phase_ring_it.first << std::endl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi leftover?
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi is there a way to let the phase_ring_item
manage the memory allocation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? I'm almost sure that once the item is added, it should be cleaned by the tree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but if something bad happens at the previous line memory will be leaked. Thus my question. It's fine if the answer is no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something bad like what? I would suppose that setText doesn't throw exceptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suppose that setText doesn't throw exceptions
Hmm, I'd prefer would suppose
to be just know
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quoting Qt:
Qt itself will not throw exceptions. Instead, error codes are used.
This doesn't return a code error, which I'll say nothing will happen.
} | ||
|
||
void RulesVisualizerWidget::ConstructPhaseRingTree(std::unordered_map<std::string, | ||
std::vector<QString>>&& phases_per_ring) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi fyi: you may also pass by value. As long as the provided argument is an rvalue, the move constructor will be used to set it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also want to pass an l-value just in case, that's why I'm using a universal reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doesn't that mean that:
std::unordered_map<std::string, std::vector<QString>> phases_per_ring
widget.ConstructPhaseRingTree(phases_per_ring);
is valid? If so, phases_per_ring
is emptied in ConstructPhaseRingTree
and there's no indication of it whatsoever (besides documentation of course). Wouldn't it be better to have ConstructPhaseRingTree(std::move(phases_per_ring))
? At least it is explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's valid and that way we enforce the user to explicitly consider that whatever he's using will be moved. If he wants to preserve whatever he has, he will need to copy the container (which will be the same if we weren't moving the data). I don't want to leave the possibility of copying the parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine I guess. But please do update ConstructPhaseRingTree
documentation about these side effects.
// |_PhaseRing2 | ||
// |__Phase1 | ||
QTreeWidgetItem* parent = selected_phase->parent(); | ||
if (parent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi hmm, what does it mean to select a phase ring? IIUC phases belong to rings, but that's about it. They bear other no meaning w.r.t. rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Select only a phase ring doesn't mean anything. But I need it for the query. I think I'm not following you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a typo in my comment, sorry about that. My point is, if there's no meaning to a phase ring being selected, why allowing it in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not allowing it. That's the point of the if condition. The signal is fired whenever I click on the phase ring or the phase alone.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That I understand. But if we don't allow it, there's no need to have code dealing with it. Why not using QTreeWidgetItem::SetFlags
, specifically Qt::ItemIsSelectable
in Qt::ItemFlags
? Sorry for not being explicit enough.
Also, std::pair
is somewhat terse. Can we have a struct? Or simply looking up the Phase*
in the PhaseRingBook
could work too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if we don't allow it, there's no need to have code dealing with it. Why not using QTreeWidgetItem::SetFlags, specifically Qt::ItemIsSelectable in Qt::ItemFlags? Sorry for not being explicit enough.
I got confused a little bit. I'm not connecting the "Selected" signal from the tree. I get the currentItem
which provides me the item I clicked no matter what. I tried to turning off Qt::ItemIsSelectable
bit I still get the phase ring item. I suspect that the signal in the tree doesn't get called, but the clicked index
does get saved internally anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, would using selectedItems()
method instead solve the problem?
32dc66f
to
db56c20
Compare
void MaliputFileSelectionWidget::onRoadRulebookButtonPressed() { | ||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open Road Rulebook YAML"), | ||
QString::fromStdString(this->fileDialogOpenPath), | ||
tr("YAML files (*.YAML)")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi nit²: *.YAML
-> *.yaml
here and below.
@@ -322,7 +333,30 @@ ContainerType MaliputViewerModel::GetAllLaneIds() const { | |||
} | |||
|
|||
template <typename StringType> | |||
StringType MaliputViewerModel::GetRulesOfLane(const std::string& _laneId) const { | |||
std::unordered_map<std::string, std::vector<StringType>> MaliputViewerModel::GetPhaseRings() const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi something I had not noticed before, why not using StringType
for a map key type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't want to force the implementation of a hash for the string type. QString is not supported for std::unordered_map
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I see. That's fine.
std::vector<QString>>&& phases_per_ring) { | ||
DELPHYNE_DEMAND(this->phase_tree != nullptr); | ||
this->phase_tree->clear(); | ||
for (auto& phase_ring_it : phases_per_ring) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi nit: phase_ring_n_phases
? It's not an iterator.
} | ||
|
||
void RulesVisualizerWidget::ConstructPhaseRingTree(std::unordered_map<std::string, | ||
std::vector<QString>>&& phases_per_ring) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, doesn't that mean that:
std::unordered_map<std::string, std::vector<QString>> phases_per_ring
widget.ConstructPhaseRingTree(phases_per_ring);
is valid? If so, phases_per_ring
is emptied in ConstructPhaseRingTree
and there's no indication of it whatsoever (besides documentation of course). Wouldn't it be better to have ConstructPhaseRingTree(std::move(phases_per_ring))
? At least it is explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, thinking about this from another perspective, why not simply having a single slot that is connected to lane and phase ID update signals and then retrieves that information instead of getting it via signal arguments? Clicking the scene would then update the lane selection which in turn would update the rules.
I bring this up because I see room for simplification if we cascade some updates instead of them being direct.
for (const auto& phase_ring_id : phase_ring_ids) { | ||
drake::optional<maliput::api::rules::PhaseRing> phase_ring = phase_ring_book->GetPhaseRing(phase_ring_id); | ||
const std::unordered_map<maliput::api::rules::Phase::Id, maliput::api::rules::Phase>& phases = | ||
phase_ring.value().phases(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi should we DELPHYNE_DEMAND
that phase_ring.has_value()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess. I assumed that it should have at least one phase.
@@ -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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi nit: now that we've added so much functionality, maybe this callback should be OnNewRoadNetwork
instead?
@@ -34,19 +40,25 @@ class RulesVisualizerWidget : public QWidget { | |||
void ClearLaneList(); | |||
/// \brief Clear the text in TextBrowser. | |||
void ClearText(); | |||
/// \brief Construct the tree for the given phase_ring. Beware that the parameter will be moved. Caller will have |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi consider using a \warning
or a \post
command for Beware...
and so on.
this->rulesVisualizerWiget->ClearText(); | ||
this->rulesVisualizerWiget->ClearLaneList(); | ||
this->rulesVisualizerWiget->ConstructPhaseRingTree(this->model->GetPhaseRings<QString>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi typo: rulesVisualizerWidget
OnRulesForLaneRequested(QString(lane_id.c_str())); | ||
PhaseRingPhaseIds phaseRingIdAndPhaseIdSelected = this->rulesVisualizerWiget->GetSelectedPhaseRingAndPhaseId(); | ||
OnRulesForLaneRequested(phaseRingIdAndPhaseIdSelected.phase_ring_id, phaseRingIdAndPhaseIdSelected.phase_id, | ||
QString(lane_id.c_str())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi why do we call the slot here instead of emitting the signal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if the slot is emiting the signal only, that could change. I prefer to make use of the same method instead.
I didn't understand anything. A single slot where? |
I see that we call the rules request slot manually here and through a connected signal here. In both cases, we retrieve phase and lane IDs to call that slot. Now, imagine you had a single slot that doesn't get phase and lane IDs as arguments but looks for them itself. If you connect it to signals, you can trigger it whenever you want to update the rules being listed. That'd simplify the code e.g. you would be able to collapse Sorry for not being explicit enough. What do you think? |
c6c082c
to
696f8f4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
if (lastIndexOf != -1) { | ||
return filePath.mid(lastIndexOf + 1); | ||
} | ||
return QString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi could it be thatfilePath
has no slashes? Also, don't we have filesystem
utilities elsewhere we can reuse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll double check and use them if they exist
} | ||
|
||
QString RulesVisualizerWidget::GetSelectedLaneId() const { | ||
QListWidgetItem* currentItem = this->lanes_list->currentItem(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi should it be selectedItem
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
void MaliputFileSelectionWidget::ClearLineEdits(bool keepOldTextsAsPlaceholders) { | ||
if (keepOldTextsAsPlaceholders) { | ||
std::string roadRulebookFileName = ignition::common::basename(this->roadRulebookLineEdit->text().toStdString()); | ||
this->roadRulebookLineEdit->setPlaceholderText(QString::fromStdString(roadRulebookFileName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rules += "[Right of way rules by phase ring id and phase id]\n" + | ||
GetPhaseRightOfWayRules<StringType>(maliput::api::rules::PhaseRing::Id(_phaseRingId), | ||
maliput::api::rules::Phase::Id(_phaseId)) + | ||
"\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BMarchi nit: we could use std::ostringstream
here as we do elsewhere. It's more efficient and portable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That implies to erase all template methods since we are not going to return any string and then convert it to a QString. If you want that, I'll change it then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just saw that you did something quite similar below for another method. My comment still stands, but it's just a nit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the stringstream is used at the moment because the Query class use that. But this method in particular just gathers all the rules that were parsed with that class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
88092a8
to
0a32fa4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Part of #207