moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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
52#include <boost/lexical_cast.hpp>
53
54// Qt
55#include <QApplication>
56#include <QComboBox>
57#include <QHBoxLayout>
58#include <QLabel>
59#include <QLineEdit>
60#include <QMessageBox>
61#include <QPushButton>
62#include <QStackedWidget>
63#include <QTableWidget>
64#include <QTreeWidget>
65#include <QTreeWidgetItem>
66#include <QVBoxLayout>
67
68namespace moveit_setup
69{
70namespace srdf_setup
71{
72// Name of rviz topic in ROS
73static const std::string VIS_TOPIC_NAME = "planning_components_visualization";
74
75// ******************************************************************************************
76// Constructor
77// ******************************************************************************************
79{
80 // Basic widget container
81 QVBoxLayout* layout = new QVBoxLayout();
82
83 // Top Label Area ------------------------------------------------
84 auto header = new HeaderWidget(
85 "Define Planning Groups",
86 "Create and edit 'joint model' groups for your robot based on joint collections, "
87 "link collections, kinematic chains or subgroups. "
88 "A planning group defines the set of (joint, link) pairs considered for planning "
89 "and collision checking. Define individual groups for each subset of the robot you want to plan for.\n"
90 "Note: when adding a link to the group, its parent joint is added too and vice versa.",
91 this);
92 layout->addWidget(header);
93
94 // Left Side ---------------------------------------------
95
96 // Create left side widgets
97 groups_tree_widget_ = createContentsWidget(); // included in this file
98
99 // Joints edit widget
100 joints_widget_ = new DoubleListWidget(this, "Joint Collection", "Joint");
101 connect(joints_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
102 connect(joints_widget_, SIGNAL(doneEditing()), this, SLOT(saveJointsScreen()));
103 connect(joints_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
104 SLOT(previewSelectedJoints(std::vector<std::string>)));
105
106 // Links edit widget
107 links_widget_ = new DoubleListWidget(this, "Link Collection", "Link");
108 connect(links_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
109 connect(links_widget_, SIGNAL(doneEditing()), this, SLOT(saveLinksScreen()));
110 connect(links_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
111 SLOT(previewSelectedLink(std::vector<std::string>)));
112
113 // Chain Widget
114 chain_widget_ = new KinematicChainWidget(this, rviz_panel_);
115 connect(chain_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
116 connect(chain_widget_, SIGNAL(doneEditing()), this, SLOT(saveChainScreen()));
117
118 // Subgroups Widget
119 subgroups_widget_ = new DoubleListWidget(this, "Subgroup", "Subgroup");
120 connect(subgroups_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
121 connect(subgroups_widget_, SIGNAL(doneEditing()), this, SLOT(saveSubgroupsScreen()));
122 connect(subgroups_widget_, SIGNAL(previewSelected(std::vector<std::string>)), this,
123 SLOT(previewSelectedSubgroup(std::vector<std::string>)));
124
125 // Group Edit Widget
126 group_edit_widget_ = new GroupEditWidget(this, setup_step_);
127 connect(group_edit_widget_, SIGNAL(cancelEditing()), this, SLOT(cancelEditing()));
128 connect(group_edit_widget_, SIGNAL(deleteGroup()), this, SLOT(deleteGroup()));
129 connect(group_edit_widget_, SIGNAL(save()), this, SLOT(saveGroupScreenEdit()));
130 connect(group_edit_widget_, SIGNAL(saveJoints()), this, SLOT(saveGroupScreenJoints()));
131 connect(group_edit_widget_, SIGNAL(saveLinks()), this, SLOT(saveGroupScreenLinks()));
132 connect(group_edit_widget_, SIGNAL(saveChain()), this, SLOT(saveGroupScreenChain()));
133 connect(group_edit_widget_, SIGNAL(saveSubgroups()), this, SLOT(saveGroupScreenSubgroups()));
134
135 // Combine into stack: Note, order is same as GroupType!
136 stacked_widget_ = new QStackedWidget(this);
137 stacked_widget_->addWidget(groups_tree_widget_); // screen index 0
138 stacked_widget_->addWidget(joints_widget_); // screen index 1
139 stacked_widget_->addWidget(links_widget_); // screen index 2
140 stacked_widget_->addWidget(chain_widget_); // screen index 3
141 stacked_widget_->addWidget(subgroups_widget_); // screen index 4
142 stacked_widget_->addWidget(group_edit_widget_); // screen index 5
143
144 showMainScreen();
145
146 // Finish GUI -----------------------------------------------------------
147
148 layout->addWidget(stacked_widget_);
149 setLayout(layout);
150
151 // process the gui
152 QApplication::processEvents();
153}
154
155// ******************************************************************************************
156// Create the main tree view widget
157// ******************************************************************************************
158QWidget* PlanningGroupsWidget::createContentsWidget()
159{
160 // Main widget
161 QWidget* content_widget = new QWidget(this);
162
163 // Basic widget container
164 QVBoxLayout* layout = new QVBoxLayout(this);
165
166 // Tree Box ----------------------------------------------------------------------
167
168 groups_tree_ = new QTreeWidget(this);
169 groups_tree_->setHeaderLabel("Current Groups");
170 connect(groups_tree_, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(editSelected()));
171 connect(groups_tree_, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(previewSelected()));
172 layout->addWidget(groups_tree_);
173
174 // Bottom Controls -------------------------------------------------------------
175
176 QHBoxLayout* controls_layout = new QHBoxLayout();
177
178 // Expand/Contract controls
179 QLabel* expand_controls = new QLabel(this);
180 expand_controls->setText("<a href='expand'>Expand All</a> <a href='contract'>Collapse All</a>");
181 connect(expand_controls, SIGNAL(linkActivated(const QString)), this, SLOT(alterTree(const QString)));
182 controls_layout->addWidget(expand_controls);
183
184 // Spacer
185 controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
186
187 // Delete Selected Button
188 btn_delete_ = new QPushButton("&Delete Selected", this);
189 btn_delete_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
190 btn_delete_->setMaximumWidth(300);
191 connect(btn_delete_, SIGNAL(clicked()), this, SLOT(deleteGroup()));
192 controls_layout->addWidget(btn_delete_);
193 controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
194
195 // Edit Selected Button
196 btn_edit_ = new QPushButton("&Edit Selected", this);
197 btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
198 btn_edit_->setMaximumWidth(300);
199 btn_edit_->hide(); // show once we know if there are existing groups
200 connect(btn_edit_, SIGNAL(clicked()), this, SLOT(editSelected()));
201 controls_layout->addWidget(btn_edit_);
202 controls_layout->setAlignment(btn_edit_, Qt::AlignRight);
203
204 // Add Group Button
205 QPushButton* btn_add = new QPushButton("&Add Group", this);
206 btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
207 btn_add->setMaximumWidth(300);
208 connect(btn_add, SIGNAL(clicked()), this, SLOT(addGroup()));
209 controls_layout->addWidget(btn_add);
210 controls_layout->setAlignment(btn_add, Qt::AlignRight);
211
212 // Add Controls to layout
213 layout->addLayout(controls_layout);
214
215 // Set layout
216 content_widget->setLayout(layout);
217
218 return content_widget;
219}
220
221// ******************************************************************************************
222// Displays data in the link_pairs_ data structure into a QtTableWidget
223// ******************************************************************************************
224void PlanningGroupsWidget::loadGroupsTree()
225{
226 // Disable Tree
227 groups_tree_->setUpdatesEnabled(false); // prevent table from updating until we are completely done
228 groups_tree_->setDisabled(true); // make sure we disable it so that the cellChanged event is not called
229 groups_tree_->clear(); // reset the tree
230
231 // Display all groups by looping through them
232 std::vector<srdf::Model::Group>& groups = setup_step_.getContainer();
233 for (srdf::Model::Group& group : groups)
234 {
235 loadGroupsTreeRecursive(group, nullptr);
236 }
237
238 // Re-enable Tree
239 groups_tree_->setUpdatesEnabled(true); // prevent table from updating until we are completely done
240 groups_tree_->setDisabled(false); // make sure we disable it so that the cellChanged event is not called
241
242 // Show Edit button if there are things to edit
243 if (!groups.empty())
244 {
245 btn_edit_->show();
246 btn_delete_->show();
247 }
248 else
249 {
250 btn_edit_->hide();
251 btn_delete_->hide();
252 }
253
254 alterTree("expand");
255}
256
257// ******************************************************************************************
258// Recursively Adds Groups, and subgroups to groups...
259// ******************************************************************************************
260void PlanningGroupsWidget::loadGroupsTreeRecursive(srdf::Model::Group& group_it, QTreeWidgetItem* parent)
261{
262 // Fonts for tree
263 const QFont top_level_font(QFont().defaultFamily(), 11, QFont::Bold);
264 const QFont type_font(QFont().defaultFamily(), 11, QFont::Normal, QFont::StyleItalic);
265
266 QTreeWidgetItem* group;
267
268 // Allow a subgroup to open into a whole new group
269 if (parent == nullptr)
270 {
271 group = new QTreeWidgetItem(groups_tree_);
272 group->setText(0, group_it.name_.c_str());
273 group->setFont(0, top_level_font);
274 group->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, GROUP)));
275 groups_tree_->addTopLevelItem(group);
276 }
277 else
278 {
279 group = new QTreeWidgetItem(parent);
280 group->setText(0, group_it.name_.c_str());
281 group->setFont(0, top_level_font);
282 group->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, GROUP)));
283 parent->addChild(group);
284 }
285
286 // Joints --------------------------------------------------------------
287 QTreeWidgetItem* joints = new QTreeWidgetItem(group);
288 joints->setText(0, "Joints");
289 joints->setFont(0, type_font);
290 joints->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, JOINT)));
291 group->addChild(joints);
292
293 // Loop through all aval. joints
294 for (std::vector<std::string>::const_iterator joint_it = group_it.joints_.begin(); joint_it != group_it.joints_.end();
295 ++joint_it)
296 {
297 QTreeWidgetItem* j = new QTreeWidgetItem(joints);
298 j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, JOINT)));
299 std::string joint_name;
300
301 // Get the type of joint this is
302 std::string joint_type = setup_step_.getJointType(*joint_it);
303 if (!joint_type.empty())
304 {
305 joint_name = *joint_it + " - " + joint_type;
306 }
307 else
308 {
309 joint_name = *joint_it;
310 }
311
312 // Add to tree
313 j->setText(0, joint_name.c_str());
314 joints->addChild(j);
315 }
316
317 // Links -------------------------------------------------------------
318 QTreeWidgetItem* links = new QTreeWidgetItem(group);
319 links->setText(0, "Links");
320 links->setFont(0, type_font);
321 links->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, LINK)));
322 group->addChild(links);
323
324 // Loop through all aval. links
325 for (std::vector<std::string>::const_iterator joint_it = group_it.links_.begin(); joint_it != group_it.links_.end();
326 ++joint_it)
327 {
328 QTreeWidgetItem* j = new QTreeWidgetItem(links);
329 j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, LINK)));
330 j->setText(0, joint_it->c_str());
331 links->addChild(j);
332 }
333
334 // Chains -------------------------------------------------------------
335 QTreeWidgetItem* chains = new QTreeWidgetItem(group);
336 chains->setText(0, "Chain");
337 chains->setFont(0, type_font);
338 chains->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, CHAIN)));
339 group->addChild(chains);
340
341 // Warn if there is more than 1 chain per group
342 static bool warn_once = true;
343 if (group_it.chains_.size() > 1 && warn_once)
344 {
345 warn_once = false;
346 QMessageBox::warning(this, "Group with Multiple Kinematic Chains",
347 "Warning: this MoveIt Setup Assistant is only designed to handle one kinematic chain per "
348 "group. The loaded SRDF has more than one kinematic chain for a group. A possible loss of "
349 "data may occur.");
350 }
351
352 // Loop through all aval. chains
353 for (std::vector<std::pair<std::string, std::string> >::const_iterator chain_it = group_it.chains_.begin();
354 chain_it != group_it.chains_.end(); ++chain_it)
355 {
356 QTreeWidgetItem* j = new QTreeWidgetItem(chains);
357 j->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, CHAIN)));
358 j->setText(0, QString(chain_it->first.c_str()).append(" -> ").append(chain_it->second.c_str()));
359 chains->addChild(j);
360 }
361
362 // Subgroups -------------------------------------------------------------
363 QTreeWidgetItem* subgroups = new QTreeWidgetItem(group);
364 subgroups->setText(0, "Subgroups");
365 subgroups->setFont(0, type_font);
366 subgroups->setData(0, Qt::UserRole, QVariant::fromValue(PlanGroupType(&group_it, SUBGROUP)));
367 group->addChild(subgroups);
368
369 // Loop through all aval. subgroups
370 for (std::vector<std::string>::iterator subgroup_it = group_it.subgroups_.begin();
371 subgroup_it != group_it.subgroups_.end(); ++subgroup_it)
372 {
373 // Find group with this subgroups' name
374 srdf::Model::Group* searched_group;
375
376 try
377 {
378 searched_group = setup_step_.find(*subgroup_it);
379 }
380 catch (const std::runtime_error& e)
381 {
382 QMessageBox::critical(this, "Error Loading SRDF",
383 QString("Subgroup '")
384 .append(subgroup_it->c_str())
385 .append("' of group '")
386 .append(group_it.name_.c_str())
387 .append("' not found. Your SRDF is invalid"));
388 return; // TODO: something better for error handling?
389 }
390
391 // Recurse this function for each new group
392 loadGroupsTreeRecursive(*searched_group, subgroups);
393 }
394}
395
396// ******************************************************************************************
397// Highlight the group of whatever element is selected in the tree view
398// ******************************************************************************************
399void PlanningGroupsWidget::previewSelected()
400{
401 QTreeWidgetItem* item = groups_tree_->currentItem();
402
403 // Check that something was actually selected
404 if (item == nullptr)
405 return;
406
407 // Get the user custom properties of the currently selected row
408 PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
409
410 // Unhighlight all links
412
413 // Highlight the group
414 rviz_panel_->highlightGroup(plan_group.group_->name_);
415}
416
417// ******************************************************************************************
418// Edit whatever element is selected in the tree view
419// ******************************************************************************************
420void PlanningGroupsWidget::editSelected()
421{
422 QTreeWidgetItem* item = groups_tree_->currentItem();
423
424 // Check that something was actually selected
425 if (item == nullptr)
426 return;
427
428 adding_new_group_ = false;
429
430 // Get the user custom properties of the currently selected row
431 PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
432
433 switch (plan_group.type_)
434 {
435 case JOINT:
436 loadJointsScreen(plan_group.group_);
437 break;
438 case LINK:
439 loadLinksScreen(plan_group.group_);
440 break;
441 case CHAIN:
442 loadChainScreen(plan_group.group_);
443 break;
444 case SUBGROUP:
445 loadSubgroupsScreen(plan_group.group_);
446 break;
447 case GROUP:
448 loadGroupScreen(plan_group.group_);
449 break;
450 default:
451 QMessageBox::critical(this, "Error Loading", "An internal error has occurred while loading.");
452 return;
453 }
454 return_screen_ = 0; // return to main screen directly
455 changeScreen(plan_group.type_);
456}
457
458// ******************************************************************************************
459// Load the popup screen with correct data for joints
460// ******************************************************************************************
461void PlanningGroupsWidget::loadJointsScreen(srdf::Model::Group* this_group)
462{
463 // Get the names of the all joints
464 const std::vector<std::string>& joints = setup_step_.getJointNames();
465
466 if (joints.empty())
467 {
468 QMessageBox::critical(this, "Error Loading", "No joints found for robot model");
469 return;
470 }
471
472 // Set the available joints (left box)
473 joints_widget_->setAvailable(joints);
474
475 // Set the selected joints (right box)
476 joints_widget_->setSelected(this_group->joints_);
477
478 // Set the title
479 joints_widget_->title_->setText(
480 QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Joint Collection"));
481
482 // Remember what is currently being edited so we can later save changes
483 current_edit_group_ = this_group->name_;
484}
485
486// ******************************************************************************************
487// Load the popup screen with correct data for links
488// ******************************************************************************************
489void PlanningGroupsWidget::loadLinksScreen(srdf::Model::Group* this_group)
490{
491 // Get the names of the all links
492 const std::vector<std::string>& links = setup_step_.getLinkNames();
493
494 if (links.empty())
495 {
496 QMessageBox::critical(this, "Error Loading", "No links found for robot model");
497 return;
498 }
499
500 // Set the available links (left box)
501 links_widget_->setAvailable(links);
502
503 // Set the selected links (right box)
504 links_widget_->setSelected(this_group->links_);
505
506 // Set the title
507 links_widget_->title_->setText(
508 QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Link Collection"));
509
510 // Remember what is currently being edited so we can later save changes
511 current_edit_group_ = this_group->name_;
512}
513
514// ******************************************************************************************
515// Load the popup screen with correct data for chains
516// ******************************************************************************************
517void PlanningGroupsWidget::loadChainScreen(srdf::Model::Group* this_group)
518{
519 chain_widget_->setAvailable(setup_step_.getLinkNameTree());
520
521 // Make sure there isn't more than 1 chain pair
522 if (this_group->chains_.size() > 1)
523 {
524 QMessageBox::warning(this, "Multiple Kinematic Chains",
525 "Warning: This setup assistant is only designed to handle "
526 "one kinematic chain per group. The loaded SRDF has more "
527 "than one kinematic chain for a group. A possible loss of "
528 "data may occur.");
529 }
530
531 // Set the selected tip and base of chain if one exists
532 if (!this_group->chains_.empty())
533 {
534 chain_widget_->setSelected(this_group->chains_[0].first, this_group->chains_[0].second);
535 }
536
537 // Set the title
538 chain_widget_->title_->setText(
539 QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Kinematic Chain"));
540
541 // Remember what is currently being edited so we can later save changes
542 current_edit_group_ = this_group->name_;
543}
544
545// ******************************************************************************************
546// Load the popup screen with correct data for subgroups
547// ******************************************************************************************
548void PlanningGroupsWidget::loadSubgroupsScreen(srdf::Model::Group* this_group)
549{
550 // Load all groups into the subgroup screen except the current group
551 std::vector<std::string> subgroups;
552
553 // Display all groups by looping through them
554 for (const std::string& group_name : setup_step_.getGroupNames())
555 {
556 if (group_name != this_group->name_) // do not include current group
557 {
558 // add to available subgroups list
559 subgroups.push_back(group_name);
560 }
561 }
562
563 // Set the available subgroups (left box)
564 subgroups_widget_->setAvailable(subgroups);
565
566 // Set the selected subgroups (right box)
567 subgroups_widget_->setSelected(this_group->subgroups_);
568
569 // Set the title
570 subgroups_widget_->title_->setText(
571 QString("Edit '").append(QString::fromUtf8(this_group->name_.c_str())).append("' Subgroups"));
572
573 // Remember what is currently being edited so we can later save changes
574 current_edit_group_ = this_group->name_;
575}
576
577// ******************************************************************************************
578// Load the popup screen with correct data for groups
579// ******************************************************************************************
580void PlanningGroupsWidget::loadGroupScreen(srdf::Model::Group* this_group)
581{
582 // Load the avail kin solvers. This function only runs once
583 group_edit_widget_->loadKinematicPlannersComboBox();
584
585 if (this_group == nullptr) // this is a new screen
586 {
587 current_edit_group_.clear(); // provide a blank group name
588 group_edit_widget_->title_->setText("Create New Planning Group");
589 group_edit_widget_->btn_delete_->hide();
590 group_edit_widget_->new_buttons_widget_->show(); // helps user choose next step
591 group_edit_widget_->btn_save_->hide(); // this is only for edit mode
592 }
593 else // load the group name into the widget
594 {
595 current_edit_group_ = this_group->name_;
596 group_edit_widget_->title_->setText(
597 QString("Edit Planning Group '").append(current_edit_group_.c_str()).append("'"));
598 group_edit_widget_->btn_delete_->show();
599 group_edit_widget_->new_buttons_widget_->hide(); // not necessary for existing groups
600 group_edit_widget_->btn_save_->show(); // this is only for edit mode
601 }
602
603 // Set the data in the edit box
604 group_edit_widget_->setSelected(current_edit_group_, setup_step_.getMetaData(current_edit_group_));
605}
606
607// ******************************************************************************************
608// Delete a group
609// ******************************************************************************************
610void PlanningGroupsWidget::deleteGroup()
611{
612 std::string group_to_delete = current_edit_group_;
613 if (group_to_delete.empty())
614 {
615 QTreeWidgetItem* item = groups_tree_->currentItem();
616 // Check that something was actually selected
617 if (item == nullptr)
618 return;
619 // Get the user custom properties of the currently selected row
620 PlanGroupType plan_group = item->data(0, Qt::UserRole).value<PlanGroupType>();
621 if (plan_group.group_)
622 group_to_delete = plan_group.group_->name_;
623 }
624 else
625 current_edit_group_.clear();
626 if (group_to_delete.empty())
627 return;
628
629 // Confirm user wants to delete group
630 if (QMessageBox::question(this, "Confirm Group Deletion",
631 QString("Are you sure you want to delete the planning group '")
632 .append(group_to_delete.c_str())
633 .append("'? This will also delete all references in subgroups, robot poses and end "
634 "effectors."),
635 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
636 {
637 return;
638 }
639
640 // Ensure we want to delete the states
641 std::vector<std::string> pose_names = setup_step_.getPosesByGroup(group_to_delete);
642 if (!pose_names.empty() &&
643 QMessageBox::question(
644 this, "Confirm Group State Deletion",
645 QString("The group that is about to be deleted has robot poses (robot states) that depend on this "
646 "group. Are you sure you want to delete this group as well as all dependent robot poses?"),
647 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
648 {
649 return;
650 }
651 // Ensure we want to delete the end_effectors
652 std::vector<std::string> eef_names = setup_step_.getEndEffectorsByGroup(group_to_delete);
653 if (!eef_names.empty() &&
654 QMessageBox::question(
655 this, "Confirm End Effector Deletion",
656 QString("The group that is about to be deleted has end effectors (grippers) that depend on this "
657 "group. Are you sure you want to delete this group as well as all dependent end effectors?"),
658 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
659 {
660 return;
661 }
662
663 setup_step_.deleteGroup(group_to_delete);
664
665 // Switch to main screen
666 showMainScreen();
667
668 // Reload main screen table
669 loadGroupsTree();
670}
671
672// ******************************************************************************************
673// Create a new, empty group
674// ******************************************************************************************
675void PlanningGroupsWidget::addGroup()
676{
677 adding_new_group_ = true;
678
679 // Load the data
680 loadGroupScreen(nullptr); // nullptr indicates this is a new group, not an existing one
681
682 // Switch to screen
684}
685
686// ******************************************************************************************
687// Call when joints edit screen is done and needs to be saved
688// ******************************************************************************************
689void PlanningGroupsWidget::saveJointsScreen()
690{
691 setup_step_.setJoints(current_edit_group_, joints_widget_->getSelectedValues());
692
693 // Switch to main screen
694 showMainScreen();
695
696 // Reload main screen table
697 loadGroupsTree();
698}
699
700// ******************************************************************************************
701// Call when links edit screen is done and needs to be saved
702// ******************************************************************************************
703void PlanningGroupsWidget::saveLinksScreen()
704{
705 setup_step_.setLinks(current_edit_group_, links_widget_->getSelectedValues());
706
707 // Switch to main screen
708 showMainScreen();
709
710 // Reload main screen table
711 loadGroupsTree();
712}
713
714// ******************************************************************************************
715// Call when chains edit screen is done and needs to be saved
716// ******************************************************************************************
717void PlanningGroupsWidget::saveChainScreen()
718{
719 // Get a reference to the supplied strings
720 const std::string& tip = chain_widget_->tip_link_field_->text().trimmed().toStdString();
721 const std::string& base = chain_widget_->base_link_field_->text().trimmed().toStdString();
722
723 try
724 {
725 setup_step_.setChain(current_edit_group_, base, tip);
726 }
727 catch (const std::runtime_error& e)
728 {
729 QMessageBox::warning(this, "Error Saving", e.what());
730 return;
731 }
732
733 // Switch to main screen
734 showMainScreen();
735
736 // Reload main screen table
737 loadGroupsTree();
738}
739
740// ******************************************************************************************
741// Call when subgroups edit screen is done and needs to be saved
742// ******************************************************************************************
743void PlanningGroupsWidget::saveSubgroupsScreen()
744{
745 try
746 {
747 setup_step_.setSubgroups(current_edit_group_, subgroups_widget_->getSelectedValues());
748 }
749 catch (const std::runtime_error& e)
750 {
751 QMessageBox::warning(this, "Error Saving", e.what());
752 return;
753 }
754
755 // Switch to main screen
756 showMainScreen();
757
758 // Reload main screen table
759 loadGroupsTree();
760}
761
762// ******************************************************************************************
763// Call when groups edit screen is done and needs to be saved
764// ******************************************************************************************
765bool PlanningGroupsWidget::saveGroupScreen()
766{
767 const std::string& group_name = group_edit_widget_->group_name_field_->text().trimmed().toStdString();
768
769 GroupMetaData meta_data;
770 meta_data.kinematics_solver_ = group_edit_widget_->kinematics_solver_field_->currentText().toStdString();
771 meta_data.kinematics_parameters_file_ = group_edit_widget_->kinematics_parameters_file_field_->text().toStdString();
772 meta_data.default_planner_ = group_edit_widget_->default_planner_field_->currentText().toStdString();
773 if (meta_data.default_planner_ == "None")
774 {
775 meta_data.default_planner_ = "";
776 }
777
778 // Check that a valid group name has been given
779 if (group_name.empty())
780 {
781 QMessageBox::warning(this, "Error Saving", "A name must be given for the group!");
782 return false;
783 }
784
785 // Check that the resolution is an double number
786 const std::string& kinematics_resolution = group_edit_widget_->kinematics_resolution_field_->text().toStdString();
787 try
788 {
789 meta_data.kinematics_solver_search_resolution_ = boost::lexical_cast<double>(kinematics_resolution);
790 }
791 catch (boost::bad_lexical_cast&)
792 {
793 QMessageBox::warning(this, "Error Saving", "Unable to convert kinematics resolution to a double number.");
794 return false;
795 }
796
797 // Check that the timeout is a double number
798 const std::string& kinematics_timeout = group_edit_widget_->kinematics_timeout_field_->text().toStdString();
799 try
800 {
801 meta_data.kinematics_solver_timeout_ = boost::lexical_cast<double>(kinematics_timeout);
802 }
803 catch (boost::bad_lexical_cast&)
804 {
805 QMessageBox::warning(this, "Error Saving", "Unable to convert kinematics solver timeout to a double number.");
806 return false;
807 }
808
809 // Check that all numbers are >0
810 if (meta_data.kinematics_solver_search_resolution_ <= 0)
811 {
812 QMessageBox::warning(this, "Error Saving", "Kinematics solver search resolution must be greater than 0.");
813 return false;
814 }
815 if (meta_data.kinematics_solver_timeout_ <= 0)
816 {
817 QMessageBox::warning(this, "Error Saving", "Kinematics solver search timeout must be greater than 0.");
818 return false;
819 }
820
821 try
822 {
823 adding_new_group_ = current_edit_group_.empty();
824 setup_step_.get(group_name, current_edit_group_);
825 }
826 catch (const std::runtime_error& e)
827 {
828 QMessageBox::warning(this, "Error Saving", e.what());
829 return false;
830 }
831
832 setup_step_.setMetaData(group_name, meta_data);
833
834 // Reload main screen table
835 loadGroupsTree();
836
837 // Update the current edit group so that we can proceed to the next screen, if user desires
838 current_edit_group_ = group_name;
839
840 return true;
841}
842
843// ******************************************************************************************
844// Call when a new group is created and ready to progress to next screen
845// ******************************************************************************************
846void PlanningGroupsWidget::saveGroupScreenEdit()
847{
848 // Save the group
849 if (!saveGroupScreen())
850 return;
851
852 // Switch to main screen
853 showMainScreen();
854}
855
856// ******************************************************************************************
857// Call when a new group is created and ready to progress to next screen
858// ******************************************************************************************
859void PlanningGroupsWidget::saveGroupScreenJoints()
860{
861 // Save the group
862 if (!saveGroupScreen())
863 return;
864
865 // Find the group we are editing based on the group name string
866 loadJointsScreen(setup_step_.find(current_edit_group_));
867 return_screen_ = GROUP;
868
869 // Switch to screen
871}
872
873// ******************************************************************************************
874// Call when a new group is created and ready to progress to next screen
875// ******************************************************************************************
876void PlanningGroupsWidget::saveGroupScreenLinks()
877{
878 // Save the group
879 if (!saveGroupScreen())
880 return;
881
882 // Find the group we are editing based on the group name string
883 loadLinksScreen(setup_step_.find(current_edit_group_));
884 return_screen_ = GROUP;
885
886 // Switch to screen
888}
889
890// ******************************************************************************************
891// Call when a new group is created and ready to progress to next screen
892// ******************************************************************************************
893void PlanningGroupsWidget::saveGroupScreenChain()
894{
895 // Save the group
896 if (!saveGroupScreen())
897 return;
898
899 // Find the group we are editing based on the group name string
900 loadChainScreen(setup_step_.find(current_edit_group_));
901 return_screen_ = GROUP;
902
903 // Switch to screen
905}
906
907// ******************************************************************************************
908// Call when a new group is created and ready to progress to next screen
909// ******************************************************************************************
910void PlanningGroupsWidget::saveGroupScreenSubgroups()
911{
912 // Save the group
913 if (!saveGroupScreen())
914 return;
915
916 // Find the group we are editing based on the group name string
917 loadSubgroupsScreen(setup_step_.find(current_edit_group_));
918 return_screen_ = GROUP;
919
920 // Switch to screen
922}
923
924// ******************************************************************************************
925// Call when edit screen is canceled
926// ******************************************************************************************
927void PlanningGroupsWidget::cancelEditing()
928{
929 if (return_screen_)
930 {
931 changeScreen(return_screen_);
932 return_screen_ = 0;
933 return;
934 }
935 if (!current_edit_group_.empty() && adding_new_group_)
936 {
937 srdf::Model::Group* editing = setup_step_.find(current_edit_group_);
938 if (editing && editing->joints_.empty() && editing->links_.empty() && editing->chains_.empty() &&
939 editing->subgroups_.empty())
940 {
941 setup_step_.deleteGroup(editing->name_);
942 current_edit_group_.clear();
943 // Load the data to the tree
944 loadGroupsTree();
945 }
946 }
947
948 // Switch to main screen
949 showMainScreen();
950}
951
952// ******************************************************************************************
953// Called when setup assistant navigation switches to this screen
954// ******************************************************************************************
956{
957 // Show the current groups screen
958 showMainScreen();
959
960 // Load the data to the tree
961 loadGroupsTree();
962}
963
964// ******************************************************************************************
965// Expand/Collapse Tree
966// ******************************************************************************************
967void PlanningGroupsWidget::alterTree(const QString& link)
968{
969 if (link.contains("expand"))
970 {
971 groups_tree_->expandAll();
972 }
973 else
974 {
975 groups_tree_->collapseAll();
976 }
977}
978
979// ******************************************************************************************
980// Switch to current groups view
981// ******************************************************************************************
982void PlanningGroupsWidget::showMainScreen()
983{
984 stacked_widget_->setCurrentIndex(0);
985
986 // Announce that this widget is not in modal mode anymore
987 Q_EMIT setModalMode(false);
988}
989
990// ******************************************************************************************
991// Switch which screen is being shown
992// ******************************************************************************************
994{
995 stacked_widget_->setCurrentIndex(index);
996
997 // Announce this widget's mode
998 Q_EMIT setModalMode(index != 0);
999}
1000
1001// ******************************************************************************************
1002// Called from Double List widget to highlight a link
1003// ******************************************************************************************
1004void PlanningGroupsWidget::previewSelectedLink(const std::vector<std::string>& links)
1005{
1006 // Unhighlight all links
1008
1009 for (const std::string& link : links)
1010 {
1011 if (link.empty())
1012 {
1013 continue;
1014 }
1015
1016 // Highlight link
1017 rviz_panel_->highlightLink(link, QColor(255, 0, 0));
1018 }
1019}
1020
1021// ******************************************************************************************
1022// Called from Double List widget to highlight joints
1023// ******************************************************************************************
1024void PlanningGroupsWidget::previewSelectedJoints(const std::vector<std::string>& joints)
1025{
1026 // Unhighlight all links
1028
1029 for (const std::string& joint : joints)
1030 {
1031 const std::string link = setup_step_.getChildOfJoint(joint);
1032 if (link.empty())
1033 {
1034 continue;
1035 }
1036
1037 // Highlight link
1038 rviz_panel_->highlightLink(link, QColor(255, 0, 0));
1039 }
1040}
1041
1042// ******************************************************************************************
1043// Called from Double List widget to highlight a subgroup
1044// ******************************************************************************************
1045void PlanningGroupsWidget::previewSelectedSubgroup(const std::vector<std::string>& groups)
1046{
1047 // Unhighlight all links
1049
1050 for (const std::string& group : groups)
1051 {
1052 // Highlight group
1054 }
1055}
1056
1057// ******************************************************************************************
1058// ******************************************************************************************
1059// CLASS
1060// ******************************************************************************************
1061// ******************************************************************************************
1062
1063PlanGroupType::PlanGroupType(srdf::Model::Group* group, const GroupType type) : group_(group), type_(type)
1064{
1065}
1066
1067} // namespace srdf_setup
1068} // namespace moveit_setup
1069
1070#include <pluginlib/class_list_macros.hpp> // NOLINT
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
std::vector< std::string > getSelectedValues() const
Return all the values that are in the "selected" subset.
void setAvailable(const std::vector< std::string > &items)
Loads the available data list.
void setSelected(const std::vector< std::string > &items)
Set the right box.
void highlightGroup(const std::string &group_name)
void highlightLink(const std::string &link_name, const QColor &color)
The GUI code for one SetupStep.
void setModalMode(bool isModal)
Event for when the current screen is in modal view. Disables the left navigation.
void loadKinematicPlannersComboBox()
Populate the combo dropdown box with kinematic planners.
void setSelected(const std::string &group_name, const GroupMetaData &meta_data)
Set the previous data.
void setAvailable(const LinkNameTree &link_name_tree)
Loads the available data list.
void setSelected(const std::string &base_link, const std::string &tip_link)
Set the link field with previous value.
void focusGiven() override
Received when this widget is chosen from the navigation menu.
void setChain(const std::string &group_name, const std::string &base, const std::string &tip)
Set the specified group's kinematic chain.
const std::vector< std::string > & getJointNames() const
std::vector< srdf::Model::Group > & getContainer() override
Returns the reference to the vector in the SRDF.
const GroupMetaData & getMetaData(const std::string &group_name) const
void setLinks(const std::string &group_name, const std::vector< std::string > &link_names)
Set the specified group's link names.
void setSubgroups(const std::string &selected_group_name, const std::vector< std::string > &subgroups)
Set the specified group's subgroups.
std::string getChildOfJoint(const std::string &joint_name) const
void deleteGroup(const std::string &group_name)
std::vector< std::string > getPosesByGroup(const std::string &group_name) const
std::string getJointType(const std::string &joint_name) const
const std::vector< std::string > & getLinkNames() const
std::vector< std::string > getEndEffectorsByGroup(const std::string &group_name) const
void setJoints(const std::string &group_name, const std::vector< std::string > &joint_names)
Set the specified group's joint names.
void setMetaData(const std::string &group_name, const GroupMetaData &meta_data)
T * get(const std::string &name, const std::string &old_name="")
Get a pointer to an item with the given name, creating if necessary. If old_name is provided (and is ...
T * find(const std::string &name)
Return a pointer to an item with the given name if it exists, otherwise null.
Definition srdf_step.hpp:98
std::string append(const std::string &left, const std::string &right)