planning_groups_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 // ******************************************************************************************
38 /* DEVELOPER NOTES
39 
40  This widget has 6 subscreens, located in somewhat different places
41  - Main screen, the tree view of all groups & subgroups - embedded in this file as a function
42  - Add/Edit Group screen - located in group_edit_widget.cpp
43  - Joint Collection Screen - implements the double_list_widget.cpp widget
44  - Link Collection Screen - implements the double_list_widget.cpp widget
45  - Kinematic Chain Screen - uses it own custom widget - kinematic_chain_widget.cpp
46  - Subgroup Screen - implements the double_list_widget.cpp widget
47 */
48 // ******************************************************************************************
49 
50 #include "header_widget.h"
51 #include "planning_groups_widget.h"
52 #include "double_list_widget.h" // for joints, links and subgroups pages
53 #include "kinematic_chain_widget.h" // for kinematic chain page
54 #include "group_edit_widget.h" // for group rename page
55 // Qt
56 #include <QApplication>
57 #include <QComboBox>
58 #include <QHBoxLayout>
59 #include <QLabel>
60 #include <QLineEdit>
61 #include <QDoubleSpinBox>
62 #include <QMessageBox>
63 #include <QPushButton>
64 #include <QStackedWidget>
65 #include <QTableWidget>
66 #include <QTreeWidget>
67 #include <QTreeWidgetItem>
68 #include <QVBoxLayout>
69 
71 #include <boost/graph/adjacency_list.hpp>
72 #include <boost/graph/depth_first_search.hpp>
73 
74 namespace moveit_setup_assistant
75 {
76 // Name of rviz topic in ROS
77 static const std::string VIS_TOPIC_NAME = "planning_components_visualization";
78 
79 // Used for checking for cycles in a subgroup hierarchy
80 struct CycleDetector : public boost::dfs_visitor<>
81 {
82  CycleDetector(bool& has_cycle) : m_has_cycle(has_cycle)
83  {
84  }
85 
86  template <class Edge, class Graph>
87  void backEdge(Edge /*unused*/, Graph& /*unused*/)
88  {
89  m_has_cycle = true;
90  }
91 
92 protected:
93  bool& m_has_cycle;
94 };
95 
96 // ******************************************************************************************
97 // Constructor
98 // ******************************************************************************************
99 PlanningGroupsWidget::PlanningGroupsWidget(QWidget* parent, const MoveItConfigDataPtr& config_data)
100  : SetupScreenWidget(parent), config_data_(config_data)
101 {
102  // Basic widget container
103  QVBoxLayout* layout = new QVBoxLayout();
104 
105  // Top Label Area ------------------------------------------------
106  HeaderWidget* header = new HeaderWidget(
107  "Define Planning Groups",
108  "Create and edit 'joint model' groups for your robot based on joint collections, "
109  "link collections, kinematic chains or subgroups. "
110  "A planning group defines the set of (joint, link) pairs considered for planning "
111  "and collision checking. Define individual groups for each subset of the robot you want to plan for.\n"
112  "Note: when adding a link to the group, its parent joint is added too and vice versa.",
113  this);
114  layout->addWidget(header);
115 
116  // Left Side ---------------------------------------------
117 
118  // Create left side widgets
119  groups_tree_widget_ = createContentsWidget(); // included in this file
120 
121  // Joints edit widget
122  joints_widget_ = new DoubleListWidget(this, config_data_, "Joint Collection", "Joint");
123  connect(joints_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
124  connect(joints_widget_, SIGNAL(doneEditing()), this, SLOT(saveJointsScreen()));
125  connect(joints_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
126  SLOT(previewSelectedJoints(std::vector<std::string>)));
127 
128  // Links edit widget
129  links_widget_ = new DoubleListWidget(this, config_data_, "Link Collection", "Link");
130  connect(links_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
131  connect(links_widget_, SIGNAL(doneEditing()), this, SLOT(saveLinksScreen()));
132  connect(links_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
133  SLOT(previewSelectedLink(std::vector<std::string>)));
134 
135  // Chain Widget
136  chain_widget_ = new KinematicChainWidget(this, config_data);
137  connect(chain_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
138  connect(chain_widget_, SIGNAL(doneEditing()), this, SLOT(saveChainScreen()));
139  connect(chain_widget_, SIGNAL(unhighlightAll()), this, SIGNAL(unhighlightAll()));
140  connect(chain_widget_, SIGNAL(highlightLink(const std::string&, const QColor&)), this,
141  SIGNAL(highlightLink(const std::string&, const QColor&)));
142 
143  // Subgroups Widget
144  subgroups_widget_ = new DoubleListWidget(this, config_data_, "Subgroup", "Subgroup");
145  connect(subgroups_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
146  connect(subgroups_widget_, SIGNAL(doneEditing()), this, SLOT(saveSubgroupsScreen()));
147  connect(subgroups_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
148  SLOT(previewSelectedSubgroup(std::vector<std::string>)));
149 
150  // Group Edit Widget
151  group_edit_widget_ = new GroupEditWidget(this, config_data_);
152  connect(group_edit_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
153  connect(group_edit_widget_, SIGNAL(deleteGroup()), this, SLOT(deleteGroup()));
154  connect(group_edit_widget_, SIGNAL(save()), this, SLOT(saveGroupScreenEdit()));
155  connect(group_edit_widget_, SIGNAL(saveJoints()), this, SLOT(saveGroupScreenJoints()));
156  connect(group_edit_widget_, SIGNAL(saveLinks()), this, SLOT(saveGroupScreenLinks()));
157  connect(group_edit_widget_, SIGNAL(saveChain()), this, SLOT(saveGroupScreenChain()));
158  connect(group_edit_widget_, SIGNAL(saveSubgroups()), this, SLOT(saveGroupScreenSubgroups()));
159 
160  // Combine into stack: Note, order is same as GroupType!
161  stacked_widget_ = new QStackedWidget(this);
162  stacked_widget_->addWidget(groups_tree_widget_); // screen index 0
163  stacked_widget_->addWidget(joints_widget_); // screen index 1
164  stacked_widget_->addWidget(links_widget_); // screen index 2
165  stacked_widget_->addWidget(chain_widget_); // screen index 3
166  stacked_widget_->addWidget(subgroups_widget_); // screen index 4
167  stacked_widget_->addWidget(group_edit_widget_); // screen index 5
168 
169  showMainScreen();
170 
171  // Finish GUI -----------------------------------------------------------
172 
173  layout->addWidget(stacked_widget_);
174  setLayout(layout);
175 
176  // process the gui
177  QApplication::processEvents();
178 }
179 
180 // ******************************************************************************************
181 // Create the main tree view widget
182 // ******************************************************************************************
184 {
185  // Main widget
186  QWidget* content_widget = new QWidget(this);
187 
188  // Basic widget container
189  QVBoxLayout* layout = new QVBoxLayout(this);
190 
191  // Tree Box ----------------------------------------------------------------------
192 
193  groups_tree_ = new QTreeWidget(this);
194  groups_tree_->setHeaderLabel("Current Groups");
195  connect(groups_tree_, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(editSelected()));
196  connect(groups_tree_, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(previewSelected()));
197  layout->addWidget(groups_tree_);
198 
199  // Bottom Controls -------------------------------------------------------------
200 
201  QHBoxLayout* controls_layout = new QHBoxLayout();
202 
203  // Expand/Contract controls
204  QLabel* expand_controls = new QLabel(this);
205  expand_controls->setText("<a href='expand'>Expand All</a> <a href='contract'>Collapse All</a>");
206  connect(expand_controls, SIGNAL(linkActivated(const QString)), this, SLOT(alterTree(const QString)));
207  controls_layout->addWidget(expand_controls);
208 
209  // Spacer
210  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
211 
212  // Delete Selected Button
213  btn_delete_ = new QPushButton("&Delete Selected", this);
214  btn_delete_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
215  btn_delete_->setMaximumWidth(300);
216  connect(btn_delete_, SIGNAL(clicked()), this, SLOT(deleteGroup()));
217  controls_layout->addWidget(btn_delete_);
218  controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
219 
220  // Edit Selected Button
221  btn_edit_ = new QPushButton("&Edit Selected", this);
222  btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
223  btn_edit_->setMaximumWidth(300);
224  btn_edit_->hide(); // show once we know if there are existing groups
225  connect(btn_edit_, SIGNAL(clicked()), this, SLOT(editSelected()));
226  controls_layout->addWidget(btn_edit_);
227  controls_layout->setAlignment(btn_edit_, Qt::AlignRight);
228 
229  // Add Group Button
230  QPushButton* btn_add = new QPushButton("&Add Group", this);
231  btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
232  btn_add->setMaximumWidth(300);
233  connect(btn_add, SIGNAL(clicked()), this, SLOT(addGroup()));
234  controls_layout->addWidget(btn_add);
235  controls_layout->setAlignment(btn_add, Qt::AlignRight);
236 
237  // Add Controls to layout
238  layout->addLayout(controls_layout);
239 
240  // Set layout
241  content_widget->setLayout(layout);
242 
243  return content_widget;
244 }
245 
246 // ******************************************************************************************
247 // Displays data in the link_pairs_ data structure into a QtTableWidget
248 // ******************************************************************************************
250 {
251  // Disable Tree
252  groups_tree_->setUpdatesEnabled(false); // prevent table from updating until we are completely done
253  groups_tree_->setDisabled(true); // make sure we disable it so that the cellChanged event is not called
254  groups_tree_->clear(); // reset the tree
255 
256  // Display all groups by looping through them
257  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
258  {
259  loadGroupsTreeRecursive(group, nullptr);
260  }
261 
262  // Reenable Tree
263  groups_tree_->setUpdatesEnabled(true); // prevent table from updating until we are completely done
264  groups_tree_->setDisabled(false); // make sure we disable it so that the cellChanged event is not called
265 
266  // Show Edit button if there are things to edit
267  if (!config_data_->srdf_->groups_.empty())
268  {
269  btn_edit_->show();
270  btn_delete_->show();
271  }
272  else
273  {
274  btn_edit_->hide();
275  btn_delete_->hide();
276  }
277 
278  alterTree("expand");
279 }
280 
281 // ******************************************************************************************
282 // Recursively Adds Groups, and subgroups to groups...
283 // ******************************************************************************************
284 void PlanningGroupsWidget::loadGroupsTreeRecursive(srdf::Model::Group& group_it, QTreeWidgetItem* parent)
285 {
286  // Fonts for tree
287  const QFont top_level_font(QFont().defaultFamily(), 11, QFont::Bold);
288  const QFont type_font(QFont().defaultFamily(), 11, QFont::Normal, QFont::StyleItalic);
289 
290  QTreeWidgetItem* group;
291 
292  // Allow a subgroup to open into a whole new group
293  if (parent == nullptr)
294  {
295  group = new QTreeWidgetItem(groups_tree_);
296  group->setText(0, group_it.name_.c_str());
297  group->setFont(0, top_level_font);
298  group->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, GROUP)));
299  groups_tree_->addTopLevelItem(group);
300  }
301  else
302  {
303  group = new QTreeWidgetItem(parent);
304  group->setText(0, group_it.name_.c_str());
305  group->setFont(0, top_level_font);
306  group->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, GROUP)));
307  parent->addChild(group);
308  }
309 
310  // Joints --------------------------------------------------------------
311  QTreeWidgetItem* joints = new QTreeWidgetItem(group);
312  joints->setText(0, "Joints");
313  joints->setFont(0, type_font);
314  joints->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, JOINT)));
315  group->addChild(joints);
316 
317  // Retrieve pointer to the shared kinematic model
318  const moveit::core::RobotModelConstPtr& model = config_data_->getRobotModel();
319 
320  // Loop through all aval. joints
321  for (std::vector<std::string>::const_iterator joint_it = group_it.joints_.begin(); joint_it != group_it.joints_.end();
322  ++joint_it)
323  {
324  QTreeWidgetItem* j = new QTreeWidgetItem(joints);
325  j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, JOINT)));
326  std::string joint_name;
327 
328  // Get the type of joint this is
329  const moveit::core::JointModel* jm = model->getJointModel(*joint_it);
330  if (jm) // check if joint model was found
331  {
332  joint_name = *joint_it + " - " + jm->getTypeName();
333  }
334  else
335  {
336  joint_name = *joint_it;
337  }
338 
339  // Add to tree
340  j->setText(0, joint_name.c_str());
341  joints->addChild(j);
342  }
343 
344  // Links -------------------------------------------------------------
345  QTreeWidgetItem* links = new QTreeWidgetItem(group);
346  links->setText(0, "Links");
347  links->setFont(0, type_font);
348  links->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, LINK)));
349  group->addChild(links);
350 
351  // Loop through all aval. links
352  for (std::vector<std::string>::const_iterator joint_it = group_it.links_.begin(); joint_it != group_it.links_.end();
353  ++joint_it)
354  {
355  QTreeWidgetItem* j = new QTreeWidgetItem(links);
356  j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, LINK)));
357  j->setText(0, joint_it->c_str());
358  links->addChild(j);
359  }
360 
361  // Chains -------------------------------------------------------------
362  QTreeWidgetItem* chains = new QTreeWidgetItem(group);
363  chains->setText(0, "Chain");
364  chains->setFont(0, type_font);
365  chains->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, CHAIN)));
366  group->addChild(chains);
367 
368  // Warn if there is more than 1 chain per group
369  static bool warn_once = true;
370  if (group_it.chains_.size() > 1 && warn_once)
371  {
372  warn_once = false;
373  QMessageBox::warning(this, "Group with Multiple Kinematic Chains",
374  "Warning: this MoveIt Setup Assistant is only designed to handle one kinematic chain per "
375  "group. The loaded SRDF has more than one kinematic chain for a group. A possible loss of "
376  "data may occur.");
377  }
378 
379  // Loop through all aval. chains
380  for (std::vector<std::pair<std::string, std::string> >::const_iterator chain_it = group_it.chains_.begin();
381  chain_it != group_it.chains_.end(); ++chain_it)
382  {
383  QTreeWidgetItem* j = new QTreeWidgetItem(chains);
384  j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, CHAIN)));
385  j->setText(0, QString(chain_it->first.c_str()).append(" -> ").append(chain_it->second.c_str()));
386  chains->addChild(j);
387  }
388 
389  // Subgroups -------------------------------------------------------------
390  QTreeWidgetItem* subgroups = new QTreeWidgetItem(group);
391  subgroups->setText(0, "Subgroups");
392  subgroups->setFont(0, type_font);
393  subgroups->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, SUBGROUP)));
394  group->addChild(subgroups);
395 
396  // Loop through all aval. subgroups
397  for (std::vector<std::string>::iterator subgroup_it = group_it.subgroups_.begin();
398  subgroup_it != group_it.subgroups_.end(); ++subgroup_it)
399  {
400  // Find group with this subgroups' name
401 
402  srdf::Model::Group* searched_group = nullptr; // used for holding our search results
403 
404  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
405  {
406  if (group.name_ == *subgroup_it) // this is the group we are looking for
407  {
408  searched_group = &group; // convert to pointer from iterator
409  break; // we are done searching
410  }
411  }
412 
413  // Check if subgroup was found
414  if (searched_group == nullptr) // not found
415  {
416  QMessageBox::critical(this, "Error Loading SRDF",
417  QString("Subgroup '")
418  .append(subgroup_it->c_str())
419  .append("' of group '")
420  .append(group_it.name_.c_str())
421  .append("' not found. Your SRDF is invalid"));
422  return; // TODO: something better for error handling?
423  }
424 
425  // subgroup found!
426 
427  // Recurse this function for each new group
428  loadGroupsTreeRecursive(*searched_group, subgroups);
429  }
430 }
431 
432 // ******************************************************************************************
433 // Highlight the group of whatever element is selected in the tree view
434 // ******************************************************************************************
436 {
437  QTreeWidgetItem* item = groups_tree_->currentItem();
438 
439  // Check that something was actually selected
440  if (item == nullptr)
441  return;
442 
443  // Get the user custom properties of the currently selected row
444  PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
445 
446  // Unhighlight all links
447  Q_EMIT unhighlightAll();
448 
449  // Highlight the group
450  Q_EMIT(highlightGroup(plan_group.group_->name_));
451 }
452 
453 // ******************************************************************************************
454 // Edit whatever element is selected in the tree view
455 // ******************************************************************************************
457 {
458  QTreeWidgetItem* item = groups_tree_->currentItem();
459 
460  // Check that something was actually selected
461  if (item == nullptr)
462  return;
463 
464  adding_new_group_ = false;
465 
466  // Get the user custom properties of the currently selected row
467  PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
468 
469  switch (plan_group.type_)
470  {
471  case JOINT:
472  loadJointsScreen(plan_group.group_);
473  break;
474  case LINK:
475  loadLinksScreen(plan_group.group_);
476  break;
477  case CHAIN:
478  loadChainScreen(plan_group.group_);
479  break;
480  case SUBGROUP:
481  loadSubgroupsScreen(plan_group.group_);
482  break;
483  case GROUP:
484  loadGroupScreen(plan_group.group_);
485  break;
486  default:
487  QMessageBox::critical(this, "Error Loading", "An internal error has occured while loading.");
488  return;
489  }
490  return_screen_ = 0; // return to main screen directly
491  changeScreen(plan_group.type_);
492 }
493 
494 // ******************************************************************************************
495 // Load the popup screen with correct data for joints
496 // ******************************************************************************************
498 {
499  // Retrieve pointer to the shared kinematic model
500  const moveit::core::RobotModelConstPtr& model = config_data_->getRobotModel();
501 
502  // Get the names of the all joints
503  const std::vector<std::string>& joints = model->getJointModelNames();
504 
505  if (joints.empty())
506  {
507  QMessageBox::critical(this, "Error Loading", "No joints found for robot model");
508  return;
509  }
510 
511  // Set the available joints (left box)
512  joints_widget_->setAvailable(joints);
513 
514  // Set the selected joints (right box)
515  joints_widget_->setSelected(this_group->joints_);
516 
517  // Set the title
518  joints_widget_->title_->setText(
519  QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Joint Collection"));
520 
521  // Remember what is currently being edited so we can later save changes
522  current_edit_group_ = this_group->name_;
523 }
524 
525 // ******************************************************************************************
526 // Load the popup screen with correct data for links
527 // ******************************************************************************************
529 {
530  // Retrieve pointer to the shared kinematic model
531  const moveit::core::RobotModelConstPtr& model = config_data_->getRobotModel();
532 
533  // Get the names of the all links
534  const std::vector<std::string>& links = model->getLinkModelNames();
535 
536  if (links.empty())
537  {
538  QMessageBox::critical(this, "Error Loading", "No links found for robot model");
539  return;
540  }
541 
542  // Set the available links (left box)
543  links_widget_->setAvailable(links);
544 
545  // Set the selected links (right box)
546  links_widget_->setSelected(this_group->links_);
547 
548  // Set the title
549  links_widget_->title_->setText(
550  QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Link Collection"));
551 
552  // Remember what is currently being edited so we can later save changes
553  current_edit_group_ = this_group->name_;
554 }
555 
556 // ******************************************************************************************
557 // Load the popup screen with correct data for chains
558 // ******************************************************************************************
560 {
561  // Tell the kin chain widget to load up the chain from a kinematic model
563 
564  // Make sure there isn't more than 1 chain pair
565  if (this_group->chains_.size() > 1)
566  {
567  QMessageBox::warning(this, "Multiple Kinematic Chains",
568  "Warning: This setup assistant is only designed to handle "
569  "one kinematic chain per group. The loaded SRDF has more "
570  "than one kinematic chain for a group. A possible loss of "
571  "data may occur.");
572  }
573 
574  // Set the selected tip and base of chain if one exists
575  if (!this_group->chains_.empty())
576  {
577  chain_widget_->setSelected(this_group->chains_[0].first, this_group->chains_[0].second);
578  }
579 
580  // Set the title
581  chain_widget_->title_->setText(
582  QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Kinematic Chain"));
583 
584  // Remember what is currently being edited so we can later save changes
585  current_edit_group_ = this_group->name_;
586 }
587 
588 // ******************************************************************************************
589 // Load the popup screen with correct data for subgroups
590 // ******************************************************************************************
592 {
593  // Load all groups into the subgroup screen except the current group
594  std::vector<std::string> subgroups;
595 
596  // Display all groups by looping through them
597  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
598  {
599  if (group.name_ != this_group->name_) // do not include current group
600  {
601  // add to available subgroups list
602  subgroups.push_back(group.name_);
603  }
604  }
605 
606  // Set the available subgroups (left box)
607  subgroups_widget_->setAvailable(subgroups);
608 
609  // Set the selected subgroups (right box)
611 
612  // Set the title
613  subgroups_widget_->title_->setText(
614  QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Subgroups"));
615 
616  // Remember what is currently being edited so we can later save changes
617  current_edit_group_ = this_group->name_;
618 }
619 
620 // ******************************************************************************************
621 // Load the popup screen with correct data for groups
622 // ******************************************************************************************
624 {
625  // Load the avail kin solvers. This function only runs once
627 
628  if (this_group == nullptr) // this is a new screen
629  {
630  current_edit_group_.clear(); // provide a blank group name
631  group_edit_widget_->title_->setText("Create New Planning Group");
633  group_edit_widget_->new_buttons_widget_->show(); // helps user choose next step
634  group_edit_widget_->btn_save_->hide(); // this is only for edit mode
635  }
636  else // load the group name into the widget
637  {
638  current_edit_group_ = this_group->name_;
639  group_edit_widget_->title_->setText(
640  QString("Edit Planning Group '").append(current_edit_group_.c_str()).append("'"));
642  group_edit_widget_->new_buttons_widget_->hide(); // not necessary for existing groups
643  group_edit_widget_->btn_save_->show(); // this is only for edit mode
644  }
645 
646  // Set the data in the edit box
648 }
649 
650 // ******************************************************************************************
651 // Delete a group
652 // ******************************************************************************************
654 {
655  std::string group = current_edit_group_;
656  if (group.empty())
657  {
658  QTreeWidgetItem* item = groups_tree_->currentItem();
659  // Check that something was actually selected
660  if (item == nullptr)
661  return;
662  // Get the user custom properties of the currently selected row
663  PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
664  if (plan_group.group_)
665  group = plan_group.group_->name_;
666  }
667  else
668  current_edit_group_.clear();
669  if (group.empty())
670  return;
671 
672  // Find the group we are editing based on the goup name string
673  srdf::Model::Group* searched_group = config_data_->findGroupByName(group);
674 
675  // Confirm user wants to delete group
676  if (QMessageBox::question(this, "Confirm Group Deletion",
677  QString("Are you sure you want to delete the planning group '")
678  .append(searched_group->name_.c_str())
679  .append("'? This will also delete all references in subgroups, robot poses and end "
680  "effectors."),
681  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
682  {
683  return;
684  }
685 
686  // delete all robot poses that use that group's name
687  bool have_confirmed_group_state_deletion = false;
688  bool have_deleted_group_state = true;
689  while (have_deleted_group_state)
690  {
691  have_deleted_group_state = false;
692  for (std::vector<srdf::Model::GroupState>::iterator pose_it = config_data_->srdf_->group_states_.begin();
693  pose_it != config_data_->srdf_->group_states_.end(); ++pose_it)
694  {
695  // check if this group state depends on the currently being deleted group
696  if (pose_it->group_ == searched_group->name_)
697  {
698  if (!have_confirmed_group_state_deletion)
699  {
700  have_confirmed_group_state_deletion = true;
701 
702  // confirm the user wants to delete group states
703  if (QMessageBox::question(
704  this, "Confirm Group State Deletion",
705  QString("The group that is about to be deleted has robot poses (robot states) that depend on this "
706  "group. Are you sure you want to delete this group as well as all dependend robot poses?"),
707  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
708  {
709  return;
710  }
711  }
713 
714  // the user has confirmed, now delete this group state
715  config_data_->srdf_->group_states_.erase(pose_it);
716  have_deleted_group_state = true;
717  break; // you can only delete 1 item in vector before invalidating iterator
718  }
719  }
720  }
721 
722  // delete all end effectors that use that group's name
723  bool have_confirmed_end_effector_deletion = false;
724  bool have_deleted_end_effector = true;
725  while (have_deleted_end_effector)
726  {
727  have_deleted_end_effector = false;
728  for (std::vector<srdf::Model::EndEffector>::iterator effector_it = config_data_->srdf_->end_effectors_.begin();
729  effector_it != config_data_->srdf_->end_effectors_.end(); ++effector_it)
730  {
731  // check if this group state depends on the currently being deleted group
732  if (effector_it->component_group_ == searched_group->name_)
733  {
734  if (!have_confirmed_end_effector_deletion)
735  {
736  have_confirmed_end_effector_deletion = true;
737 
738  // confirm the user wants to delete group states
739  if (QMessageBox::question(
740  this, "Confirm End Effector Deletion",
741  QString("The group that is about to be deleted has end effectors (grippers) that depend on this "
742  "group. Are you sure you want to delete this group as well as all dependend end effectors?"),
743  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
744  {
745  return;
746  }
747  }
749 
750  // the user has confirmed, now delete this group state
751  config_data_->srdf_->end_effectors_.erase(effector_it);
752  have_deleted_end_effector = true;
753  break; // you can only delete 1 item in vector before invalidating iterator
754  }
755  }
756  }
757 
759 
760  // delete actual group
761  for (std::vector<srdf::Model::Group>::iterator group_it = config_data_->srdf_->groups_.begin();
762  group_it != config_data_->srdf_->groups_.end(); ++group_it)
763  {
764  // check if this is the group we want to delete
765  if (group_it->name_ == group) // string match
766  {
767  config_data_->srdf_->groups_.erase(group_it);
768  break;
769  }
770  }
771 
772  // loop again to delete all subgroup references
773  for (srdf::Model::Group& group_it : config_data_->srdf_->groups_)
774  {
775  // delete all subgroup references
776  bool deleted_subgroup = true;
777  while (deleted_subgroup)
778  {
779  deleted_subgroup = false;
780 
781  // check if the subgroups reference our deleted group
782  for (std::vector<std::string>::iterator subgroup_it = group_it.subgroups_.begin();
783  subgroup_it != group_it.subgroups_.end(); ++subgroup_it)
784  {
785  // Check if that subgroup references the deletion group. if so, delete it
786  if (subgroup_it->compare(group) == 0) // same name
787  {
788  group_it.subgroups_.erase(subgroup_it); // delete
789  deleted_subgroup = true;
790  break;
791  }
792  }
793  }
794  }
795 
796  // Switch to main screen
797  showMainScreen();
798 
799  // Reload main screen table
800  loadGroupsTree();
801 
802  // Update the kinematic model with changes
803  config_data_->updateRobotModel();
804 }
805 
806 // ******************************************************************************************
807 // Create a new, empty group
808 // ******************************************************************************************
810 {
811  adding_new_group_ = true;
812 
813  // Load the data
814  loadGroupScreen(nullptr); // NULL indicates this is a new group, not an existing one
815 
816  // Switch to screen
818 }
819 
820 // ******************************************************************************************
821 // Call when joints edit sceen is done and needs to be saved
822 // ******************************************************************************************
824 {
825  // Find the group we are editing based on the goup name string
826  srdf::Model::Group* searched_group = config_data_->findGroupByName(current_edit_group_);
827 
828  // clear the old data
829  searched_group->joints_.clear();
830 
831  // copy the data
832  for (int i = 0; i < joints_widget_->selected_data_table_->rowCount(); ++i)
833  {
834  searched_group->joints_.push_back(joints_widget_->selected_data_table_->item(i, 0)->text().toStdString());
835  }
836 
837  // Switch to main screen
838  showMainScreen();
839 
840  // Reload main screen table
842 
843  // Update the kinematic model with changes
844  config_data_->updateRobotModel();
846 }
847 
848 // ******************************************************************************************
849 // Call when links edit sceen is done and needs to be saved
850 // ******************************************************************************************
852 {
853  // Find the group we are editing based on the goup name string
854  srdf::Model::Group* searched_group = config_data_->findGroupByName(current_edit_group_);
855 
856  // Find the group we are editing based on the goup name string
857  // clear the old data
858  searched_group->links_.clear();
859 
860  // copy the data
861  for (int i = 0; i < links_widget_->selected_data_table_->rowCount(); ++i)
862  {
863  searched_group->links_.push_back(links_widget_->selected_data_table_->item(i, 0)->text().toStdString());
864  }
865 
866  // Switch to main screen
867  showMainScreen();
868 
869  // Reload main screen table
870  loadGroupsTree();
871 
872  // Update the kinematic model with changes
873  config_data_->updateRobotModel();
875 }
876 
877 // ******************************************************************************************
878 // Call when chains edit sceen is done and needs to be saved
879 // ******************************************************************************************
881 {
882  // Find the group we are editing based on the goup name string
883  srdf::Model::Group* searched_group = config_data_->findGroupByName(current_edit_group_);
884 
885  // Get a reference to the supplied strings
886  const std::string& tip = chain_widget_->tip_link_field_->text().trimmed().toStdString();
887  const std::string& base = chain_widget_->base_link_field_->text().trimmed().toStdString();
888 
889  // Check that box the tip and base, or neither, have text
890  if ((!tip.empty() && base.empty()) || (tip.empty() && !base.empty()))
891  {
892  QMessageBox::warning(this, "Error Saving",
893  "You must specify a link for both the base and tip, or leave both "
894  "blank.");
895  return;
896  }
897 
898  // Check that both given links are valid links, unless they are both blank
899  if (!tip.empty() && !base.empty())
900  {
901  // Check that they are not the same link
902  if (tip.compare(base) == 0) // they are same
903  {
904  QMessageBox::warning(this, "Error Saving", "Tip and base link cannot be the same link.");
905  return;
906  }
907 
908  bool found_tip = false;
909  bool found_base = false;
910  const std::vector<std::string>& links = config_data_->getRobotModel()->getLinkModelNames();
911 
912  for (const std::string& link : links)
913  {
914  // Check if string matches either of user specified links
915  if (link.compare(tip) == 0) // they are same
916  found_tip = true;
917  else if (link.compare(base) == 0) // they are same
918  found_base = true;
919 
920  // Check if we are done searching
921  if (found_tip && found_base)
922  break;
923  }
924 
925  // Check if we found both links
926  if (!found_tip || !found_base)
927  {
928  QMessageBox::warning(this, "Error Saving", "Tip or base link(s) were not found in kinematic chain.");
929  return;
930  }
931  }
932 
933  // clear the old data
934  searched_group->chains_.clear();
935 
936  // Save the data if there is data to save
937  if (!tip.empty() && !base.empty())
938  {
939  searched_group->chains_.push_back(std::pair<std::string, std::string>(base, tip));
940  }
941 
942  // Switch to main screen
943  showMainScreen();
944 
945  // Reload main screen table
946  loadGroupsTree();
947 
948  // Update the kinematic model with changes
949  config_data_->updateRobotModel();
951 }
952 
953 // ******************************************************************************************
954 // Call when subgroups edit sceen is done and needs to be saved
955 // ******************************************************************************************
957 {
958  // Find the group we are editing based on the goup name string
959  srdf::Model::Group* searched_group = config_data_->findGroupByName(current_edit_group_);
960 
961  // Check for cycles -------------------------------
962 
963  // Create vector index of all nodes
964  std::map<std::string, int> group_nodes;
965 
966  // Create vector of all nodes for use as id's
967  int node_id = 0;
968  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
969  {
970  // Add string to vector
971  group_nodes.insert(std::pair<std::string, int>(group.name_, node_id));
972  ++node_id;
973  }
974 
975  // Create the empty graph
976  typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> Graph;
977  Graph g(group_nodes.size());
978 
979  // Traverse the group list again, this time inserting subgroups into graph
980  int from_id = 0; // track the outer node we are on to reduce searches performed
981  for (srdf::Model::Group& group : config_data_->srdf_->groups_)
982  {
983  // Check if group_it is same as current group
984  if (group.name_ == searched_group->name_) // yes, same group
985  {
986  // add new subgroup list from widget, not old one. this way we can check for new cycles
987  for (int i = 0; i < subgroups_widget_->selected_data_table_->rowCount(); ++i)
988  {
989  // Get std::string of subgroup
990  const std::string to_string = subgroups_widget_->selected_data_table_->item(i, 0)->text().toStdString();
991 
992  // convert subgroup string to associated id
993  int to_id = group_nodes[to_string];
994 
995  // Add edge from from_id to to_id
996  add_edge(from_id, to_id, g);
997  }
998  }
999  else // this group is not the group we are editing, so just add subgroups from memory
1000  {
1001  // add new subgroup list from widget, not old one. this way we can check for new cycles
1002  for (const std::string& to_string : group.subgroups_)
1003  {
1004  // Get std::string of subgroup
1005  // convert subgroup string to associated id
1006  int to_id = group_nodes[to_string];
1007 
1008  // Add edge from from_id to to_id
1009  add_edge(from_id, to_id, g);
1010  }
1011  }
1012 
1013  ++from_id;
1014  }
1015 
1016  // Check for cycles
1017  bool has_cycle = false;
1018  CycleDetector vis(has_cycle);
1019  boost::depth_first_search(g, visitor(vis));
1020 
1021  if (has_cycle)
1022  {
1023  // report to user the error
1024  QMessageBox::warning(this, "Error Saving", "Depth first search reveals a cycle in the subgroups");
1025  return;
1026  }
1027 
1028  // clear the old data
1029  searched_group->subgroups_.clear();
1030 
1031  // copy the data
1032  for (int i = 0; i < subgroups_widget_->selected_data_table_->rowCount(); ++i)
1033  {
1034  searched_group->subgroups_.push_back(subgroups_widget_->selected_data_table_->item(i, 0)->text().toStdString());
1035  }
1036 
1037  // Switch to main screen
1038  showMainScreen();
1039 
1040  // Reload main screen table
1041  loadGroupsTree();
1042 
1043  // Update the kinematic model with changes
1044  config_data_->updateRobotModel();
1046 }
1047 
1048 // ******************************************************************************************
1049 // Call when groups edit sceen is done and needs to be saved
1050 // ******************************************************************************************
1052 {
1053  // Get a reference to the supplied strings
1054  const std::string& group_name = group_edit_widget_->group_name_field_->text().trimmed().toStdString();
1055  const std::string& kinematics_solver = group_edit_widget_->kinematics_solver_field_->currentText().toStdString();
1056  const std::string& default_planner = group_edit_widget_->default_planner_field_->currentText().toStdString();
1057  const std::string& kinematics_resolution = group_edit_widget_->kinematics_resolution_field_->text().toStdString();
1058  const std::string& kinematics_timeout = group_edit_widget_->kinematics_timeout_field_->text().toStdString();
1059  double goal_joint_tolerance = group_edit_widget_->goal_joint_tolerance_field_->value();
1060  double goal_position_tolerance = group_edit_widget_->goal_position_tolerance_field_->value();
1061  double goal_orientation_tolerance = group_edit_widget_->goal_orientation_tolerance_field_->value();
1062  const std::string& kinematics_parameters_file =
1064 
1065  // Used for editing existing groups
1066  srdf::Model::Group* searched_group = nullptr;
1067 
1068  // Check that a valid group name has been given
1069  if (group_name.empty())
1070  {
1071  QMessageBox::warning(this, "Error Saving", "A name must be given for the group!");
1072  return false;
1073  }
1074 
1075  // Check if this is an existing group
1076  if (!current_edit_group_.empty())
1077  {
1078  // Find the group we are editing based on the goup name string
1079  searched_group = config_data_->findGroupByName(current_edit_group_);
1080  }
1081 
1082  // Check that the group name is unique
1083  for (const auto& group : config_data_->srdf_->groups_)
1084  {
1085  if (group.name_.compare(group_name) == 0) // the names are the same
1086  {
1087  // is this our existing group? check if group pointers are same
1088  if (&group != searched_group)
1089  {
1090  QMessageBox::warning(this, "Error Saving", "A group already exists with that name!");
1091  return false;
1092  }
1093  }
1094  }
1095 
1096  // Check that the resolution is an double number
1097  double kinematics_resolution_double;
1098  try
1099  {
1100  kinematics_resolution_double = boost::lexical_cast<double>(kinematics_resolution);
1101  }
1102  catch (boost::bad_lexical_cast&)
1103  {
1104  QMessageBox::warning(this, "Error Saving", "Unable to convert kinematics resolution to a double number.");
1105  return false;
1106  }
1107 
1108  // Check that the timeout is a double number
1109  double kinematics_timeout_double;
1110  try
1111  {
1112  kinematics_timeout_double = boost::lexical_cast<double>(kinematics_timeout);
1113  }
1114  catch (boost::bad_lexical_cast&)
1115  {
1116  QMessageBox::warning(this, "Error Saving", "Unable to convert kinematics solver timeout to a double number.");
1117  return false;
1118  }
1119 
1120  // Check that all numbers are >0
1121  if (kinematics_resolution_double <= 0)
1122  {
1123  QMessageBox::warning(this, "Error Saving", "Kinematics solver search resolution must be greater than 0.");
1124  return false;
1125  }
1126  if (kinematics_timeout_double <= 0)
1127  {
1128  QMessageBox::warning(this, "Error Saving", "Kinematics solver search timeout must be greater than 0.");
1129  return false;
1130  }
1131  if (goal_joint_tolerance <= 0)
1132  {
1133  QMessageBox::warning(this, "Error Saving", "Goal joint tolerance must be greater than 0.");
1134  return false;
1135  }
1136  if (goal_position_tolerance <= 0)
1137  {
1138  QMessageBox::warning(this, "Error Saving", "Goal position tolerance must be greater than 0.");
1139  return false;
1140  }
1141  if (goal_orientation_tolerance <= 0)
1142  {
1143  QMessageBox::warning(this, "Error Saving", "Goal orientation tolerance must be greater than 0.");
1144  return false;
1145  }
1146 
1147  adding_new_group_ = false;
1148 
1149  // Save the new group name or create the new group
1150  if (searched_group == nullptr) // create new
1151  {
1152  srdf::Model::Group new_group;
1153  new_group.name_ = group_name;
1154  config_data_->srdf_->groups_.push_back(new_group);
1155  adding_new_group_ = true; // remember this group is new
1157  }
1158  else
1159  {
1160  // Remember old group name
1161  const std::string old_group_name = searched_group->name_;
1162 
1163  // Change group name
1164  searched_group->name_ = group_name;
1165 
1166  // Change all references to this group name in other subgroups
1167  // Loop through every group
1168  for (std::vector<srdf::Model::Group>::iterator group_it = config_data_->srdf_->groups_.begin();
1169  group_it != config_data_->srdf_->groups_.end(); ++group_it)
1170  {
1171  // Loop through every subgroup
1172  for (std::string& subgroup : group_it->subgroups_)
1173  {
1174  // Check if that subgroup references old group name. if so, update it
1175  if (subgroup.compare(old_group_name) == 0) // same name
1176  {
1177  subgroup.assign(group_name); // updated
1179  }
1180  }
1181  }
1182 
1183  // Change all references to this group name in the end effectors screen
1184  for (std::vector<srdf::Model::EndEffector>::iterator eef_it = config_data_->srdf_->end_effectors_.begin();
1185  eef_it != config_data_->srdf_->end_effectors_.end(); ++eef_it)
1186  {
1187  // Check if this eef's parent group references old group name. if so, update it
1188  if (eef_it->parent_group_.compare(old_group_name) == 0) // same name
1189  {
1190  ROS_DEBUG_STREAM_NAMED("setup_assistant",
1191  "Changed eef '" << eef_it->name_ << "' to new parent group name " << group_name);
1192  eef_it->parent_group_ = group_name; // updated
1194  }
1195 
1196  // Check if this eef's group references old group name. if so, update it
1197  if (eef_it->component_group_.compare(old_group_name) == 0) // same name
1198  {
1199  ROS_DEBUG_STREAM_NAMED("setup_assistant",
1200  "Changed eef '" << eef_it->name_ << "' to new group name " << group_name);
1201  eef_it->component_group_ = group_name; // updated
1203  }
1204  }
1205 
1206  // Change all references to this group name in the robot poses screen
1207  for (std::vector<srdf::Model::GroupState>::iterator state_it = config_data_->srdf_->group_states_.begin();
1208  state_it != config_data_->srdf_->group_states_.end(); ++state_it)
1209  {
1210  // Check if this eef's parent group references old group name. if so, update it
1211  if (state_it->group_.compare(old_group_name) == 0) // same name
1212  {
1213  ROS_DEBUG_STREAM_NAMED("setup_assistant", "Changed group state group '" << state_it->group_
1214  << "' to new parent group name "
1215  << group_name);
1216  state_it->group_ = group_name; // updated
1218  }
1219  }
1220 
1221  // Now update the robot model based on our changed to the SRDF
1222  config_data_->updateRobotModel();
1223  }
1224 
1225  // Save the group meta data
1226  config_data_->group_meta_data_[group_name].kinematics_solver_ = kinematics_solver;
1227  config_data_->group_meta_data_[group_name].kinematics_solver_search_resolution_ = kinematics_resolution_double;
1228  config_data_->group_meta_data_[group_name].kinematics_solver_timeout_ = kinematics_timeout_double;
1229  config_data_->group_meta_data_[group_name].goal_joint_tolerance_ = goal_joint_tolerance;
1230  config_data_->group_meta_data_[group_name].goal_position_tolerance_ = goal_position_tolerance;
1231  config_data_->group_meta_data_[group_name].goal_orientation_tolerance_ = goal_orientation_tolerance;
1232  config_data_->group_meta_data_[group_name].kinematics_parameters_file_ = kinematics_parameters_file;
1233  config_data_->group_meta_data_[group_name].default_planner_ = (default_planner == "None" ? "" : default_planner);
1235 
1236  // Reload main screen table
1237  loadGroupsTree();
1238 
1239  // Update the current edit group so that we can proceed to the next screen, if user desires
1240  current_edit_group_ = group_name;
1241 
1242  return true;
1243 }
1244 
1245 // ******************************************************************************************
1246 // Call when a new group is created and ready to progress to next screen
1247 // ******************************************************************************************
1249 {
1250  // Save the group
1251  if (!saveGroupScreen())
1252  return;
1253 
1254  // Switch to main screen
1255  showMainScreen();
1256 }
1257 
1258 // ******************************************************************************************
1259 // Call when a new group is created and ready to progress to next screen
1260 // ******************************************************************************************
1262 {
1263  // Save the group
1264  if (!saveGroupScreen())
1265  return;
1266 
1267  // Find the group we are editing based on the goup name string
1270 
1271  // Switch to screen
1273 }
1274 
1275 // ******************************************************************************************
1276 // Call when a new group is created and ready to progress to next screen
1277 // ******************************************************************************************
1279 {
1280  // Save the group
1281  if (!saveGroupScreen())
1282  return;
1283 
1284  // Find the group we are editing based on the goup name string
1287 
1288  // Switch to screen
1289  changeScreen(LINK);
1290 }
1291 
1292 // ******************************************************************************************
1293 // Call when a new group is created and ready to progress to next screen
1294 // ******************************************************************************************
1296 {
1297  // Save the group
1298  if (!saveGroupScreen())
1299  return;
1300 
1301  // Find the group we are editing based on the goup name string
1304 
1305  // Switch to screen
1307 }
1308 
1309 // ******************************************************************************************
1310 // Call when a new group is created and ready to progress to next screen
1311 // ******************************************************************************************
1313 {
1314  // Save the group
1315  if (!saveGroupScreen())
1316  return;
1317 
1318  // Find the group we are editing based on the goup name string
1321 
1322  // Switch to screen
1324 }
1325 
1326 // ******************************************************************************************
1327 // Call when edit screen is canceled
1328 // ******************************************************************************************
1330 {
1331  if (return_screen_)
1332  {
1334  return_screen_ = 0;
1335  return;
1336  }
1337  if (!current_edit_group_.empty() && adding_new_group_)
1338  {
1339  srdf::Model::Group* editing = config_data_->findGroupByName(current_edit_group_);
1340  if (editing && editing->joints_.empty() && editing->links_.empty() && editing->chains_.empty() &&
1341  editing->subgroups_.empty())
1342  {
1343  config_data_->group_meta_data_.erase(editing->name_);
1344  for (std::vector<srdf::Model::Group>::iterator group_it = config_data_->srdf_->groups_.begin();
1345  group_it != config_data_->srdf_->groups_.end(); ++group_it)
1346  if (&(*group_it) == editing)
1347  {
1348  config_data_->srdf_->groups_.erase(group_it);
1349  break;
1350  }
1351  current_edit_group_.clear();
1352  // Load the data to the tree
1353  loadGroupsTree();
1354  }
1355  }
1356 
1357  // Switch to main screen
1358  showMainScreen();
1359 }
1360 
1361 // ******************************************************************************************
1362 // Called when setup assistant navigation switches to this screen
1363 // ******************************************************************************************
1365 {
1366  // Show the current groups screen
1367  showMainScreen();
1368 
1369  // Load the data to the tree
1370  loadGroupsTree();
1371 }
1372 
1373 // ******************************************************************************************
1374 // Expand/Collapse Tree
1375 // ******************************************************************************************
1376 void PlanningGroupsWidget::alterTree(const QString& link)
1377 {
1378  if (link.contains("expand"))
1379  groups_tree_->expandAll();
1380  else
1381  groups_tree_->collapseAll();
1382 }
1383 
1384 // ******************************************************************************************
1385 // Switch to current groups view
1386 // ******************************************************************************************
1388 {
1389  stacked_widget_->setCurrentIndex(0);
1390 
1391  // Announce that this widget is not in modal mode anymore
1392  Q_EMIT isModal(false);
1393 }
1394 
1395 // ******************************************************************************************
1396 // Switch which screen is being shown
1397 // ******************************************************************************************
1398 void PlanningGroupsWidget::changeScreen(int index)
1399 {
1400  stacked_widget_->setCurrentIndex(index);
1401 
1402  // Announce this widget's mode
1403  Q_EMIT isModal(index != 0);
1404 }
1405 
1406 // ******************************************************************************************
1407 // Called from Double List widget to highlight a link
1408 // ******************************************************************************************
1409 void PlanningGroupsWidget::previewSelectedLink(const std::vector<std::string>& links)
1410 {
1411  // Unhighlight all links
1412  Q_EMIT unhighlightAll();
1413 
1414  for (const std::string& link : links)
1415  {
1416  if (link.empty())
1417  {
1418  continue;
1419  }
1420 
1421  // Highlight link
1422  Q_EMIT highlightLink(link, QColor(255, 0, 0));
1423  }
1424 }
1425 
1426 // ******************************************************************************************
1427 // Called from Double List widget to highlight joints
1428 // ******************************************************************************************
1429 void PlanningGroupsWidget::previewSelectedJoints(const std::vector<std::string>& joints)
1431  // Unhighlight all links
1432  Q_EMIT unhighlightAll();
1433 
1434  for (const std::string& joint : joints)
1435  {
1436  const moveit::core::JointModel* joint_model = config_data_->getRobotModel()->getJointModel(joint);
1437 
1438  // Check that a joint model was found
1439  if (!joint_model)
1440  {
1441  continue;
1442  }
1443 
1444  // Get the name of the link
1445  const std::string link = joint_model->getChildLinkModel()->getName();
1446 
1447  if (link.empty())
1448  {
1449  continue;
1450  }
1451 
1452  // Highlight link
1453  Q_EMIT highlightLink(link, QColor(255, 0, 0));
1454  }
1455 }
1456 
1457 // ******************************************************************************************
1458 // Called from Double List widget to highlight a subgroup
1459 // ******************************************************************************************
1460 void PlanningGroupsWidget::previewSelectedSubgroup(const std::vector<std::string>& groups)
1462  // Unhighlight all links
1463  Q_EMIT unhighlightAll();
1464 
1465  for (const std::string& group : groups)
1466  {
1467  // Highlight group
1468  Q_EMIT highlightGroup(group);
1469  }
1470 }
1471 
1472 } // namespace moveit_setup_assistant
1473 
1474 // ******************************************************************************************
1475 // ******************************************************************************************
1476 // CLASS
1477 // ******************************************************************************************
1478 // ******************************************************************************************
1479 
1481  : group_(group), type_(type)
1482 {
1483 }
moveit_setup_assistant::PlanningGroupsWidget::loadChainScreen
void loadChainScreen(srdf::Model::Group *this_group)
Definition: planning_groups_widget.cpp:591
moveit_setup_assistant::PlanningGroupsWidget::saveLinksScreen
void saveLinksScreen()
Definition: planning_groups_widget.cpp:883
PlanGroupType
Definition: planning_groups_widget.h:215
moveit_setup_assistant::GroupType
GroupType
Definition: planning_groups_widget.h:62
moveit_setup_assistant::GroupEditWidget::title_
QLabel * title_
Definition: group_edit_widget.h:81
moveit_setup_assistant::GroupEditWidget::new_buttons_widget_
QWidget * new_buttons_widget_
Definition: group_edit_widget.h:93
moveit_setup_assistant::PerceptionWidget::config_data_
moveit_setup_assistant::MoveItConfigDataPtr config_data_
Contains all the configuration data for the setup assistant.
Definition: perception_widget.h:121
moveit_setup_assistant::PlanningGroupsWidget::addGroup
void addGroup()
Create a new, empty group.
Definition: planning_groups_widget.cpp:841
moveit_setup_assistant::PlanningGroupsWidget::deleteGroup
void deleteGroup()
Definition: planning_groups_widget.cpp:685
moveit_setup_assistant::GroupEditWidget::kinematics_resolution_field_
QLineEdit * kinematics_resolution_field_
Definition: group_edit_widget.h:84
moveit_setup_assistant::SUBGROUP
@ SUBGROUP
Definition: planning_groups_widget.h:67
moveit_setup_assistant::PlanningGroupsWidget::previewSelectedLink
void previewSelectedLink(const std::vector< std::string > &links)
Called from Double List widget to highlight a link.
Definition: planning_groups_widget.cpp:1441
moveit_setup_assistant::DoubleListWidget::setSelected
void setSelected(const std::vector< std::string > &items)
Set the right box.
Definition: double_list_widget.cpp:216
moveit_setup_assistant::PlanningGroupsWidget::previewSelected
void previewSelected()
Highlight the group of whatever element is selected in the tree view.
Definition: planning_groups_widget.cpp:467
moveit_setup_assistant::DoubleListWidget
Definition: double_list_widget.h:51
moveit_setup_assistant::PlanningGroupsWidget::saveChainScreen
void saveChainScreen()
Definition: planning_groups_widget.cpp:912
double_list_widget.h
moveit_setup_assistant::GroupEditWidget::goal_joint_tolerance_field_
QDoubleSpinBox * goal_joint_tolerance_field_
Definition: group_edit_widget.h:86
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreenJoints
void saveGroupScreenJoints()
Definition: planning_groups_widget.cpp:1293
moveit_setup_assistant::CHAIN
@ CHAIN
Definition: planning_groups_widget.h:66
moveit_setup_assistant::DoubleListWidget::selected_data_table_
QTableWidget * selected_data_table_
Definition: double_list_widget.h:86
ROS_DEBUG_STREAM_NAMED
#define ROS_DEBUG_STREAM_NAMED(name, args)
moveit_setup_assistant::KinematicChainWidget::setAvailable
void setAvailable()
Loads the availble data list.
Definition: kinematic_chain_widget.cpp:173
moveit_setup_assistant::GroupEditWidget::kinematics_solver_field_
QComboBox * kinematics_solver_field_
Definition: group_edit_widget.h:83
srdf::Model::Group::chains_
std::vector< std::pair< std::string, std::string > > chains_
moveit_setup_assistant::MoveItConfigData::POSES
@ POSES
Definition: moveit_config_data.h:233
moveit_setup_assistant::KinematicChainWidget::base_link_field_
QLineEdit * base_link_field_
Definition: kinematic_chain_widget.h:83
moveit_setup_assistant::PlanningGroupsWidget::links_widget_
DoubleListWidget * links_widget_
Definition: planning_groups_widget.h:160
moveit_setup_assistant::GroupEditWidget::btn_delete_
QPushButton * btn_delete_
Definition: group_edit_widget.h:91
moveit_setup_assistant::PlanningGroupsWidget::loadGroupScreen
void loadGroupScreen(srdf::Model::Group *this_group)
Definition: planning_groups_widget.cpp:655
moveit_setup_assistant::GROUP
@ GROUP
Definition: planning_groups_widget.h:68
moveit_setup_assistant::PlanningGroupsWidget::editSelected
void editSelected()
Edit whatever element is selected in the tree view.
Definition: planning_groups_widget.cpp:488
moveit_setup_assistant::JOINT
@ JOINT
Definition: planning_groups_widget.h:64
moveit_setup_assistant::LINK
@ LINK
Definition: planning_groups_widget.h:65
moveit_setup_assistant::GroupEditWidget::kinematics_timeout_field_
QLineEdit * kinematics_timeout_field_
Definition: group_edit_widget.h:85
moveit_setup_assistant::GroupEditWidget::loadKinematicPlannersComboBox
void loadKinematicPlannersComboBox()
Populate the combo dropdown box with kinematic planners.
Definition: group_edit_widget.cpp:383
moveit_setup_assistant::CycleDetector::backEdge
void backEdge(Edge, Graph &)
Definition: planning_groups_widget.cpp:119
moveit_setup_assistant::GroupEditWidget::goal_position_tolerance_field_
QDoubleSpinBox * goal_position_tolerance_field_
Definition: group_edit_widget.h:87
kinematic_chain_widget.h
moveit_setup_assistant::GroupEditWidget
Definition: group_edit_widget.h:52
SetupScreenWidget::isModal
void isModal(bool isModal)
Event for when the current screen is in modal view. Essential disabled the left navigation.
SetupScreenWidget::highlightLink
void highlightLink(const std::string &name, const QColor &)
Event for telling rviz to highlight a link of the robot.
moveit::core::JointModel::getChildLinkModel
const LinkModel * getChildLinkModel() const
moveit_setup_assistant::PlanningGroupsWidget::btn_edit_
QPushButton * btn_edit_
Show and hide edit button.
Definition: planning_groups_widget.h:152
moveit_setup_assistant::PlanningGroupsWidget::joints_widget_
DoubleListWidget * joints_widget_
Definition: planning_groups_widget.h:159
moveit_setup_assistant::PlanningGroupsWidget::current_edit_group_
std::string current_edit_group_
Remember what group we are editing when an edit screen is being shown.
Definition: planning_groups_widget.h:173
srdf::Model::Group::name_
std::string name_
PlanGroupType::type_
moveit_setup_assistant::GroupType type_
Definition: planning_groups_widget.h:230
moveit_setup_assistant::GroupEditWidget::btn_save_
QPushButton * btn_save_
Definition: group_edit_widget.h:92
moveit_setup_assistant::MoveItConfigData::GROUPS
@ GROUPS
Definition: moveit_config_data.h:230
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreenLinks
void saveGroupScreenLinks()
Definition: planning_groups_widget.cpp:1310
moveit_setup_assistant::MoveItConfigData::END_EFFECTORS
@ END_EFFECTORS
Definition: moveit_config_data.h:234
moveit_setup_assistant::PlanningGroupsWidget::chain_widget_
KinematicChainWidget * chain_widget_
Definition: planning_groups_widget.h:162
moveit_setup_assistant::PlanningGroupsWidget::return_screen_
int return_screen_
Remember to which editing screen we should return on Cancel.
Definition: planning_groups_widget.h:176
moveit_setup_assistant::PlanningGroupsWidget::loadGroupsTreeRecursive
void loadGroupsTreeRecursive(srdf::Model::Group &group_it, QTreeWidgetItem *parent)
Recursively build the SRDF tree.
Definition: planning_groups_widget.cpp:316
moveit_setup_assistant::KinematicChainWidget::title_
QLabel * title_
Definition: kinematic_chain_widget.h:81
srdf::Model::Group::links_
std::vector< std::string > links_
group_edit_widget.h
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreenEdit
void saveGroupScreenEdit()
Definition: planning_groups_widget.cpp:1280
moveit_setup_assistant::PlanningGroupsWidget::previewSelectedSubgroup
void previewSelectedSubgroup(const std::vector< std::string > &groups)
Called from Double List widget to highlight a subgroup.
Definition: planning_groups_widget.cpp:1492
SetupScreenWidget
Definition: setup_screen_widget.h:44
moveit_setup_assistant::KinematicChainWidget::tip_link_field_
QLineEdit * tip_link_field_
Definition: kinematic_chain_widget.h:84
moveit_setup_assistant::GroupEditWidget::goal_orientation_tolerance_field_
QDoubleSpinBox * goal_orientation_tolerance_field_
Definition: group_edit_widget.h:88
moveit_setup_assistant::GroupEditWidget::kinematics_parameters_file_field_
QLineEdit * kinematics_parameters_file_field_
Definition: group_edit_widget.h:89
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreenChain
void saveGroupScreenChain()
Definition: planning_groups_widget.cpp:1327
moveit_setup_assistant::PlanningGroupsWidget::group_edit_widget_
GroupEditWidget * group_edit_widget_
Definition: planning_groups_widget.h:163
SetupScreenWidget::unhighlightAll
void unhighlightAll()
Event for telling rviz to unhighlight all links of the robot.
moveit_setup_assistant::GroupEditWidget::group_name_field_
QLineEdit * group_name_field_
Definition: group_edit_widget.h:82
moveit_setup_assistant::PlanningGroupsWidget::saveJointsScreen
void saveJointsScreen()
Call when screen is done being edited.
Definition: planning_groups_widget.cpp:855
moveit_setup_assistant::DoubleListWidget::setAvailable
void setAvailable(const std::vector< std::string > &items)
Loads the availble data list.
Definition: double_list_widget.cpp:204
header_widget.h
moveit_setup_assistant
Definition: compute_default_collisions.h:46
moveit_setup_assistant::PlanningGroupsWidget::showMainScreen
void showMainScreen()
Switch to current groups view.
Definition: planning_groups_widget.cpp:1419
moveit_setup_assistant::PlanningGroupsWidget::previewSelectedJoints
void previewSelectedJoints(const std::vector< std::string > &joints)
Called from Double List widget to highlight a joint.
Definition: planning_groups_widget.cpp:1461
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreen
bool saveGroupScreen()
Definition: planning_groups_widget.cpp:1083
moveit_setup_assistant::PlanningGroupsWidget::focusGiven
void focusGiven() override
Received when this widget is chosen from the navigation menu.
Definition: planning_groups_widget.cpp:1396
append
ROSCPP_DECL std::string append(const std::string &left, const std::string &right)
moveit_setup_assistant::CycleDetector::m_has_cycle
bool & m_has_cycle
Definition: planning_groups_widget.cpp:125
moveit::core::JointModel::getTypeName
std::string getTypeName() const
moveit_setup_assistant::VIS_TOPIC_NAME
static const std::string VIS_TOPIC_NAME
Definition: planning_groups_widget.cpp:109
moveit_setup_assistant::PlanningGroupsWidget::stacked_widget_
QStackedWidget * stacked_widget_
For changing between table and different add/edit views.
Definition: planning_groups_widget.h:149
moveit_setup_assistant::PlanningGroupsWidget::subgroups_widget_
DoubleListWidget * subgroups_widget_
Definition: planning_groups_widget.h:161
index
unsigned int index
moveit_setup_assistant::PlanningGroupsWidget::saveSubgroupsScreen
void saveSubgroupsScreen()
Definition: planning_groups_widget.cpp:988
moveit_setup_assistant::PlanningGroupsWidget::groups_tree_
QTreeWidget * groups_tree_
Main table for holding groups.
Definition: planning_groups_widget.h:146
moveit_setup_assistant::PlanningGroupsWidget::loadGroupsTree
void loadGroupsTree()
Displays data in the link_pairs_ data structure into a QtTableWidget.
Definition: planning_groups_widget.cpp:281
moveit_setup_assistant::PlanningGroupsWidget::cancelEditing
void cancelEditing()
Call when edit screen is canceled.
Definition: planning_groups_widget.cpp:1361
moveit_setup_assistant::PlanningGroupsWidget::alterTree
void alterTree(const QString &link)
Called when user clicks link part of bottom left label.
Definition: planning_groups_widget.cpp:1408
moveit_setup_assistant::PlanningGroupsWidget::config_data_
moveit_setup_assistant::MoveItConfigDataPtr config_data_
Contains all the configuration data for the setup assistant.
Definition: planning_groups_widget.h:170
srdf::Model::Group::subgroups_
std::vector< std::string > subgroups_
moveit_setup_assistant::MoveItConfigData::GROUP_KINEMATICS
@ GROUP_KINEMATICS
Definition: moveit_config_data.h:232
moveit_setup_assistant::PlanningGroupsWidget::changeScreen
void changeScreen(int index)
Definition: planning_groups_widget.cpp:1430
moveit_setup_assistant::PlanningGroupsWidget::loadLinksScreen
void loadLinksScreen(srdf::Model::Group *this_group)
Definition: planning_groups_widget.cpp:560
moveit_setup_assistant::MoveItConfigData::GROUP_CONTENTS
@ GROUP_CONTENTS
Definition: moveit_config_data.h:231
moveit_setup_assistant::KinematicChainWidget::setSelected
void setSelected(const std::string &base_link, const std::string &tip_link)
Set the link field with previous value.
Definition: kinematic_chain_widget.cpp:253
srdf::Model::Group
moveit_setup_assistant::PlanningGroupsWidget::btn_delete_
QPushButton * btn_delete_
Definition: planning_groups_widget.h:154
moveit_setup_assistant::PlanningGroupsWidget::createContentsWidget
QWidget * createContentsWidget()
Builds the main screen list widget.
Definition: planning_groups_widget.cpp:215
moveit::core::LinkModel::getName
const std::string & getName() const
moveit_setup_assistant::PlanningGroupsWidget::adding_new_group_
bool adding_new_group_
Remember whethere we're editing a group or adding a new one.
Definition: planning_groups_widget.h:179
header
const std::string header
moveit_setup_assistant::KinematicChainWidget
Definition: kinematic_chain_widget.h:51
planning_groups_widget.h
moveit_setup_assistant::GroupEditWidget::default_planner_field_
QComboBox * default_planner_field_
Definition: group_edit_widget.h:90
PlanGroupType::group_
srdf::Model::Group * group_
Definition: planning_groups_widget.h:228
moveit_setup_assistant::PlanningGroupsWidget::loadSubgroupsScreen
void loadSubgroupsScreen(srdf::Model::Group *this_group)
Definition: planning_groups_widget.cpp:623
moveit_setup_assistant::PlanningGroupsWidget::loadJointsScreen
void loadJointsScreen(srdf::Model::Group *this_group)
Definition: planning_groups_widget.cpp:529
base
MatrixDerived base(const Eigen::MatrixBase< MatrixDerived > &m)
moveit_setup_assistant::DoubleListWidget::title_
QLabel * title_
Definition: double_list_widget.h:87
SetupScreenWidget::highlightGroup
void highlightGroup(const std::string &name)
Event for telling rviz to highlight a group of the robot.
moveit_setup_assistant::CycleDetector::CycleDetector
CycleDetector(bool &has_cycle)
Definition: planning_groups_widget.cpp:114
moveit_setup_assistant::PlanningGroupsWidget::saveGroupScreenSubgroups
void saveGroupScreenSubgroups()
Definition: planning_groups_widget.cpp:1344
srdf::Model::Group::joints_
std::vector< std::string > joints_
moveit_setup_assistant::GroupEditWidget::setSelected
void setSelected(const std::string &group_name)
Set the previous data.
Definition: group_edit_widget.cpp:289
moveit::core::JointModel
PlanGroupType::PlanGroupType
PlanGroupType()
Definition: planning_groups_widget.h:219
moveit_setup_assistant::PlanningGroupsWidget::PlanningGroupsWidget
PlanningGroupsWidget(QWidget *parent, const MoveItConfigDataPtr &config_data)
Definition: planning_groups_widget.cpp:131
group
group


moveit_setup_assistant
Author(s): Dave Coleman
autogenerated on Sat May 3 2025 02:28:04