moveit2
The MoveIt Motion Planning Framework for ROS 2.
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 
51 namespace moveit_setup
52 {
53 namespace 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 
215 {
216  delete model_;
217 }
218 
219 // ******************************************************************************************
220 // start thread generating the collision table
221 // ******************************************************************************************
222 void 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 // ******************************************************************************************
240 void 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 // ******************************************************************************************
258 void 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 
352 void 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 
372 void 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  menu.addAction(header_actions_.at(0)); // only 'show' action
397  else
398  menu.addActions(header_actions_);
399  menu.exec(global);
400 
401  clicked_headers_ = {};
402  clicked_section_ = -1;
403 }
404 
405 void DefaultCollisionsWidget::hideSections()
406 {
407  QList<int> list;
408  QHeaderView* header = nullptr;
409  if (clicked_headers_ == Qt::Horizontal)
410  {
411  for (const QModelIndex& index : selection_model_->selectedColumns())
412  list << index.column();
413  header = collision_table_->horizontalHeader();
414  }
415  else if (clicked_headers_ == Qt::Vertical)
416  {
417  for (const QModelIndex& index : selection_model_->selectedRows())
418  list << index.row();
419  header = collision_table_->verticalHeader();
420  }
421 
422  // if somewhere else than the selection was clicked, hide only this row/column
423  if (!list.contains(clicked_section_))
424  {
425  list.clear();
426  list << clicked_section_;
427  }
428 
429  for (auto index : list)
430  header->setSectionHidden(index, true);
431 }
432 
433 void DefaultCollisionsWidget::hideOtherSections()
434 {
435  QList<int> list;
436  QHeaderView* header = nullptr;
437  if (clicked_headers_ == Qt::Horizontal)
438  {
439  header = collision_table_->horizontalHeader();
440  for (const QModelIndex& index : selection_model_->selectedColumns())
441  if (!header->isSectionHidden(index.column()))
442  list << index.column();
443  }
444  else if (clicked_headers_ == Qt::Vertical)
445  {
446  header = collision_table_->verticalHeader();
447  for (const QModelIndex& index : selection_model_->selectedRows())
448  if (!header->isSectionHidden(index.row()))
449  list << index.row();
450  }
451 
452  // if somewhere else than the selection was clicked, hide only this row/column
453  if (!list.contains(clicked_section_))
454  {
455  list.clear();
456  list << clicked_section_;
457  }
458 
459  // first hide all sections
460  for (std::size_t index = 0, end = header->count(); index != end; ++index)
461  header->setSectionHidden(index, true);
462 
463  // and subsequently show selected ones
464  for (auto index : list)
465  header->setSectionHidden(index, false);
466 }
467 
468 void DefaultCollisionsWidget::showSections()
469 {
470  QList<int> list;
471  if (clicked_section_ < 0) // show all
472  {
473  if (clicked_headers_.testFlag(Qt::Horizontal))
474  {
475  // show all columns
476  list.clear();
477  list << 0 << model_->columnCount() - 1;
478  showSections(collision_table_->horizontalHeader(), list);
479  }
480 
481  if (clicked_headers_.testFlag(Qt::Vertical)) // show all rows
482  {
483  list.clear();
484  list << 0 << model_->rowCount() - 1;
485  showSections(collision_table_->verticalHeader(), list);
486  }
487  return;
488  }
489 
490  QHeaderView* header = nullptr;
491  if (clicked_headers_ == Qt::Horizontal)
492  {
493  for (const QModelIndex& index : selection_model_->selectedColumns())
494  list << index.column();
495  header = collision_table_->horizontalHeader();
496  }
497  else if (clicked_headers_ == Qt::Vertical)
498  {
499  for (const QModelIndex& index : selection_model_->selectedRows())
500  list << index.row();
501  header = collision_table_->verticalHeader();
502  }
503 
504  // if somewhere else than the selection was clicked, hide only this row/column
505  if (!list.contains(clicked_section_))
506  {
507  list.clear();
508  list << clicked_section_;
509  }
510  showSections(header, list);
511 }
512 void DefaultCollisionsWidget::showSections(QHeaderView* header, const QList<int>& logicalIndexes)
513 {
514  if (logicalIndexes.size() < 2)
515  return;
516  int prev = 0;
517  for (int next = 1, end = logicalIndexes.size(); next != end; prev = next, ++next)
518  {
519  for (int index = logicalIndexes[prev], index_end = logicalIndexes[next]; index <= index_end; ++index)
520  header->setSectionHidden(index, false);
521  }
522 }
523 
524 void DefaultCollisionsWidget::revertChanges()
525 {
526  setup_step_.linkPairsFromSRDF();
527  loadCollisionTable();
528  btn_revert_->setEnabled(false); // no changes to revert
529 }
530 
531 bool DefaultCollisionsWidget::eventFilter(QObject* object, QEvent* event)
532 {
533  if (object != collision_table_)
534  return false; // leave event unhandled
535 
536  if (event->type() == QEvent::Enter)
537  {
538  // grab focus as soon as mouse enters to allow for <space> to work in all cases
539  collision_table_->setFocus();
540  return false;
541  }
542  else if (event->type() == QEvent::KeyPress)
543  {
544  QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
545  if (key_event->key() != Qt::Key_Space)
546  return false;
547 
548  toggleSelection(selection_model_->selection());
549  return true; // no need for further processing
550  }
551 
552  return false;
553 }
554 
555 void DefaultCollisionsWidget::toggleSelection(QItemSelection selection)
556 {
557  // remove hidden rows / columns from selection
558  int rows = model_->rowCount();
559  int cols = model_->columnCount();
560  for (int r = 0; r != rows; ++r)
561  {
562  if (collision_table_->isRowHidden(r))
563  selection.merge(QItemSelection(model_->index(r, 0), model_->index(r, cols - 1)), QItemSelectionModel::Deselect);
564  }
565  for (int c = 0; c != cols; ++c)
566  {
567  if (collision_table_->isColumnHidden(c))
568  selection.merge(QItemSelection(model_->index(0, c), model_->index(rows - 1, c)), QItemSelectionModel::Deselect);
569  }
570 
571  // set all selected items to inverse value of current item
572  const QModelIndex& cur_idx = selection_model_->currentIndex();
573  if (view_mode_buttons_->checkedId() == MATRIX_MODE)
574  {
575  QModelIndex input_index;
576  if (cur_idx.flags() & Qt::ItemIsUserCheckable)
577  input_index = cur_idx; // if current index is checkable, this serves as input
578  else
579  { // search for first checkable index in selection that can serve as input
580  for (const auto idx : selection.indexes())
581  {
582  if (idx.flags() & Qt::ItemIsUserCheckable)
583  {
584  input_index = idx;
585  break;
586  }
587  }
588  if (!input_index.isValid())
589  return; // no valid selection
590  }
591 
592  bool current = model_->data(input_index, Qt::CheckStateRole) == Qt::Checked;
593  CollisionMatrixModel* m = static_cast<CollisionMatrixModel*>(model_);
594  m->setEnabled(selection, !current);
595  }
596  else
597  {
598  bool current = model_->data(model_->index(cur_idx.row(), 2), Qt::CheckStateRole) == Qt::Checked;
599  SortFilterProxyModel* m = static_cast<SortFilterProxyModel*>(model_);
600  m->setEnabled(selection, !current);
601  }
602 }
603 
604 // ******************************************************************************************
605 // GUI func for showing sampling density amount
606 // ******************************************************************************************
607 void DefaultCollisionsWidget::changeDensityLabel(int value)
608 {
609  density_value_label_->setText(QString::number(value * 1000 + 1000)); //.append(" samples") );
610 }
611 
612 // ******************************************************************************************
613 // Helper function to disable parts of GUI during computation
614 // ******************************************************************************************
615 void DefaultCollisionsWidget::disableControls(bool disable)
616 {
617  controls_box_->setDisabled(disable);
618  collision_table_->setDisabled(disable);
619 
620  if (disable)
621  {
622  progress_bar_->show(); // only show when computation begins
623  progress_label_->show();
624  }
625  else
626  {
627  progress_label_->hide();
628  progress_bar_->hide();
629  }
630 
631  QApplication::processEvents(); // allow the progress bar to be shown
632 }
633 
634 // ******************************************************************************************
635 // Changes the table to show or hide collisions that are not disabled (that have collision checking enabled)
636 // ******************************************************************************************
637 void DefaultCollisionsWidget::checkedFilterChanged()
638 {
639  SortFilterProxyModel* m = qobject_cast<SortFilterProxyModel*>(model_);
640  m->setShowAll(collision_checkbox_->checkState() == Qt::Checked);
641 }
642 
643 // ******************************************************************************************
644 // Preview whatever element is selected
645 // ******************************************************************************************
646 void DefaultCollisionsWidget::previewSelectedMatrix(const QModelIndex& index)
647 {
648  // Unhighlight all links
650 
651  if (!index.isValid())
652  return;
653 
654  // normalize index
655  int r = index.row();
656  int c = index.column();
657  if (r == c)
658  return;
659  if (r > c)
660  std::swap(r, c);
661 
662  // Highlight link pair
663  const QString& first_link = model_->headerData(r, Qt::Vertical, Qt::DisplayRole).toString();
664  const QString& second_link = model_->headerData(c, Qt::Horizontal, Qt::DisplayRole).toString();
665  uint check_state = model_->data(index, Qt::CheckStateRole).toUInt();
666 
667  QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
668  rviz_panel_->highlightLink(first_link.toStdString(), color);
669  rviz_panel_->highlightLink(second_link.toStdString(), color);
670 }
671 
672 void DefaultCollisionsWidget::previewSelectedLinear(const QModelIndex& index)
673 {
674  // Unhighlight all links
676 
677  if (!index.isValid())
678  return;
679 
680  // Highlight link pair
681  const QString& first_link = model_->data(model_->index(index.row(), 0), Qt::DisplayRole).toString();
682  const QString& second_link = model_->data(model_->index(index.row(), 1), Qt::DisplayRole).toString();
683  uint check_state = model_->data(model_->index(index.row(), 2), Qt::CheckStateRole).toUInt();
684 
685  QColor color = (check_state == Qt::Checked) ? QColor(0, 255, 0) : QColor(255, 0, 0);
686  rviz_panel_->highlightLink(first_link.toStdString(), color);
687  rviz_panel_->highlightLink(second_link.toStdString(), color);
688 }
689 
690 // ******************************************************************************************
691 // Called when setup assistant navigation switches to this screen
692 // ******************************************************************************************
693 void DefaultCollisionsWidget::focusGiven()
694 {
695  // Convert the SRDF data to LinkPairData format
696  setup_step_.linkPairsFromSRDF();
697 
698  // Load the data to the table
699  loadCollisionTable();
700 
701  // Enable the table
702  disableControls(false);
703  btn_revert_->setEnabled(false); // no changes to revert
704 }
705 
706 bool DefaultCollisionsWidget::focusLost()
707 {
708  if (worker_)
709  {
710  if (QMessageBox::No == QMessageBox::question(this, "Collision Matrix Generation",
711  "Collision Matrix Generation is still active. Cancel computation?",
712  QMessageBox::Yes | QMessageBox::No, QMessageBox::No))
713  return false;
714  worker_->cancel();
715  worker_->wait();
716  }
717 
718  // Copy changes to srdf_writer object
719  setup_step_.linkPairsToSRDF();
720  return true;
721 }
722 
723 MonitorThread::MonitorThread(DefaultCollisions& setup_step, QProgressBar* progress_bar)
724  : setup_step_(setup_step), canceled_(false)
725 {
726  // connect progress bar for updates
727  if (progress_bar)
728  connect(this, SIGNAL(progress(int)), progress_bar, SLOT(setValue(int)));
729 }
730 
732 {
733  // loop until worker thread is finished or cancel is requested
734  int thread_progress;
735  while (!canceled_ && (thread_progress = setup_step_.getThreadProgress()) < 100)
736  {
737  Q_EMIT progress(thread_progress);
738  QThread::msleep(100); // sleep 100ms
739  }
740 
741  // cancel worker thread
742  if (canceled_)
743  setup_step_.cancelGenerationThread();
744  setup_step_.joinGenerationThread();
745 
746  Q_EMIT progress(100);
747 }
748 
749 } // namespace srdf_setup
750 } // namespace moveit_setup
751 
752 #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)
Definition: rviz_panel.hpp:96
The GUI code for one SetupStep.
User interface for editing the default collision matrix list in an SRDF.
std::vector< std::string > getCollidingLinks()
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)
p
Definition: pick.py:62
r
Definition: plan.py:56