moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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
48namespace moveit_setup
49{
50namespace 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 setLayout(layout);
78}
79
80// ******************************************************************************************
81// Create the main content widget
82// ******************************************************************************************
83QWidget* 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// ******************************************************************************************
151QWidget* 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// ******************************************************************************************
220void 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// ******************************************************************************************
241void VirtualJointsWidget::editDoubleClicked(int /*row*/, int /*column*/)
242{
243 editSelected();
244}
245
246// ******************************************************************************************
247// Preview whatever element is selected
248// ******************************************************************************************
249void VirtualJointsWidget::previewClicked(int /*row*/, int /*column*/)
250{
251}
252
253// ******************************************************************************************
254// Edit whatever element is selected
255// ******************************************************************************************
256void 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// ******************************************************************************************
272void 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// ******************************************************************************************
319void 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// ******************************************************************************************
333void 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// ******************************************************************************************
348void 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();
375 rviz_panel_->updateFixedFrame();
376}
377
378// ******************************************************************************************
379// Save editing changes
380// ******************************************************************************************
381void 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
439 rviz_panel_->updateFixedFrame();
440}
441
442// ******************************************************************************************
443// Cancel changes
444// ******************************************************************************************
445void 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// ******************************************************************************************
457void 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.
void focusGiven() override
Received when this widget is chosen from the navigation menu.
std::string append(const std::string &left, const std::string &right)
name
Definition setup.py:7