moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
default_collisions_widget.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Dave Coleman */
36
37#include <QApplication>
38#include <QHBoxLayout>
39#include <QKeyEvent>
40#include <QMenu>
41#include <QMessageBox>
42#include <QRadioButton>
43#include <QString>
44
50
51namespace moveit_setup
52{
53namespace srdf_setup
54{
55// ******************************************************************************************
56// User interface for editing the default collision matrix list in an SRDF
57// ******************************************************************************************
59{
60 model_ = nullptr;
61 selection_model_ = nullptr;
62 worker_ = nullptr;
63
64 // Basic widget container
65 layout_ = new QVBoxLayout(this);
66
67 // Top Label Area ------------------------------------------------
68 auto header = new HeaderWidget(
69 "Optimize Self-Collision Checking",
70 "This searches for pairs of robot links that can safely be disabled from collision checking, decreasing motion "
71 "planning time. These pairs are disabled when they are always in collision, never in collision, in collision in "
72 "the robot's default position, or when the links are adjacent to each other on the kinematic chain. Sampling "
73 "density specifies how many random robot positions to check for self collision.",
74 this);
75 layout_->addWidget(header);
76
77 // Top Button Area -----------------------------------------------
78 controls_box_ = new QGroupBox(this);
79 layout_->addWidget(controls_box_);
80 QVBoxLayout* controls_box_layout = new QVBoxLayout(controls_box_);
81
82 QHBoxLayout* slider_layout = new QHBoxLayout();
83 slider_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
84 controls_box_layout->addLayout(slider_layout);
85
86 // Slider Label
87 QLabel* density_left_label = new QLabel(this);
88 density_left_label->setText("Sampling Density: Low");
89 slider_layout->addWidget(density_left_label);
90
91 // Slider
92 density_slider_ = new QSlider(this);
93 density_slider_->setTickPosition(QSlider::TicksBelow);
94 density_slider_->setMinimum(0);
95 density_slider_->setMaximum(99);
96 density_slider_->setSingleStep(10);
97 density_slider_->setPageStep(50);
98 density_slider_->setSliderPosition(9); // 10,000 is default
99 density_slider_->setTickInterval(10);
100 density_slider_->setOrientation(Qt::Horizontal);
101 slider_layout->addWidget(density_slider_);
102 connect(density_slider_, SIGNAL(valueChanged(int)), this, SLOT(changeDensityLabel(int)));
103
104 // Slider Right Label
105 QLabel* density_right_label = new QLabel(this);
106 density_right_label->setText("High ");
107 slider_layout->addWidget(density_right_label);
108
109 // Slider Value Label
110 density_value_label_ = new QLabel(this);
111 density_value_label_->setMinimumWidth(50);
112 slider_layout->addWidget(density_value_label_);
113 changeDensityLabel(density_slider_->value()); // initialize label with value
114
115 QHBoxLayout* buttons_layout = new QHBoxLayout();
116 buttons_layout->setAlignment(Qt::AlignRight);
117 controls_box_layout->addLayout(buttons_layout);
118
119 // Fraction spin box
120 fraction_label_ = new QLabel(this);
121 fraction_label_->setText("Min. collisions for \"always\"-colliding pairs:");
122 buttons_layout->addWidget(fraction_label_);
123
124 fraction_spinbox_ = new QSpinBox(this);
125 fraction_spinbox_->setRange(1, 100);
126 fraction_spinbox_->setValue(95);
127 fraction_spinbox_->setSuffix("%");
128 buttons_layout->addWidget(fraction_spinbox_);
129
130 // Generate Button
131 btn_generate_ = new QPushButton(this);
132 btn_generate_->setText("&Generate Collision Matrix");
133 btn_generate_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
134 connect(btn_generate_, SIGNAL(clicked()), this, SLOT(startGeneratingCollisionTable()));
135 buttons_layout->addWidget(btn_generate_);
136
137 // Progress Bar Area ---------------------------------------------
138
139 // Progress Label
140 progress_label_ = new QLabel(this);
141 progress_label_->setText("Generating Default Collision Matrix");
142 progress_label_->hide();
143 layout_->addWidget(progress_label_);
144
145 // Progress Bar
146 progress_bar_ = new QProgressBar(this);
147 progress_bar_->setMaximum(100);
148 progress_bar_->setMinimum(0);
149 progress_bar_->hide(); // only show when computation begins
150 layout_->addWidget(progress_bar_);
151
152 // Table Area --------------------------------------------
153
154 // Table
155 collision_table_ = new QTableView(this);
156 layout_->addWidget(collision_table_);
157
158 QAction* action;
159 action = new QAction(tr("Show"), this);
160 header_actions_ << action;
161 connect(action, SIGNAL(triggered()), this, SLOT(showSections()));
162 action = new QAction(tr("Hide"), this);
163 header_actions_ << action;
164 connect(action, SIGNAL(triggered()), this, SLOT(hideSections()));
165 action = new QAction(tr("Hide others"), this);
166 header_actions_ << action;
167 connect(action, SIGNAL(triggered()), this, SLOT(hideOtherSections()));
168
169 // Bottom Area ----------------------------------------
170
171 QHBoxLayout* bottom_layout = new QHBoxLayout();
172 bottom_layout->setAlignment(Qt::AlignRight);
173 layout_->addLayout(bottom_layout);
174
175 // Link Filter QLineEdit
176 link_name_filter_ = new QLineEdit(this);
177 link_name_filter_->setPlaceholderText("link name filter");
178 bottom_layout->addWidget(link_name_filter_);
179
180 // Collision Filter Checkbox
181 collision_checkbox_ = new QCheckBox(this);
182 collision_checkbox_->setText("show enabled pairs");
183 connect(collision_checkbox_, SIGNAL(toggled(bool)), this, SLOT(checkedFilterChanged()));
184 bottom_layout->addWidget(collision_checkbox_);
185
186 // View Mode Buttons
187 view_mode_buttons_ = new QButtonGroup(this);
188 QRadioButton* radio_btn;
189 radio_btn = new QRadioButton("linear view");
190 bottom_layout->addWidget(radio_btn);
191 view_mode_buttons_->addButton(radio_btn, LINEAR_MODE);
192 radio_btn->setChecked(true);
193
194 radio_btn = new QRadioButton("matrix view");
195 bottom_layout->addWidget(radio_btn);
196 view_mode_buttons_->addButton(radio_btn, MATRIX_MODE);
197 connect(view_mode_buttons_, SIGNAL(buttonClicked(int)), this, SLOT(loadCollisionTable()));
198
199 // Revert Button
200 btn_revert_ = new QPushButton(this);
201 btn_revert_->setText("&Revert");
202 btn_revert_->setToolTip("Revert current changes to collision matrix");
203 btn_revert_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
204 btn_revert_->setDisabled(true);
205 connect(btn_revert_, SIGNAL(clicked()), this, SLOT(revertChanges()));
206 bottom_layout->addWidget(btn_revert_);
207
208 setLayout(layout_);
209 setWindowTitle("Default Collision Matrix");
210
211 collision_table_->installEventFilter(this);
212}
213
218
219// ******************************************************************************************
220// start thread generating the collision table
221// ******************************************************************************************
222void DefaultCollisionsWidget::startGeneratingCollisionTable()
223{
224 // Disable controls on form
225 disableControls(true);
226 btn_revert_->setEnabled(true); // allow to interrupt and revert
227
228 // Start actual worker thread
229 setup_step_.startGenerationThread(density_slider_->value() * 1000 + 1000, fraction_spinbox_->value() / 100.0);
230
231 // create a MonitorThread running generateCollisionTable() in a worker thread and monitoring the progress
232 worker_ = new MonitorThread(setup_step_, progress_bar_);
233 connect(worker_, SIGNAL(finished()), this, SLOT(finishGeneratingCollisionTable()));
234 worker_->start(); // start after having finished() signal connected
235}
236
237// ******************************************************************************************
238// cleanup after worker_ thread has finished
239// ******************************************************************************************
240void DefaultCollisionsWidget::finishGeneratingCollisionTable()
241{
242 if (worker_->canceled())
243 return;
244
245 // Load the results into the GUI
246 loadCollisionTable();
247
248 // Hide the progress bar
249 disableControls(false); // enable everything else
250
251 worker_->deleteLater();
252 worker_ = nullptr;
253}
254
255// ******************************************************************************************
256// Displays data in the link_pairs_ data structure into a QtTableWidget
257// ******************************************************************************************
258void DefaultCollisionsWidget::loadCollisionTable()
259{
260 CollisionMatrixModel* matrix_model =
261 new CollisionMatrixModel(setup_step_.getLinkPairs(), setup_step_.getCollidingLinks());
262 QAbstractItemModel* model;
263
264 if (view_mode_buttons_->checkedId() == MATRIX_MODE)
265 {
266 model = matrix_model;
267 }
268 else
269 {
270 CollisionLinearModel* linear_model = new CollisionLinearModel(matrix_model);
271 SortFilterProxyModel* sorted_model = new SortFilterProxyModel();
272 model = sorted_model;
273 sorted_model->setSourceModel(linear_model);
274 // ensure deletion of underlying models with model
275 linear_model->setParent(sorted_model);
276 matrix_model->setParent(linear_model);
277 }
278 connect(link_name_filter_, SIGNAL(textChanged(QString)), model, SLOT(setFilterRegExp(QString)));
279 QMetaObject::invokeMethod(model, "setFilterRegExp", Q_ARG(QString, link_name_filter_->text()));
280
281 collision_table_->setModel(model);
282 // delete old and remember new model
283 delete model_;
284 model_ = model;
285
286 // delete old and fetch new selection model
287 delete selection_model_;
288 selection_model_ = collision_table_->selectionModel();
289
290 QHeaderView *horizontal_header, *vertical_header;
291
292 // activate some model-specific settings
293 if (view_mode_buttons_->checkedId() == MATRIX_MODE)
294 {
295 connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)), this,
296 SLOT(previewSelectedMatrix(QModelIndex)));
297
298 collision_table_->setSelectionBehavior(QAbstractItemView::SelectItems);
299 collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
300
301 collision_table_->setHorizontalHeader(horizontal_header = new RotatedHeaderView(Qt::Horizontal, this));
302 collision_table_->setVerticalHeader(vertical_header = new RotatedHeaderView(Qt::Vertical, this));
303 collision_table_->setSortingEnabled(false);
304
305 collision_checkbox_->hide();
306 horizontal_header->setVisible(true);
307 vertical_header->setVisible(true);
308
309 horizontal_header->setContextMenuPolicy(Qt::CustomContextMenu);
310 connect(horizontal_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
311 vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
312 connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
313 }
314 else
315 {
316 connect(selection_model_, SIGNAL(currentChanged(QModelIndex, QModelIndex)), this,
317 SLOT(previewSelectedLinear(QModelIndex)));
318
319 collision_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
320 collision_table_->setSelectionMode(QAbstractItemView::ExtendedSelection);
321
322 collision_table_->setHorizontalHeader(horizontal_header = new QHeaderView(Qt::Horizontal, this));
323 collision_table_->setVerticalHeader(vertical_header = new QHeaderView(Qt::Vertical, this));
324 collision_table_->sortByColumn(0, Qt::AscendingOrder);
325 collision_table_->setSortingEnabled(true);
326
327 collision_checkbox_->show();
328 horizontal_header->setVisible(true);
329 vertical_header->setVisible(true);
330
331 vertical_header->setContextMenuPolicy(Qt::CustomContextMenu);
332 connect(vertical_header, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderContextMenu(QPoint)));
333
334#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
335 horizontal_header->setSectionsClickable(true);
336 vertical_header->setSectionsClickable(true);
337#else
338 horizontal_header->setClickable(true);
339 vertical_header->setClickable(true);
340#endif
341 }
342
343// notice changes to the model
344#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
345 connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex, QVector<int>)), this,
346 SLOT(collisionsChanged(QModelIndex)));
347#else
348 connect(model_, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(collisionsChanged(QModelIndex)));
349#endif
350}
351
352void DefaultCollisionsWidget::collisionsChanged(const QModelIndex& index)
353{
354 btn_revert_->setEnabled(true); // enable revert button
355
356 if (!index.isValid())
357 return;
358 // Hm. For some reason, QTableView doesn't change selection if we click a checkbox
359 bool linear_mode = (view_mode_buttons_->checkedId() == LINEAR_MODE);
360 const QItemSelection& selection = selection_model_->selection();
361 if ((linear_mode && !selection.contains(index)) || // in linear mode: index not in selection
362 (!linear_mode && !(selection.contains(index) || // in matrix mode: index or symmetric index not in selection
363 selection.contains(model_->index(index.column(), index.row())))))
364 {
365 QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select | QItemSelectionModel::Current;
366 if (linear_mode)
367 flags |= QItemSelectionModel::Rows;
368 selection_model_->setCurrentIndex(index, flags);
369 }
370}
371
372void DefaultCollisionsWidget::showHeaderContextMenu(const QPoint& p)
373{
374 // This method might be triggered from either of the headers
375 QPoint global;
376 if (sender() == collision_table_->verticalHeader())
377 {
378 clicked_section_ = collision_table_->verticalHeader()->logicalIndexAt(p);
379 clicked_headers_ = Qt::Vertical;
380 global = collision_table_->verticalHeader()->mapToGlobal(p);
381 }
382 else if (sender() == collision_table_->horizontalHeader())
383 {
384 clicked_section_ = collision_table_->horizontalHeader()->logicalIndexAt(p);
385 clicked_headers_ = Qt::Horizontal;
386 global = collision_table_->horizontalHeader()->mapToGlobal(p);
387 }
388 else
389 {
390 clicked_section_ = -1;
391 clicked_headers_ = Qt::Horizontal | Qt::Vertical;
392 }
393
394 QMenu menu;
395 if (clicked_section_ < 0)
396 {
397 menu.addAction(header_actions_.at(0)); // only 'show' action
398 }
399 else
400 {
401 menu.addActions(header_actions_);
402 }
403 menu.exec(global);
404
405 clicked_headers_ = {};
406 clicked_section_ = -1;
407}
408
409void DefaultCollisionsWidget::hideSections()
410{
411 QList<int> list;
412 QHeaderView* header = nullptr;
413 if (clicked_headers_ == Qt::Horizontal)
414 {
415 for (const QModelIndex& index : selection_model_->selectedColumns())
416 list << index.column();
417 header = collision_table_->horizontalHeader();
418 }
419 else if (clicked_headers_ == Qt::Vertical)
420 {
421 for (const QModelIndex& index : selection_model_->selectedRows())
422 list << index.row();
423 header = collision_table_->verticalHeader();
424 }
425
426 // if somewhere else than the selection was clicked, hide only this row/column
427 if (!list.contains(clicked_section_))
428 {
429 list.clear();
430 list << clicked_section_;
431 }
432
433 for (auto index : list)
434 header->setSectionHidden(index, true);
435}
436
437void DefaultCollisionsWidget::hideOtherSections()
438{
439 QList<int> list;
440 QHeaderView* header = nullptr;
441 if (clicked_headers_ == Qt::Horizontal)
442 {
443 header = collision_table_->horizontalHeader();
444 for (const QModelIndex& index : selection_model_->selectedColumns())
445 {
446 if (!header->isSectionHidden(index.column()))
447 list << index.column();
448 }
449 }
450 else if (clicked_headers_ == Qt::Vertical)
451 {
452 header = collision_table_->verticalHeader();
453 for (const QModelIndex& index : selection_model_->selectedRows())
454 {
455 if (!header->isSectionHidden(index.row()))
456 list << index.row();
457 }
458 }
459
460 // if somewhere else than the selection was clicked, hide only this row/column
461 if (!list.contains(clicked_section_))
462 {
463 list.clear();
464 list << clicked_section_;
465 }
466
467 // first hide all sections
468 for (std::size_t index = 0, end = header->count(); index != end; ++index)
469 header->setSectionHidden(index, true);
470
471 // and subsequently show selected ones
472 for (auto index : list)
473 header->setSectionHidden(index, false);
474}
475
476void DefaultCollisionsWidget::showSections()
477{
478 QList<int> list;
479 if (clicked_section_ < 0) // show all
480 {
481 if (clicked_headers_.testFlag(Qt::Horizontal))
482 {
483 // show all columns
484 list.clear();
485 list << 0 << model_->columnCount() - 1;
486 showSections(collision_table_->horizontalHeader(), list);
487 }
488
489 if (clicked_headers_.testFlag(Qt::Vertical)) // show all rows
490 {
491 list.clear();
492 list << 0 << model_->rowCount() - 1;
493 showSections(collision_table_->verticalHeader(), list);
494 }
495 return;
496 }
497
498 QHeaderView* header = nullptr;
499 if (clicked_headers_ == Qt::Horizontal)
500 {
501 for (const QModelIndex& index : selection_model_->selectedColumns())
502 list << index.column();
503 header = collision_table_->horizontalHeader();
504 }
505 else if (clicked_headers_ == Qt::Vertical)
506 {
507 for (const QModelIndex& index : selection_model_->selectedRows())
508 list << index.row();
509 header = collision_table_->verticalHeader();
510 }
511
512 // if somewhere else than the selection was clicked, hide only this row/column
513 if (!list.contains(clicked_section_))
514 {
515 list.clear();
516 list << clicked_section_;
517 }
518 showSections(header, list);
519}
520void DefaultCollisionsWidget::showSections(QHeaderView* header, const QList<int>& logicalIndexes)
521{
522 if (logicalIndexes.size() < 2)
523 return;
524 int prev = 0;
525 for (int next = 1, end = logicalIndexes.size(); next != end; prev = next, ++next)
526 {
527 for (int index = logicalIndexes[prev], index_end = logicalIndexes[next]; index <= index_end; ++index)
528 header->setSectionHidden(index, false);
529 }
530}
531
532void DefaultCollisionsWidget::revertChanges()
533{
534 setup_step_.linkPairsFromSRDF();
535 loadCollisionTable();
536 btn_revert_->setEnabled(false); // no changes to revert
537}
538
539bool DefaultCollisionsWidget::eventFilter(QObject* object, QEvent* event)
540{
541 if (object != collision_table_)
542 return false; // leave event unhandled
543
544 if (event->type() == QEvent::Enter)
545 {
546 // grab focus as soon as mouse enters to allow for <space> to work in all cases
547 collision_table_->setFocus();
548 return false;
549 }
550 else if (event->type() == QEvent::KeyPress)
551 {
552 QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
553 if (key_event->key() != Qt::Key_Space)
554 return false;
555
556 toggleSelection(selection_model_->selection());
557 return true; // no need for further processing
558 }
559
560 return false;
561}
562
563void DefaultCollisionsWidget::toggleSelection(QItemSelection selection)
564{
565 // remove hidden rows / columns from selection
566 int rows = model_->rowCount();
567 int cols = model_->columnCount();
568 for (int r = 0; r != rows; ++r)
569 {
570 if (collision_table_->isRowHidden(r))
571 selection.merge(QItemSelection(model_->index(r, 0), model_->index(r, cols - 1)), QItemSelectionModel::Deselect);
572 }
573 for (int c = 0; c != cols; ++c)
574 {
575 if (collision_table_->isColumnHidden(c))
576 selection.merge(QItemSelection(model_->index(0, c), model_->index(rows - 1, c)), QItemSelectionModel::Deselect);
577 }
578
579 // set all selected items to inverse value of current item
580 const QModelIndex& cur_idx = selection_model_->currentIndex();
581 if (view_mode_buttons_->checkedId() == MATRIX_MODE)
582 {
583 QModelIndex input_index;
584 if (cur_idx.flags() & Qt::ItemIsUserCheckable)
585 {
586 input_index = cur_idx; // if current index is checkable, this serves as input
587 }
588 else
589 { // search for first checkable index in selection that can serve as input
590 for (const auto idx : selection.indexes())
591 {
592 if (idx.flags() & Qt::ItemIsUserCheckable)
593 {
594 input_index = idx;
595 break;
596 }
597 }
598 if (!input_index.isValid())
599 return; // no valid selection
600 }
601
602 bool current = model_->data(input_index, Qt::CheckStateRole) == Qt::Checked;
603 CollisionMatrixModel* m = static_cast<CollisionMatrixModel*>(model_);
604 m->setEnabled(selection, !current);
605 }
606 else
607 {
608 bool current = model_->data(model_->index(cur_idx.row(), 2), Qt::CheckStateRole) == Qt::Checked;
609 SortFilterProxyModel* m = static_cast<SortFilterProxyModel*>(model_);
610 m->setEnabled(selection, !current);
611 }
612}
613
614// ******************************************************************************************
615// GUI func for showing sampling density amount
616// ******************************************************************************************
617void DefaultCollisionsWidget::changeDensityLabel(int value)
618{
619 density_value_label_->setText(QString::number(value * 1000 + 1000)); //.append(" samples") );
620}
621
622// ******************************************************************************************
623// Helper function to disable parts of GUI during computation
624// ******************************************************************************************
625void DefaultCollisionsWidget::disableControls(bool disable)
626{
627 controls_box_->setDisabled(disable);
628 collision_table_->setDisabled(disable);
629
630 if (disable)
631 {
632 progress_bar_->show(); // only show when computation begins
633 progress_label_->show();
634 }
635 else
636 {
637 progress_label_->hide();
638 progress_bar_->hide();
639 }
640
641 QApplication::processEvents(); // allow the progress bar to be shown
642}
643
644// ******************************************************************************************
645// Changes the table to show or hide collisions that are not disabled (that have collision checking enabled)
646// ******************************************************************************************
647void DefaultCollisionsWidget::checkedFilterChanged()
648{
649 SortFilterProxyModel* m = qobject_cast<SortFilterProxyModel*>(model_);
650 m->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
651}
652
653// ******************************************************************************************
654// Preview whatever element is selected
655// ******************************************************************************************
656void DefaultCollisionsWidget::previewSelectedMatrix(const QModelIndex& index)
657{
658 // Unhighlight all links
660
661 if (!index.isValid())
662 return;
663
664 // normalize index
665 int r = index.row();
666 int c = index.column();
667 if (r == c)
668 return;
669 if (r > c)
670 std::swap(r, c);
671
672 // Highlight link pair
673 const QString& first_link = model_->headerData(r, Qt::Vertical, Qt::DisplayRole).toString();
674 const QString& second_link = model_->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString();
675 uint check_state = model_->data(index, Qt::CheckStateRole).toUInt();
676
677 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
678 rviz_panel_->highlightLink(first_link.toStdString(), color);
679 rviz_panel_->highlightLink(second_link.toStdString(), color);
680}
681
682void DefaultCollisionsWidget::previewSelectedLinear(const QModelIndex& index)
683{
684 // Unhighlight all links
686
687 if (!index.isValid())
688 return;
689
690 // Highlight link pair
691 const QString& first_link = model_->data(model_->index(index.row(), 0), Qt::DisplayRole).toString();
692 const QString& second_link = model_->data(model_->index(index.row(), 1), Qt::DisplayRole).toString();
693 uint check_state = model_->data(model_->index(index.row(), 2), Qt::CheckStateRole).toUInt();
694
695 QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
696 rviz_panel_->highlightLink(first_link.toStdString(), color);
697 rviz_panel_->highlightLink(second_link.toStdString(), color);
698}
699
700// ******************************************************************************************
701// Called when setup assistant navigation switches to this screen
702// ******************************************************************************************
703void DefaultCollisionsWidget::focusGiven()
704{
705 // Convert the SRDF data to LinkPairData format
706 setup_step_.linkPairsFromSRDF();
707
708 // Load the data to the table
709 loadCollisionTable();
710
711 // Enable the table
712 disableControls(false);
713 btn_revert_->setEnabled(false); // no changes to revert
714}
715
716bool DefaultCollisionsWidget::focusLost()
717{
718 if (worker_)
719 {
720 if (QMessageBox::No == QMessageBox::question(this, "Collision Matrix Generation",
721 "Collision Matrix Generation is still active. Cancel computation?",
722 QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
723 return false;
724 worker_->cancel();
725 worker_->wait();
726 }
727
728 // Copy changes to srdf_writer object
729 setup_step_.linkPairsToSRDF();
730 return true;
731}
732
733MonitorThread::MonitorThread(DefaultCollisions& setup_step, QProgressBar* progress_bar)
734 : setup_step_(setup_step), canceled_(false)
735{
736 // connect progress bar for updates
737 if (progress_bar)
738 connect(this, SIGNAL(progress(int)), progress_bar, SLOT(setValue(int)));
739}
740
742{
743 // loop until worker thread is finished or cancel is requested
744 int thread_progress;
745 while (!canceled_ && (thread_progress = setup_step_.getThreadProgress()) < 100)
746 {
747 Q_EMIT progress(thread_progress);
748 QThread::msleep(100); // sleep 100ms
749 }
750
751 // cancel worker thread
752 if (canceled_)
753 setup_step_.cancelGenerationThread();
754 setup_step_.joinGenerationThread();
755
756 Q_EMIT progress(100);
757}
758
759} // namespace srdf_setup
760} // namespace moveit_setup
761
762#include <pluginlib/class_list_macros.hpp> // NOLINT
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
void highlightLink(const std::string &link_name, const QColor &color)
The GUI code for one SetupStep.
User interface for editing the default collision matrix list in an SRDF.
void startGenerationThread(unsigned int num_trials, double min_frac, bool verbose=true)
void linkPairsFromSRDF()
Load Link Pairs from SRDF Format.
void linkPairsToSRDF()
Output Link Pairs to SRDF Format.
QThread to monitor progress of the setup step.
MonitorThread(DefaultCollisions &setup_step, QProgressBar *progress_bar=nullptr)