moveit2
The MoveIt Motion Planning Framework for ROS 2.
virtual_joints_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 
39 
40 // Qt
41 #include <QApplication>
42 #include <QFormLayout>
43 #include <QHBoxLayout>
44 #include <QMessageBox>
45 #include <QString>
46 #include <QVBoxLayout>
47 
48 namespace moveit_setup
49 {
50 namespace srdf_setup
51 {
53 {
54  // Basic widget container
55  QVBoxLayout* layout = new QVBoxLayout();
56 
57  // Top Header Area ------------------------------------------------
58 
59  auto header = new HeaderWidget("Define Virtual Joints",
60  "Create a virtual joint between the base robot link and an external frame of "
61  "reference. This allows to place the robot in the world or on a mobile platform.",
62  this);
63  layout->addWidget(header);
64 
65  // Create contents screens ---------------------------------------
66 
67  vjoint_list_widget_ = createContentsWidget();
68  vjoint_edit_widget_ = createEditWidget();
69 
70  // Create Widget wrapper for layout
71  stacked_widget_ = new QStackedWidget(this);
72  stacked_widget_->addWidget(vjoint_list_widget_); // screen index 0
73  stacked_widget_->addWidget(vjoint_edit_widget_); // screen index 1
74  layout->addWidget(stacked_widget_);
75 
76  // Finish Layout --------------------------------------------------
77  this->setLayout(layout);
78 }
79 
80 // ******************************************************************************************
81 // Create the main content widget
82 // ******************************************************************************************
83 QWidget* VirtualJointsWidget::createContentsWidget()
84 {
85  // Main widget
86  QWidget* content_widget = new QWidget(this);
87 
88  // Basic widget container
89  QVBoxLayout* layout = new QVBoxLayout(this);
90 
91  // Table ------------ ------------------------------------------------
92 
93  data_table_ = new QTableWidget(this);
94  data_table_->setColumnCount(4);
95  data_table_->setSortingEnabled(true);
96  data_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
97  connect(data_table_, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(editDoubleClicked(int, int)));
98  connect(data_table_, SIGNAL(cellClicked(int, int)), this, SLOT(previewClicked(int, int)));
99  layout->addWidget(data_table_);
100 
101  // Set header labels
102  QStringList header_list;
103  header_list.append("Virtual Joint Name");
104  header_list.append("Child Link");
105  header_list.append("Parent Frame");
106  header_list.append("Type");
107  data_table_->setHorizontalHeaderLabels(header_list);
108 
109  // Bottom Buttons --------------------------------------------------
110 
111  QHBoxLayout* controls_layout = new QHBoxLayout();
112 
113  // Spacer
114  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
115 
116  // Edit Selected Button
117  btn_edit_ = new QPushButton("&Edit Selected", this);
118  btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
119  btn_edit_->setMaximumWidth(300);
120  btn_edit_->hide(); // show once we know if there are existing poses
121  connect(btn_edit_, SIGNAL(clicked()), this, SLOT(editSelected()));
122  controls_layout->addWidget(btn_edit_);
123  controls_layout->setAlignment(btn_edit_, Qt::AlignRight);
124 
125  // Delete Selected Button
126  btn_delete_ = new QPushButton("&Delete Selected", this);
127  connect(btn_delete_, SIGNAL(clicked()), this, SLOT(deleteSelected()));
128  controls_layout->addWidget(btn_delete_);
129  controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
130 
131  // Add VJoint Button
132  QPushButton* btn_add = new QPushButton("&Add Virtual Joint", this);
133  btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
134  btn_add->setMaximumWidth(300);
135  connect(btn_add, SIGNAL(clicked()), this, SLOT(showNewScreen()));
136  controls_layout->addWidget(btn_add);
137  controls_layout->setAlignment(btn_add, Qt::AlignRight);
138 
139  // Add layout
140  layout->addLayout(controls_layout);
141 
142  // Set layout -----------------------------------------------------
143  content_widget->setLayout(layout);
144 
145  return content_widget;
146 }
147 
148 // ******************************************************************************************
149 // Create the edit widget
150 // ******************************************************************************************
151 QWidget* VirtualJointsWidget::createEditWidget()
152 {
153  // Main widget
154  QWidget* edit_widget = new QWidget(this);
155  // Layout
156  QVBoxLayout* layout = new QVBoxLayout();
157 
158  // Simple form -------------------------------------------
159  QFormLayout* form_layout = new QFormLayout();
160  // form_layout->setContentsMargins( 0, 15, 0, 15 );
161  form_layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
162 
163  // Name input
164  vjoint_name_field_ = new QLineEdit(this);
165  form_layout->addRow("Virtual Joint Name:", vjoint_name_field_);
166 
167  // Child Link input
168  child_link_field_ = new QComboBox(this);
169  child_link_field_->setEditable(false);
170  form_layout->addRow("Child Link:", child_link_field_);
171 
172  // Parent frame name input
173  parent_name_field_ = new QLineEdit(this);
174  form_layout->addRow("Parent Frame Name:", parent_name_field_);
175 
176  // Type input
177  joint_type_field_ = new QComboBox(this);
178  joint_type_field_->setEditable(false);
179  loadJointTypesComboBox(); // only do this once
180  // connect( joint_type_field_, SIGNAL( currentIndexChanged( const QString & ) ),
181  // this, SLOT( loadJoinSliders( const QString & ) ) );
182  form_layout->addRow("Joint Type:", joint_type_field_);
183 
184  layout->addLayout(form_layout);
185 
186  // Bottom Buttons --------------------------------------------------
187 
188  QHBoxLayout* controls_layout = new QHBoxLayout();
189  controls_layout->setContentsMargins(0, 25, 0, 15);
190 
191  // Spacer
192  controls_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
193 
194  // Save
195  QPushButton* btn_save = new QPushButton("&Save", this);
196  btn_save->setMaximumWidth(200);
197  connect(btn_save, SIGNAL(clicked()), this, SLOT(doneEditing()));
198  controls_layout->addWidget(btn_save);
199  controls_layout->setAlignment(btn_save, Qt::AlignRight);
200 
201  // Cancel
202  QPushButton* btn_cancel = new QPushButton("&Cancel", this);
203  btn_cancel->setMaximumWidth(200);
204  connect(btn_cancel, SIGNAL(clicked()), this, SLOT(cancelEditing()));
205  controls_layout->addWidget(btn_cancel);
206  controls_layout->setAlignment(btn_cancel, Qt::AlignRight);
207 
208  // Add layout
209  layout->addLayout(controls_layout);
210 
211  // Set layout -----------------------------------------------------
212  edit_widget->setLayout(layout);
213 
214  return edit_widget;
215 }
216 
217 // ******************************************************************************************
218 // Show edit screen for creating a new vjoint
219 // ******************************************************************************************
220 void VirtualJointsWidget::showNewScreen()
221 {
222  // Remember that this is a new vjoint
223  current_edit_vjoint_.clear();
224 
225  // Clear previous data
226  vjoint_name_field_->setText("");
227  parent_name_field_->setText("");
228  child_link_field_->clearEditText();
229  joint_type_field_->clearEditText(); // actually this just chooses first option
230 
231  // Switch to screen
232  stacked_widget_->setCurrentIndex(1);
233 
234  // Announce that this widget is in modal mode
235  Q_EMIT setModalMode(true);
236 }
237 
238 // ******************************************************************************************
239 // Edit whatever element is selected
240 // ******************************************************************************************
241 void VirtualJointsWidget::editDoubleClicked(int /*row*/, int /*column*/)
242 {
243  editSelected();
244 }
245 
246 // ******************************************************************************************
247 // Preview whatever element is selected
248 // ******************************************************************************************
249 void VirtualJointsWidget::previewClicked(int /*row*/, int /*column*/)
250 {
251 }
252 
253 // ******************************************************************************************
254 // Edit whatever element is selected
255 // ******************************************************************************************
256 void VirtualJointsWidget::editSelected()
257 {
258  // Get list of all selected items
259  QList<QTableWidgetItem*> selected = data_table_->selectedItems();
260 
261  // Check that an element was selected
262  if (selected.empty())
263  return;
264 
265  // Get selected name and edit it
266  edit(selected[0]->text().toStdString());
267 }
268 
269 // ******************************************************************************************
270 // Edit vjoint
271 // ******************************************************************************************
272 void VirtualJointsWidget::edit(const std::string& name)
273 {
274  // Remember what we are editing
275  current_edit_vjoint_ = name;
276 
277  // Find the selected in datastruture
278  srdf::Model::VirtualJoint* vjoint = setup_step_.find(name);
279 
280  // Check if vjoint was found
281  if (vjoint == nullptr) // not found
282  {
283  QMessageBox::critical(this, "Error Saving", "An internal error has occurred while saving. Quitting.");
284  QApplication::quit();
285  }
286 
287  // Set vjoint name
288  vjoint_name_field_->setText(vjoint->name_.c_str());
289  parent_name_field_->setText(vjoint->parent_frame_.c_str());
290 
291  // Set vjoint child link
292  int index = child_link_field_->findText(vjoint->child_link_.c_str());
293  if (index == -1)
294  {
295  QMessageBox::critical(this, "Error Loading", "Unable to find child link in drop down box");
296  return;
297  }
298  child_link_field_->setCurrentIndex(index);
299 
300  // Set joint type
301  index = joint_type_field_->findText(vjoint->type_.c_str());
302  if (index == -1)
303  {
304  QMessageBox::critical(this, "Error Loading", "Unable to find joint type in drop down box");
305  return;
306  }
307  joint_type_field_->setCurrentIndex(index);
308 
309  // Switch to screen
310  stacked_widget_->setCurrentIndex(1);
311 
312  // Announce that this widget is in modal mode
313  Q_EMIT setModalMode(true);
314 }
315 
316 // ******************************************************************************************
317 // Populate the combo dropdown box with joint types
318 // ******************************************************************************************
319 void VirtualJointsWidget::loadJointTypesComboBox()
320 {
321  // Remove all old items
322  joint_type_field_->clear();
323 
324  // joint types (hard coded)
325  joint_type_field_->addItem("fixed");
326  joint_type_field_->addItem("floating");
327  joint_type_field_->addItem("planar");
328 }
329 
330 // ******************************************************************************************
331 // Populate the combo dropdown box with avail child links
332 // ******************************************************************************************
333 void VirtualJointsWidget::loadChildLinksComboBox()
334 {
335  // Remove all old links
336  child_link_field_->clear();
337 
338  // Add all links to combo box
339  for (const auto& link_name : setup_step_.getLinkNames())
340  {
341  child_link_field_->addItem(link_name.c_str());
342  }
343 }
344 
345 // ******************************************************************************************
346 // Delete currently editing item
347 // ******************************************************************************************
348 void VirtualJointsWidget::deleteSelected()
349 {
350  // Get list of all selected items
351  QList<QTableWidgetItem*> selected = data_table_->selectedItems();
352 
353  // Check that an element was selected
354  if (selected.empty())
355  return;
356 
357  // Get selected name and edit it
358  current_edit_vjoint_ = selected[0]->text().toStdString();
359 
360  // Confirm user wants to delete group
361  if (QMessageBox::question(this, "Confirm Virtual Joint Deletion",
362  QString("Are you sure you want to delete the virtual joint '")
363  .append(current_edit_vjoint_.c_str())
364  .append("'?"),
365  QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
366  {
367  return;
368  }
369 
370  // Delete vjoint from vector
371  setup_step_.remove(current_edit_vjoint_);
372 
373  // Reload main screen table
374  loadDataTable();
376 }
377 
378 // ******************************************************************************************
379 // Save editing changes
380 // ******************************************************************************************
381 void VirtualJointsWidget::doneEditing()
382 {
383  // Get a reference to the supplied strings
384  const std::string vjoint_name = vjoint_name_field_->text().trimmed().toStdString();
385  const std::string parent_name = parent_name_field_->text().trimmed().toStdString();
386  const std::string child_link = child_link_field_->currentText().trimmed().toStdString();
387  const std::string joint_type = joint_type_field_->currentText().trimmed().toStdString();
388 
389  // Check that name field is not empty
390  if (vjoint_name.empty())
391  {
392  QMessageBox::warning(this, "Error Saving", "A name must be given for the virtual joint!");
393  return;
394  }
395 
396  // Check that parent frame name field is not empty
397  if (parent_name.empty())
398  {
399  QMessageBox::warning(this, "Error Saving", "A name must be given for the parent frame");
400  return;
401  }
402 
403  // Check that a joint type was selected
404  if (joint_type.empty())
405  {
406  QMessageBox::warning(this, "Error Saving", "A joint type must be chosen!");
407  return;
408  }
409 
410  // Check that a child link was selected
411  if (child_link.empty())
412  {
413  QMessageBox::warning(this, "Error Saving", "A child link must be chosen!");
414  return;
415  }
416 
417  try
418  {
419  srdf::Model::VirtualJoint* vj = setup_step_.get(vjoint_name, current_edit_vjoint_);
420  setup_step_.setProperties(vj, parent_name, child_link, joint_type);
421  }
422  catch (const std::runtime_error& e)
423  {
424  QMessageBox::warning(this, "Error Saving", e.what());
425  return;
426  }
427 
428  // Finish up ------------------------------------------------------
429 
430  // Reload main screen table
431  loadDataTable();
432 
433  // Switch to screen
434  stacked_widget_->setCurrentIndex(0);
435 
436  // Announce that this widget is not in modal mode
437  Q_EMIT setModalMode(false);
438 
440 }
441 
442 // ******************************************************************************************
443 // Cancel changes
444 // ******************************************************************************************
445 void VirtualJointsWidget::cancelEditing()
446 {
447  // Switch to screen
448  stacked_widget_->setCurrentIndex(0);
449 
450  // Announce that this widget is not in modal mode
451  Q_EMIT setModalMode(false);
452 }
453 
454 // ******************************************************************************************
455 // Load the virtual joints into the table
456 // ******************************************************************************************
457 void VirtualJointsWidget::loadDataTable()
458 {
459  // Disable Table
460  data_table_->setUpdatesEnabled(false); // prevent table from updating until we are completely done
461  data_table_->setDisabled(true); // make sure we disable it so that the cellChanged event is not called
462  data_table_->clearContents();
463 
464  const auto& virtual_joints = setup_step_.getContainer();
465 
466  // Set size of datatable
467  data_table_->setRowCount(virtual_joints.size());
468 
469  // Loop through every virtual joint
470  int row = 0;
471  for (const auto& virtual_joint : virtual_joints)
472  {
473  // Create row elements
474  QTableWidgetItem* data_name = new QTableWidgetItem(virtual_joint.name_.c_str());
475  data_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
476  QTableWidgetItem* child_link_name = new QTableWidgetItem(virtual_joint.child_link_.c_str());
477  child_link_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
478  QTableWidgetItem* parent_frame_name = new QTableWidgetItem(virtual_joint.parent_frame_.c_str());
479  parent_frame_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
480  QTableWidgetItem* type_name = new QTableWidgetItem(virtual_joint.type_.c_str());
481  type_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
482 
483  // Add to table
484  data_table_->setItem(row, 0, data_name);
485  data_table_->setItem(row, 1, child_link_name);
486  data_table_->setItem(row, 2, parent_frame_name);
487  data_table_->setItem(row, 3, type_name);
488 
489  // Increment counter
490  ++row;
491  }
492 
493  // Re-enable
494  data_table_->setUpdatesEnabled(true); // prevent table from updating until we are completely done
495  data_table_->setDisabled(false); // make sure we disable it so that the cellChanged event is not called
496 
497  // Resize header
498  data_table_->resizeColumnToContents(0);
499  data_table_->resizeColumnToContents(1);
500  data_table_->resizeColumnToContents(2);
501  data_table_->resizeColumnToContents(3);
502 
503  // Show edit button if applicable
504  if (!virtual_joints.empty())
505  btn_edit_->show();
506 }
507 
508 // ******************************************************************************************
509 // Called when setup assistant navigation switches to this screen
510 // ******************************************************************************************
512 {
513  // Show the current vjoints screen
514  stacked_widget_->setCurrentIndex(0);
515 
516  // Load the data to the tree
517  loadDataTable();
518 
519  // Load the avail groups to the combo box
520  loadChildLinksComboBox();
521 }
522 
523 } // namespace srdf_setup
524 } // namespace moveit_setup
525 
526 #include <pluginlib/class_list_macros.hpp> // NOLINT
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
The GUI code for one SetupStep.
void setModalMode(bool isModal)
Event for when the current screen is in modal view. Disables the left navigation.
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
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 ...
Definition: srdf_step.hpp:164
bool remove(const std::string &name)
Delete an item with the given name from the list.
Definition: srdf_step.hpp:145
void focusGiven() override
Received when this widget is chosen from the navigation menu.
void setProperties(srdf::Model::VirtualJoint *vj, const std::string &parent_name, const std::string &child_name, const std::string &joint_type)
std::vector< std::string > getLinkNames() const
std::vector< srdf::Model::VirtualJoint > & getContainer() override
Returns the reference to the vector in the SRDF.
std::string append(const std::string &left, const std::string &right)
name
Definition: setup.py:7