40 #include <moveit_msgs/JointLimits.h>
42 #include <QApplication>
44 #include <QDoubleValidator>
45 #include <QFontMetrics>
46 #include <QFormLayout>
49 #include <QMessageBox>
50 #include <QPushButton>
51 #include <QScrollArea>
53 #include <QStackedWidget>
54 #include <QTableWidget>
57 #include <moveit_msgs/DisplayRobotState.h>
69 joint_list_layout_ =
nullptr;
72 QVBoxLayout* layout =
new QVBoxLayout();
77 new HeaderWidget(
"Define Robot Poses",
78 "Create poses for the robot. Poses are defined as sets of joint values for "
79 "particular planning groups. This is useful for things like <i>home position</i>. "
80 "The <i>first</i> listed pose will be the robot's initial pose in simulation.",
82 layout->addWidget(header);
85 pose_list_widget_ = createContentsWidget();
86 pose_edit_widget_ = createEditWidget();
89 stacked_widget_ =
new QStackedWidget(
this);
90 stacked_widget_->addWidget(pose_list_widget_);
91 stacked_widget_->addWidget(pose_edit_widget_);
92 layout->addWidget(stacked_widget_);
95 this->setLayout(layout);
104 config_data_->getPlanningScene()->setName(
"MoveIt Planning Scene");
109 request.contacts =
true;
110 request.max_contacts = 1;
111 request.max_contacts_per_pair = 1;
112 request.verbose =
false;
118 QWidget* RobotPosesWidget::createContentsWidget()
121 QWidget* content_widget =
new QWidget(
this);
124 QVBoxLayout* layout =
new QVBoxLayout(
this);
128 data_table_ =
new QTableWidget(
this);
129 data_table_->setColumnCount(2);
130 data_table_->setSortingEnabled(
true);
131 data_table_->setSelectionBehavior(QAbstractItemView::SelectRows);
132 connect(data_table_, &QTableWidget::cellDoubleClicked,
this, &RobotPosesWidget::editDoubleClicked);
133 connect(data_table_, &QTableWidget::currentCellChanged,
this, &RobotPosesWidget::previewClicked);
134 layout->addWidget(data_table_);
137 QStringList header_list;
138 header_list.append(
"Pose Name");
139 header_list.append(
"Group Name");
140 data_table_->setHorizontalHeaderLabels(header_list);
144 QHBoxLayout* controls_layout =
new QHBoxLayout();
147 QPushButton* btn_default =
new QPushButton(
"&Show Default Pose",
this);
148 btn_default->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
149 btn_default->setMaximumWidth(300);
150 connect(btn_default, &QPushButton::clicked,
this, &RobotPosesWidget::showDefaultPose);
151 controls_layout->addWidget(btn_default);
152 controls_layout->setAlignment(btn_default, Qt::AlignLeft);
155 QPushButton* btn_play =
new QPushButton(
"&MoveIt",
this);
156 btn_play->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
157 btn_play->setMaximumWidth(300);
158 connect(btn_play, &QPushButton::clicked,
this, &RobotPosesWidget::playPoses);
159 controls_layout->addWidget(btn_play);
160 controls_layout->setAlignment(btn_play, Qt::AlignLeft);
163 controls_layout->addItem(
new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
166 btn_edit_ =
new QPushButton(
"&Edit Selected",
this);
167 btn_edit_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
168 btn_edit_->setMaximumWidth(300);
170 connect(btn_edit_, &QPushButton::clicked,
this, &RobotPosesWidget::editSelected);
171 controls_layout->addWidget(btn_edit_);
172 controls_layout->setAlignment(btn_edit_, Qt::AlignRight);
175 btn_delete_ =
new QPushButton(
"&Delete Selected",
this);
176 connect(btn_delete_, &QPushButton::clicked,
this, &RobotPosesWidget::deleteSelected);
177 controls_layout->addWidget(btn_delete_);
178 controls_layout->setAlignment(btn_delete_, Qt::AlignRight);
181 QPushButton* btn_add =
new QPushButton(
"&Add Pose",
this);
182 btn_add->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
183 btn_add->setMaximumWidth(300);
184 connect(btn_add, &QPushButton::clicked,
this, &RobotPosesWidget::showNewScreen);
185 controls_layout->addWidget(btn_add);
186 controls_layout->setAlignment(btn_add, Qt::AlignRight);
189 layout->addLayout(controls_layout);
192 content_widget->setLayout(layout);
194 return content_widget;
200 QWidget* RobotPosesWidget::createEditWidget()
203 QWidget* edit_widget =
new QWidget(
this);
205 QVBoxLayout* layout =
new QVBoxLayout();
209 QHBoxLayout* columns_layout =
new QHBoxLayout();
210 QVBoxLayout* column1 =
new QVBoxLayout();
211 column2_ =
new QVBoxLayout();
216 QFormLayout* form_layout =
new QFormLayout();
218 form_layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
221 pose_name_field_ =
new QLineEdit(
this);
223 form_layout->addRow(
"Pose Name:", pose_name_field_);
226 group_name_field_ =
new QComboBox(
this);
227 group_name_field_->setEditable(
false);
229 connect(group_name_field_, &QComboBox::currentTextChanged,
this, &RobotPosesWidget::loadJointSliders);
231 form_layout->addRow(
"Planning Group:", group_name_field_);
234 collision_warning_ =
new QLabel(
"<font color='red'><b>Robot in Collision State</b></font>",
this);
235 collision_warning_->setTextFormat(Qt::RichText);
236 collision_warning_->hide();
237 form_layout->addRow(
" ", collision_warning_);
239 column1->addLayout(form_layout);
240 columns_layout->addLayout(column1);
245 joint_list_widget_ =
new QWidget(
this);
248 scroll_area_ =
new QScrollArea(
this);
249 scroll_area_->setWidget(joint_list_widget_);
250 scroll_area_->setWidgetResizable(
true);
252 column2_->addWidget(scroll_area_);
254 columns_layout->addLayout(column2_);
257 layout->addLayout(columns_layout);
261 QHBoxLayout* controls_layout =
new QHBoxLayout();
262 controls_layout->setContentsMargins(0, 25, 0, 15);
265 controls_layout->addItem(
new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
268 btn_save_ =
new QPushButton(
"&Save",
this);
269 btn_save_->setMaximumWidth(200);
270 connect(btn_save_, &QPushButton::clicked,
this, &RobotPosesWidget::doneEditing);
271 controls_layout->addWidget(btn_save_);
272 controls_layout->setAlignment(btn_save_, Qt::AlignRight);
275 btn_cancel_ =
new QPushButton(
"&Cancel",
this);
276 btn_cancel_->setMaximumWidth(200);
277 connect(btn_cancel_, &QPushButton::clicked,
this, &RobotPosesWidget::cancelEditing);
278 controls_layout->addWidget(btn_cancel_);
279 controls_layout->setAlignment(btn_cancel_, Qt::AlignRight);
282 layout->addLayout(controls_layout);
285 edit_widget->setLayout(layout);
293 void RobotPosesWidget::showNewScreen()
296 stacked_widget_->setCurrentIndex(1);
299 current_edit_pose_ =
nullptr;
302 if (!group_name_field_->currentText().isEmpty())
303 loadJointSliders(group_name_field_->currentText());
306 pose_name_field_->setText(
"");
309 Q_EMIT isModal(
true);
315 void RobotPosesWidget::editDoubleClicked(
int ,
int )
324 void RobotPosesWidget::previewClicked(
int row,
int ,
int ,
int )
326 QTableWidgetItem*
name = data_table_->item(row, 0);
327 QTableWidgetItem*
group = data_table_->item(row, 1);
346 for (std::map<std::string, std::vector<double> >::const_iterator value_it = pose->
joint_values_.begin();
356 Q_EMIT unhighlightAll();
359 Q_EMIT highlightGroup(pose->
group_);
365 void RobotPosesWidget::showDefaultPose()
374 Q_EMIT unhighlightAll();
380 void RobotPosesWidget::playPoses()
383 for (std::vector<srdf::Model::GroupState>::iterator pose_it = config_data_->srdf_->group_states_.begin();
384 pose_it != config_data_->srdf_->group_states_.end(); ++pose_it)
387 showPose(&(*pose_it));
389 QApplication::processEvents();
397 void RobotPosesWidget::editSelected()
399 const auto& ranges = data_table_->selectedRanges();
402 edit(ranges[0].bottomRow());
408 void RobotPosesWidget::edit(
int row)
410 const std::string&
name = data_table_->item(row, 0)->text().toStdString();
411 const std::string&
group = data_table_->item(row, 1)->text().toStdString();
415 current_edit_pose_ = pose;
418 pose_name_field_->setText(pose->
name_.c_str());
421 int index = group_name_field_->findText(pose->
group_.c_str());
424 QMessageBox::critical(
this,
"Error Loading",
"Unable to find group name in drop down box");
427 group_name_field_->setCurrentIndex(index);
432 stacked_widget_->setCurrentIndex(1);
435 Q_EMIT isModal(
true);
438 loadJointSliders(QString(pose->
group_.c_str()));
444 void RobotPosesWidget::loadGroupsComboBox()
447 group_name_field_->clear();
452 group_name_field_->addItem(
group.name_.c_str());
459 void RobotPosesWidget::loadJointSliders(
const QString& selected)
463 if (!group_name_field_->count() || selected.isEmpty() || stacked_widget_->currentIndex() == 0)
467 const std::string group_name = selected.toStdString();
470 if (!config_data_->getRobotModel()->hasJointModelGroup(group_name))
472 QMessageBox::critical(
this,
"Error Loading",
473 QString(
"Unable to find joint model group for group: ")
474 .
append(group_name.c_str())
475 .append(
" Are you sure this group has associated joints/links?"));
480 if (joint_list_layout_)
482 delete joint_list_layout_;
483 qDeleteAll(joint_list_widget_->children());
487 joint_list_layout_ =
new QVBoxLayout();
488 joint_list_widget_->setLayout(joint_list_layout_);
489 joint_list_widget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
493 config_data_->getRobotModel()->getJointModelGroup(group_name);
494 const auto& robot_state = config_data_->getPlanningScene()->getCurrentState();
499 if (joint_model->getVariableCount() != 1 ||
500 joint_model->isPassive() ||
501 joint_model->getMimic())
508 joint_list_layout_->addWidget(sw);
511 connect(sw, &SliderWidget::jointValueChanged,
this, &RobotPosesWidget::updateRobotModel);
518 Q_EMIT unhighlightAll();
519 Q_EMIT highlightGroup(group_name);
534 searched_state = &state;
539 return searched_state;
545 void RobotPosesWidget::deleteSelected()
547 const auto& ranges = data_table_->selectedRanges();
550 int row = ranges[0].bottomRow();
552 const std::string&
name = data_table_->item(row, 0)->text().toStdString();
553 const std::string&
group = data_table_->item(row, 1)->text().toStdString();
556 if (QMessageBox::question(
this,
"Confirm Pose Deletion",
557 QString(
"Are you sure you want to delete the pose '").
append(
name.c_str()).append(
"'?"),
558 QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
564 for (std::vector<srdf::Model::GroupState>::iterator pose_it = config_data_->srdf_->group_states_.begin();
565 pose_it != config_data_->srdf_->group_states_.end(); ++pose_it)
568 if (pose_it->name_ == name && pose_it->group_ == group)
570 config_data_->srdf_->group_states_.erase(pose_it);
577 config_data_->changes |= MoveItConfigData::POSES;
583 void RobotPosesWidget::doneEditing()
586 const std::string&
name = pose_name_field_->text().toStdString();
587 const std::string&
group = group_name_field_->currentText().toStdString();
595 QMessageBox::warning(
this,
"Error Saving",
"A name must be given for the pose!");
596 pose_name_field_->setFocus();
602 QMessageBox::warning(
this,
"Error Saving",
"A planning group must be chosen!");
603 group_name_field_->setFocus();
608 if (!current_edit_pose_)
610 searched_data = findPoseByName(name, group);
611 if (searched_data != current_edit_pose_)
613 if (QMessageBox::warning(
this,
"Warning Saving",
"A pose already exists with that name! Overwrite?",
614 QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)
619 searched_data = current_edit_pose_;
621 config_data_->changes |= MoveItConfigData::POSES;
626 if (searched_data ==
nullptr)
642 const auto& robot_state = config_data_->getPlanningScene()->getCurrentState();
648 if (jm->getVariableCount() == 1 && !jm->isPassive() && !jm->getMimic())
651 std::vector<double> joint_values(jm->getVariableCount());
652 const double*
const first_variable = robot_state.
getVariablePositions() + jm->getFirstVariableIndex();
653 std::copy(first_variable, first_variable + joint_values.size(), joint_values.begin());
656 searched_data->
joint_values_[jm->getName()] = std::move(joint_values);
663 config_data_->srdf_->group_states_.push_back(*searched_data);
664 delete searched_data;
673 stacked_widget_->setCurrentIndex(0);
676 Q_EMIT isModal(
false);
682 void RobotPosesWidget::cancelEditing()
685 stacked_widget_->setCurrentIndex(0);
688 Q_EMIT isModal(
false);
694 void RobotPosesWidget::loadDataTable()
697 data_table_->setUpdatesEnabled(
false);
698 data_table_->setDisabled(
true);
699 data_table_->clearContents();
702 data_table_->setRowCount(config_data_->srdf_->group_states_.size());
706 for (
const auto& group_state : config_data_->srdf_->group_states_)
709 QTableWidgetItem* data_name =
new QTableWidgetItem(group_state.name_.c_str());
710 data_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
711 QTableWidgetItem* group_name =
new QTableWidgetItem(group_state.group_.c_str());
712 group_name->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
715 data_table_->setItem(row, 0, data_name);
716 data_table_->setItem(row, 1, group_name);
723 data_table_->setUpdatesEnabled(
true);
724 data_table_->setDisabled(
false);
727 data_table_->resizeColumnToContents(0);
728 data_table_->resizeColumnToContents(1);
731 if (!config_data_->srdf_->group_states_.empty())
738 void RobotPosesWidget::focusGiven()
741 stacked_widget_->setCurrentIndex(0);
747 loadGroupsComboBox();
753 void RobotPosesWidget::updateRobotModel(
const std::string& name,
double value)
765 void RobotPosesWidget::publishJoints()
768 auto& robot_state = config_data_->getPlanningScene()->getCurrentStateNonConst();
771 moveit_msgs::DisplayRobotState msg;
775 pub_robot_state_.publish(msg);
779 config_data_->getPlanningScene()->checkSelfCollision(request, result, robot_state,
780 config_data_->allowed_collision_matrix_);
781 collision_warning_->setHidden(result.
contacts.empty());
794 : QWidget(parent), joint_model_(joint_model)
797 QVBoxLayout* layout =
new QVBoxLayout();
798 QHBoxLayout* row2 =
new QHBoxLayout();
801 joint_label_ =
new QLabel(joint_model_->getName().c_str(),
this);
802 joint_label_->setContentsMargins(0, 0, 0, 0);
803 layout->addWidget(joint_label_);
806 joint_slider_ =
new QSlider(Qt::Horizontal,
this);
807 joint_slider_->setTickPosition(QSlider::TicksBelow);
808 joint_slider_->setSingleStep(10);
809 joint_slider_->setPageStep(500);
810 joint_slider_->setTickInterval(1000);
811 joint_slider_->setContentsMargins(0, 0, 0, 0);
812 row2->addWidget(joint_slider_);
814 QFontMetrics
m{ QFont() };
816 joint_value_ =
new QLineEdit(
this);
817 joint_value_->setMaximumWidth(
m.boundingRect(
"0000.00000").width());
818 joint_value_->setContentsMargins(0, 0, 0, 0);
820 row2->addWidget(joint_value_);
823 const std::vector<moveit_msgs::JointLimits>& limits = joint_model_->getVariableBoundsMsg();
826 QMessageBox::critical(
this,
"Error Loading",
"An internal error has occured while loading the joints");
831 moveit_msgs::JointLimits joint_limit = limits[0];
832 max_position_ = joint_limit.max_position;
833 min_position_ = joint_limit.min_position;
836 joint_slider_->setMaximum(max_position_ * 10000);
837 joint_slider_->setMinimum(min_position_ * 10000);
843 int value = init_value * 10000;
844 joint_slider_->setSliderPosition(value);
845 changeJointValue(value);
848 layout->addLayout(row2);
850 this->setContentsMargins(0, 0, 0, 0);
852 this->setGeometry(QRect(110, 80, 120, 80));
853 this->setLayout(layout);
856 qRegisterMetaType<std::string>(
"std::string");
870 const double double_value = double(value) / 10000;
873 joint_value_->setText(QString(
"%1").arg(double_value, 0,
'f', 4));
890 joint_value_->setText(QString(
"%1").arg(value, 0,
'f', 4));