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  {
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 
409 void 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 
437 void 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 
476 void 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 }
520 void 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 
532 void DefaultCollisionsWidget::revertChanges()
533 {
534  setup_step_.linkPairsFromSRDF();
535  loadCollisionTable();
536  btn_revert_->setEnabled(false); // no changes to revert
537 }
538 
539 bool 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 
563 void 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 // ******************************************************************************************
617 void 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 // ******************************************************************************************
625 void 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 // ******************************************************************************************
647 void 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 // ******************************************************************************************
656 void 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 
682 void 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 // ******************************************************************************************
703 void 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 
716 bool 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 
733 MonitorThread::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)
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)