moveit2
The MoveIt Motion Planning Framework for ROS 2.
group_edit_widget.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2012, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Dave Coleman */
36 
37 #include <QComboBox>
38 #include <QFileDialog>
39 #include <QFormLayout>
40 #include <QGroupBox>
41 #include <QHBoxLayout>
42 #include <QLabel>
43 #include <QLineEdit>
44 #include <QMessageBox>
45 #include <QPushButton>
46 #include <QString>
47 #include <QVBoxLayout>
48 
50 
51 namespace moveit_setup
52 {
53 namespace srdf_setup
54 {
55 // ******************************************************************************************
56 //
57 // ******************************************************************************************
58 GroupEditWidget::GroupEditWidget(QWidget* parent, PlanningGroups& setup_step) : QWidget(parent), setup_step_(setup_step)
59 {
60  // Basic widget container
61  QVBoxLayout* layout = new QVBoxLayout();
62 
63  QGroupBox* group1 = new QGroupBox("Kinematics");
64  QGroupBox* group2 = new QGroupBox("OMPL Planning");
65 
66  // Label ------------------------------------------------
67  title_ = new QLabel(this); // specify the title from the parent widget
68  QFont group_title_font(QFont().defaultFamily(), 12, QFont::Bold);
69  title_->setFont(group_title_font);
70  layout->addWidget(title_);
71 
72  // Kinematic form -------------------------------------------
73  QFormLayout* form_layout = new QFormLayout();
74  form_layout->setContentsMargins(0, 12, 0, 12);
75 
76  // Name input
77  group_name_field_ = new QLineEdit(this);
78  group_name_field_->setMaximumWidth(400);
79  form_layout->addRow("Group Name:", group_name_field_);
80 
81  // Kinematic solver
82  kinematics_solver_field_ = new QComboBox(this);
83  kinematics_solver_field_->setEditable(false);
84  kinematics_solver_field_->setMaximumWidth(400);
85  form_layout->addRow("Kinematic Solver:", kinematics_solver_field_);
86 
87  // resolution to use with solver
88  kinematics_resolution_field_ = new QLineEdit(this);
89  kinematics_resolution_field_->setMaximumWidth(400);
90  form_layout->addRow("Kin. Search Resolution:", kinematics_resolution_field_);
91 
92  // resolution to use with solver
93  kinematics_timeout_field_ = new QLineEdit(this);
94  kinematics_timeout_field_->setMaximumWidth(400);
95  form_layout->addRow("Kin. Search Timeout (sec):", kinematics_timeout_field_);
96 
97  // file to load additional parameters from
98  kinematics_parameters_file_field_ = new QLineEdit(this);
99  kinematics_parameters_file_field_->setMaximumWidth(400);
100  QPushButton* kinematics_parameters_file_button = new QPushButton("...", this);
101  kinematics_parameters_file_button->setMaximumWidth(50);
102  connect(kinematics_parameters_file_button, SIGNAL(clicked()), this, SLOT(selectKinematicsFile()));
103  QBoxLayout* kinematics_parameters_file_layout = new QHBoxLayout(this);
104  kinematics_parameters_file_layout->addWidget(kinematics_parameters_file_field_);
105  kinematics_parameters_file_layout->addWidget(kinematics_parameters_file_button);
106  kinematics_parameters_file_layout->setContentsMargins(0, 0, 0, 0);
107  QWidget* container = new QWidget(this);
108  container->setLayout(kinematics_parameters_file_layout);
109  form_layout->addRow("Kin. parameters file:", container);
110 
111  group1->setLayout(form_layout);
112 
113  // OMPL Planner form --------------------------------------------
114 
115  QFormLayout* form_layout2 = new QFormLayout();
116  form_layout2->setContentsMargins(0, 12, 0, 12);
117 
118  // Kinematic default planner
119  default_planner_field_ = new QComboBox(this);
120  default_planner_field_->setEditable(false);
121  default_planner_field_->setMaximumWidth(400);
122  form_layout2->addRow("Group Default Planner:", default_planner_field_);
123 
124  group2->setLayout(form_layout2);
125 
126  layout->addWidget(group1);
127  layout->addWidget(group2);
128 
129  layout->setAlignment(Qt::AlignTop);
130 
131  // New Group Options ---------------------------------------------------------
132  new_buttons_widget_ = new QWidget();
133 
134  QVBoxLayout* new_buttons_layout_container = new QVBoxLayout();
135  QHBoxLayout* label_layout = new QHBoxLayout();
136  QHBoxLayout* recommended_options = new QHBoxLayout();
137  QHBoxLayout* advanced_options = new QHBoxLayout();
138 
139  QLabel* save_and_add = new QLabel("Next, Add Components To Group:", this);
140  QFont save_and_add_font(QFont().defaultFamily(), 12, QFont::Bold);
141  save_and_add->setFont(save_and_add_font);
142  label_layout->addWidget(save_and_add);
143 
144  // Recommended options
145  QLabel* add_subtitle = new QLabel("Recommended: ", this);
146  QFont add_subtitle_font(QFont().defaultFamily(), 10, QFont::Bold);
147  add_subtitle->setFont(add_subtitle_font);
148  recommended_options->addWidget(add_subtitle, 0, Qt::AlignLeft);
149 
150  // Save and add chain
151  QPushButton* btn_save_chain = new QPushButton("Add Kin. Chain", this);
152  btn_save_chain->setMaximumWidth(200);
153  connect(btn_save_chain, SIGNAL(clicked()), this, SIGNAL(saveChain()));
154  recommended_options->addWidget(btn_save_chain);
155 
156  // Save and add joints
157  QPushButton* btn_save_joints = new QPushButton("Add Joints", this);
158  btn_save_joints->setMaximumWidth(200);
159  connect(btn_save_joints, SIGNAL(clicked()), this, SIGNAL(saveJoints()));
160  recommended_options->addWidget(btn_save_joints);
161 
162  // Advanced options
163  QLabel* add_subtitle2 = new QLabel("Advanced Options:", this);
164  add_subtitle2->setFont(add_subtitle_font);
165  advanced_options->addWidget(add_subtitle2, 0, Qt::AlignLeft);
166 
167  // Save and add subgroups
168  QPushButton* btn_save_subgroups = new QPushButton("Add Subgroups", this);
169  btn_save_subgroups->setMaximumWidth(200);
170  connect(btn_save_subgroups, SIGNAL(clicked()), this, SIGNAL(saveSubgroups()));
171  advanced_options->addWidget(btn_save_subgroups);
172 
173  // Save and add links
174  QPushButton* btn_save_links = new QPushButton("Add Links", this);
175  btn_save_links->setMaximumWidth(200);
176  connect(btn_save_links, SIGNAL(clicked()), this, SIGNAL(saveLinks()));
177  advanced_options->addWidget(btn_save_links);
178 
179  // Add layouts
180  new_buttons_layout_container->addLayout(label_layout);
181  new_buttons_layout_container->addLayout(recommended_options);
182  new_buttons_layout_container->addLayout(advanced_options);
183 
184  // Create widget and add to main layout
185  new_buttons_widget_->setLayout(new_buttons_layout_container);
186  layout->addWidget(new_buttons_widget_);
187 
188  // Vertical Spacer
189  layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
190 
191  // Bottom Controls ---------------------------------------------------------
192  QHBoxLayout* controls_layout = new QHBoxLayout();
193 
194  // Delete
195  btn_delete_ = new QPushButton("&Delete Group", this);
196  btn_delete_->setMaximumWidth(200);
197  connect(btn_delete_, SIGNAL(clicked()), this, SIGNAL(deleteGroup()));
198  controls_layout->addWidget(btn_delete_);
199  controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
200 
201  // Horizontal Spacer
202  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
203 
204  // Save
205  btn_save_ = new QPushButton("&Save", this);
206  btn_save_->setMaximumWidth(200);
207  connect(btn_save_, SIGNAL(clicked()), this, SIGNAL(save()));
208  controls_layout->addWidget(btn_save_);
209  controls_layout->setAlignment(btn_save_, Qt::AlignRight);
210 
211  // Cancel
212  QPushButton* btn_cancel = new QPushButton("&Cancel", this);
213  btn_cancel->setMaximumWidth(200);
214  connect(btn_cancel, SIGNAL(clicked()), this, SIGNAL(cancelEditing()));
215  controls_layout->addWidget(btn_cancel);
216  controls_layout->setAlignment(btn_cancel, Qt::AlignRight);
217 
218  // Add layout
219  layout->addLayout(controls_layout);
220 
221  // Finish Layout --------------------------------------------------
222  this->setLayout(layout);
223 }
224 
225 // ******************************************************************************************
226 // Set the link field with previous value
227 // ******************************************************************************************
228 void GroupEditWidget::setSelected(const std::string& group_name, const GroupMetaData& meta_data)
229 {
230  group_name_field_->setText(QString(group_name.c_str()));
231 
232  // Load properties from moveit_config_data.cpp ----------------------------------------------
233 
234  // Load resolution
235  kinematics_resolution_field_->setText(QString::number(meta_data.kinematics_solver_search_resolution_));
236 
237  // Load timeout
238  kinematics_timeout_field_->setText(QString::number(meta_data.kinematics_solver_timeout_));
239 
240  // Set kin solver
241  std::string kin_solver = meta_data.kinematics_solver_;
242 
243  // If this group doesn't have a solver, reset it to 'None'
244  if (kin_solver.empty())
245  {
246  kin_solver = "None";
247  }
248 
249  // Set the kin solver combo box
250  int index = kinematics_solver_field_->findText(kin_solver.c_str());
251  if (index == -1)
252  {
253  QMessageBox::warning(this, "Missing Kinematic Solvers",
254  QString("Unable to find the kinematic solver '")
255  .append(kin_solver.c_str())
256  .append("'. Trying running rosmake for this package. Until fixed, this setting will be "
257  "lost the next time the MoveIt configuration files are generated"));
258  return;
259  }
260  else
261  {
262  kinematics_solver_field_->setCurrentIndex(index);
263  }
264 
266 
267  // Set default planner
268  std::string default_planner = meta_data.default_planner_;
269 
270  // If this group doesn't have a solver, reset it to 'None'
271  if (default_planner.empty())
272  {
273  default_planner = "None";
274  }
275 
276  index = default_planner_field_->findText(default_planner.c_str());
277  if (index == -1)
278  {
279  QMessageBox::warning(this, "Missing Default Planner",
280  QString("Unable to find the default planner '%1'").arg(default_planner.c_str()));
281  }
282  else
283  {
284  default_planner_field_->setCurrentIndex(index);
285  }
286 }
287 
288 // ******************************************************************************************
289 // Populate the combo dropdown box with kinematic planners
290 // ******************************************************************************************
292 {
293  // Only load this combo box once
294  static bool has_loaded = false;
295  if (has_loaded)
296  return;
297  has_loaded = true;
298 
299  // Remove all old items
300  kinematics_solver_field_->clear();
301  default_planner_field_->clear();
302 
303  // Add none option, the default
304  kinematics_solver_field_->addItem("None");
305  default_planner_field_->addItem("None");
306 
307  std::vector<std::string> classes;
308  try
309  {
310  classes = setup_step_.getKinematicPlanners();
311  }
312  catch (const std::runtime_error& ex)
313  {
314  QMessageBox::warning(this, "Missing Kinematic Solvers", QString(ex.what()));
315  RCLCPP_ERROR_STREAM(setup_step_.getLogger(), ex.what());
316  return;
317  }
318 
319  // Loop through all planners and add to combo box
320  for (const std::string& kinematics_plugin_name : classes)
321  {
322  kinematics_solver_field_->addItem(kinematics_plugin_name.c_str());
323  }
324 
325  for (const std::string& planner_name : setup_step_.getOMPLPlanners())
326  {
327  default_planner_field_->addItem(planner_name.c_str());
328  }
329 }
330 
331 void GroupEditWidget::selectKinematicsFile()
332 {
333  QString filename = QFileDialog::getOpenFileName(this, "Select a parameter file", "", "YAML files (*.yaml)");
334 
335  if (filename.isEmpty())
336  {
337  return;
338  }
339 
340  std::string package_name;
341  std::filesystem::path relative_filename;
342  bool package_found = extractPackageNameFromPath(filename.toStdString(), package_name, relative_filename);
343 
344  QString lookup_path = filename;
345  if (package_found)
346  {
347  lookup_path = QString("$(find %1)/%2").arg(package_name.c_str()).arg(relative_filename.c_str());
348  }
349  kinematics_parameters_file_field_->setText(lookup_path);
350 }
351 
352 } // namespace srdf_setup
353 } // namespace moveit_setup
const rclcpp::Logger & getLogger() const
Makes a namespaced logger for this step available to the widget.
Definition: setup_step.hpp:91
void deleteGroup()
Event sent when delete is being requested for group.
void saveLinks()
Button event for new groups, progressing to adding links.
void saveJoints()
Button event for new groups, progressing to adding joints.
void cancelEditing()
Event sent when user presses cancel button.
void loadKinematicPlannersComboBox()
Populate the combo dropdown box with kinematic planners.
void save()
Button event for just saving, when in edit mode.
void saveChain()
Button event for new groups, progressing to adding a chain.
void setSelected(const std::string &group_name, const GroupMetaData &meta_data)
Set the previous data.
void saveSubgroups()
Button event for new groups, progressing to adding subgroups.
GroupEditWidget(QWidget *parent, PlanningGroups &setup_step)
Constructor.
std::vector< std::string > getKinematicPlanners() const
std::vector< std::string > getOMPLPlanners() const
bool extractPackageNameFromPath(const std::filesystem::path &path, std::string &package_name, std::filesystem::path &relative_filepath)
Definition: utilities.cpp:41
std::string append(const std::string &left, const std::string &right)
string package_name
Definition: setup.py:4