/* * Copyright (C) 2015 Open Source Robotics Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include #include #include "gazebo/common/Console.hh" #include "gazebo/rendering/Material.hh" #include "gazebo/gui/ConfigWidget.hh" #include "gazebo/gui/GuiIface.hh" #include "gazebo/gui/MainWindow.hh" #include "gazebo/gui/model/JointCreationDialog.hh" #include "gazebo/gui/model/JointCreationDialogPrivate.hh" #include "gazebo/gui/model/JointMaker.hh" using namespace gazebo; using namespace gui; ///////////////////////////////////////////////// JointCreationDialog::JointCreationDialog(JointMaker *_jointMaker, QWidget *_parent) : QDialog(_parent), dataPtr(new JointCreationDialogPrivate) { this->setObjectName("JointCreationDialog"); this->setWindowTitle(tr("Create Joint")); this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint); this->setModal(false); // Dialog size this->setMinimumWidth(500); this->setMinimumHeight(300); int desiredHeight = 940; MainWindow *mainWindow = qobject_cast(_parent); if (mainWindow) { desiredHeight = std::min(desiredHeight, (mainWindow->height()-100)); } this->resize(500, desiredHeight); this->dataPtr->jointMaker = _jointMaker; // ConfigWidget this->dataPtr->configWidget = new ConfigWidget(); // Joint types QRadioButton *fixedJointRadio = new QRadioButton(tr("Fixed")); QRadioButton *prismaticJointRadio = new QRadioButton(tr("Prismatic")); QRadioButton *revoluteJointRadio = new QRadioButton(tr("Revolute")); QRadioButton *revolute2JointRadio = new QRadioButton(tr("Revolute2")); QRadioButton *screwJointRadio = new QRadioButton(tr("Screw")); QRadioButton *universalJointRadio = new QRadioButton(tr("Universal")); QRadioButton *ballJointRadio = new QRadioButton(tr("Ball")); QRadioButton *gearboxJointRadio = new QRadioButton(tr("Gearbox")); this->dataPtr->typeButtons = new QButtonGroup(); this->dataPtr->typeButtons->addButton(fixedJointRadio, 1); this->dataPtr->typeButtons->addButton(prismaticJointRadio, 2); this->dataPtr->typeButtons->addButton(revoluteJointRadio, 3); this->dataPtr->typeButtons->addButton(revolute2JointRadio, 4); this->dataPtr->typeButtons->addButton(screwJointRadio, 5); this->dataPtr->typeButtons->addButton(universalJointRadio, 6); this->dataPtr->typeButtons->addButton(ballJointRadio, 7); this->dataPtr->typeButtons->addButton(gearboxJointRadio, 8); connect(this->dataPtr->typeButtons, SIGNAL(buttonClicked(int)), this->dataPtr->jointMaker, SLOT(OnType(int))); connect(this->dataPtr->typeButtons, SIGNAL(buttonClicked(int)), this, SLOT(OnType(int))); for (auto button : this->dataPtr->typeButtons->buttons()) { button->setStyleSheet("QRadioButton{color : #d0d0d0}"); } // Types layout QGridLayout *typesLayout = new QGridLayout(); typesLayout->addWidget(fixedJointRadio, 0, 0); typesLayout->addWidget(revoluteJointRadio, 1, 0); typesLayout->addWidget(revolute2JointRadio, 2, 0); typesLayout->addWidget(prismaticJointRadio, 3, 0); typesLayout->addWidget(screwJointRadio, 0, 1); typesLayout->addWidget(universalJointRadio, 1, 1); typesLayout->addWidget(ballJointRadio, 2, 1); typesLayout->addWidget(gearboxJointRadio, 3, 1); // Types group widget ConfigChildWidget *typesWidget = new ConfigChildWidget(); typesWidget->setLayout(typesLayout); QWidget *typesGroupWidget = this->dataPtr->configWidget->CreateGroupWidget("Joint types", typesWidget, 0); // Link selections this->dataPtr->selectionsText = new QLabel(tr( "Click a link in the scene to select parent.\n" "Click again to select child.")); // Parent config std::vector links; this->dataPtr->parentLinkWidget = this->dataPtr->configWidget->CreateEnumWidget("parent", links, 0); this->dataPtr->parentLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("active", 1)); this->dataPtr->configWidget->AddConfigChildWidget("parentCombo", this->dataPtr->parentLinkWidget); // Resize parent label auto parentLabel = this->dataPtr->parentLinkWidget->findChild(); parentLabel->setMaximumWidth(50); // Add parent icon this->dataPtr->parentIcon = new QLabel(); this->dataPtr->parentIcon->setMinimumWidth(15); this->dataPtr->parentIcon->setMaximumHeight(15); auto parentLayout = qobject_cast( this->dataPtr->parentLinkWidget->layout()); if (parentLayout) { parentLayout->insertWidget(1, this->dataPtr->parentIcon); parentLayout->setAlignment(this->dataPtr->parentIcon, Qt::AlignLeft); } // Child config this->dataPtr->childLinkWidget = this->dataPtr->configWidget->CreateEnumWidget("child", links, 0); this->dataPtr->childLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->configWidget->AddConfigChildWidget("childCombo", this->dataPtr->childLinkWidget); this->dataPtr->configWidget->SetWidgetReadOnly("childCombo", true); // Resize child label auto childLabel = this->dataPtr->childLinkWidget->findChild(); childLabel->setMaximumWidth(50); // Add child icon QPixmap childPix(":/images/child-link.png"); childPix = childPix.scaled(15, 15); auto childIcon = new QLabel(); childIcon->setPixmap(childPix); childIcon->setMaximumWidth(15); auto childLayout = qobject_cast( this->dataPtr->childLinkWidget->layout()); if (childLayout) { childLayout->insertWidget(1, childIcon); childLayout->setAlignment(childIcon, Qt::AlignLeft); } // Connect all enum value changes QObject::connect(this->dataPtr->configWidget, SIGNAL(EnumValueChanged(const QString &, const QString &)), this, SLOT(OnEnumChanged(const QString &, const QString &))); // Swap button this->dataPtr->swapButton = new QToolButton(); this->dataPtr->swapButton->setObjectName("JointCreationSwapButton"); this->dataPtr->swapButton->setToolButtonStyle(Qt::ToolButtonIconOnly); this->dataPtr->swapButton->setIcon( QPixmap(":/images/swap-parent-child.png")); this->dataPtr->swapButton->setFixedSize(QSize(50, 50)); this->dataPtr->swapButton->setIconSize(QSize(35, 35)); this->dataPtr->swapButton->setToolTip("Swap parent and child"); this->dataPtr->swapButton->setStyleSheet( "QToolButton\ {\ background-color: " + ConfigWidget::bgColors[0] + "}"); connect(this->dataPtr->swapButton, SIGNAL(clicked()), this, SLOT(OnSwap())); // Links layout QGridLayout *linksLayout = new QGridLayout(); linksLayout->setContentsMargins(0, 0, 0, 0); linksLayout->setSpacing(0); linksLayout->addWidget(this->dataPtr->selectionsText, 0, 0, 1, 2); linksLayout->addWidget(this->dataPtr->parentLinkWidget, 1, 0); linksLayout->addWidget(this->dataPtr->childLinkWidget, 2, 0); linksLayout->addWidget(this->dataPtr->swapButton, 1, 1, 2, 1); // Links group widget ConfigChildWidget *linksWidget = new ConfigChildWidget(); linksWidget->setLayout(linksLayout); QWidget *linksGroupWidget = this->dataPtr->configWidget->CreateGroupWidget( "Link Selections", linksWidget, 0); // Label when joint has no axes this->dataPtr->axis0Widget = new QLabel( tr("The joint type chosen has no axes")); // Axis1 widget this->dataPtr->axis1Widget = this->dataPtr->configWidget->CreateVector3dWidget("axis1", 0); this->dataPtr->configWidget->AddConfigChildWidget("axis1", this->dataPtr->axis1Widget); this->dataPtr->configWidget->SetVector3dWidgetValue("axis1", ignition::math::Vector3d::UnitX); this->dataPtr->configWidget->SetWidgetReadOnly("axis1", true); // Axis2 widget this->dataPtr->axis2Widget = this->dataPtr->configWidget->CreateVector3dWidget("axis2", 0); this->dataPtr->configWidget->AddConfigChildWidget("axis2", this->dataPtr->axis2Widget); this->dataPtr->configWidget->SetVector3dWidgetValue("axis2", ignition::math::Vector3d::UnitY); this->dataPtr->configWidget->SetWidgetReadOnly("axis2", true); // Axis general layout auto axisGeneralLayout = new QVBoxLayout(); axisGeneralLayout->setContentsMargins(0, 0, 0, 0); axisGeneralLayout->setSpacing(0); axisGeneralLayout->addWidget(this->dataPtr->axis0Widget); axisGeneralLayout->addWidget(this->dataPtr->axis1Widget); axisGeneralLayout->addWidget(this->dataPtr->axis2Widget); ConfigChildWidget *axisGeneralWidget = new ConfigChildWidget(); axisGeneralWidget->setLayout(axisGeneralLayout); QWidget *axisGroupWidget = this->dataPtr->configWidget->CreateGroupWidget( "Joint axis", axisGeneralWidget, 0); // Axis signals connect(this->dataPtr->configWidget, SIGNAL(Vector3dValueChanged(const QString, const ignition::math::Vector3d)), this, SLOT(OnVector3dFromDialog(const QString, const ignition::math::Vector3d))); // Align widgets QLabel *xAlignLabel = new QLabel(tr("X: ")); xAlignLabel->setMaximumWidth(20); QLabel *yAlignLabel = new QLabel(tr("Y: ")); yAlignLabel->setMaximumWidth(20); QLabel *zAlignLabel = new QLabel(tr("Z: ")); zAlignLabel->setMaximumWidth(20); auto xAlignMin = new QToolButton(); xAlignMin->setToolButtonStyle(Qt::ToolButtonIconOnly); xAlignMin->setIcon(QIcon(":/images/x_min.png")); xAlignMin->setToolTip("X Align Min"); xAlignMin->setCheckable(true); auto xAlignCenter = new QToolButton(); xAlignCenter->setToolButtonStyle(Qt::ToolButtonIconOnly); xAlignCenter->setIcon(QIcon(":/images/x_center.png")); xAlignCenter->setToolTip("X Align Center"); xAlignCenter->setCheckable(true); auto xAlignMax = new QToolButton(); xAlignMax->setToolButtonStyle(Qt::ToolButtonIconOnly); xAlignMax->setIcon(QIcon(":/images/x_max.png")); xAlignMax->setToolTip("X Align Max"); xAlignMax->setCheckable(true); auto yAlignMin = new QToolButton(); yAlignMin->setToolButtonStyle(Qt::ToolButtonIconOnly); yAlignMin->setIcon(QIcon(":/images/y_min.png")); yAlignMin->setToolTip("Y Align Min"); yAlignMin->setCheckable(true); auto yAlignCenter = new QToolButton(); yAlignCenter->setToolButtonStyle(Qt::ToolButtonIconOnly); yAlignCenter->setIcon(QIcon(":/images/y_center.png")); yAlignCenter->setToolTip("Y Align Center"); yAlignCenter->setCheckable(true); auto yAlignMax = new QToolButton(); yAlignMax->setToolButtonStyle(Qt::ToolButtonIconOnly); yAlignMax->setIcon(QIcon(":/images/y_max.png")); yAlignMax->setToolTip("Y Align Max"); yAlignMax->setCheckable(true); auto zAlignMin = new QToolButton(); zAlignMin->setToolButtonStyle(Qt::ToolButtonIconOnly); zAlignMin->setIcon(QIcon(":/images/z_min.png")); zAlignMin->setToolTip("Z Align Min"); zAlignMin->setCheckable(true); auto zAlignCenter = new QToolButton(); zAlignCenter->setToolButtonStyle(Qt::ToolButtonIconOnly); zAlignCenter->setIcon(QIcon(":/images/z_center.png")); zAlignCenter->setToolTip("Z Align Center"); zAlignCenter->setCheckable(true); auto zAlignMax = new QToolButton(); zAlignMax->setToolButtonStyle(Qt::ToolButtonIconOnly); zAlignMax->setIcon(QIcon(":/images/z_max.png")); zAlignMax->setToolTip("Z Align Max"); zAlignMax->setCheckable(true); auto alignXButtonGroup = new QButtonGroup(); alignXButtonGroup->setExclusive(false); alignXButtonGroup->addButton(xAlignMin, 0); alignXButtonGroup->addButton(xAlignCenter, 1); alignXButtonGroup->addButton(xAlignMax, 2); connect(alignXButtonGroup, SIGNAL(buttonClicked(const int)), this, SLOT(OnAlign(const int))); this->dataPtr->alignGroups.push_back(alignXButtonGroup); auto alignYButtonGroup = new QButtonGroup(); alignYButtonGroup->setExclusive(false); alignYButtonGroup->addButton(yAlignMin, 0); alignYButtonGroup->addButton(yAlignCenter, 1); alignYButtonGroup->addButton(yAlignMax, 2); connect(alignYButtonGroup, SIGNAL(buttonClicked(const int)), this, SLOT(OnAlign(const int))); this->dataPtr->alignGroups.push_back(alignYButtonGroup); auto alignZButtonGroup = new QButtonGroup(); alignZButtonGroup->setExclusive(false); alignZButtonGroup->addButton(zAlignMin, 0); alignZButtonGroup->addButton(zAlignCenter, 1); alignZButtonGroup->addButton(zAlignMax, 2); connect(alignZButtonGroup, SIGNAL(buttonClicked(const int)), this, SLOT(OnAlign(const int))); this->dataPtr->alignGroups.push_back(alignZButtonGroup); // Reverse checkboxes this->dataPtr->reverseXBox = new QCheckBox("Reverse"); connect(this->dataPtr->reverseXBox, SIGNAL(stateChanged(const int)), this, SLOT(OnAlign(const int))); this->dataPtr->reverseYBox = new QCheckBox("Reverse"); connect(this->dataPtr->reverseYBox, SIGNAL(stateChanged(const int)), this, SLOT(OnAlign(const int))); this->dataPtr->reverseZBox = new QCheckBox("Reverse"); connect(this->dataPtr->reverseZBox, SIGNAL(stateChanged(const int)), this, SLOT(OnAlign(const int))); // Target dropdown this->dataPtr->alignCombo = new QComboBox(); this->dataPtr->alignCombo->addItem("Child to Parent", 0); this->dataPtr->alignCombo->addItem("Parent to Child", 1); this->dataPtr->alignCombo->setToolTip("Choose alignment reference"); connect(this->dataPtr->alignCombo, SIGNAL(currentIndexChanged(const int)), this, SLOT(OnAlign(const int))); // Align layout QGridLayout *alignLayout = new QGridLayout(); alignLayout->addWidget(xAlignLabel, 0, 0); alignLayout->addWidget(xAlignMin, 0, 1); alignLayout->addWidget(xAlignCenter, 0, 2); alignLayout->addWidget(xAlignMax, 0, 3); alignLayout->addWidget(yAlignLabel, 1, 0); alignLayout->addWidget(yAlignMin, 1, 1); alignLayout->addWidget(yAlignCenter, 1, 2); alignLayout->addWidget(yAlignMax, 1, 3); alignLayout->addWidget(zAlignLabel, 2, 0); alignLayout->addWidget(zAlignMin, 2, 1); alignLayout->addWidget(zAlignCenter, 2, 2); alignLayout->addWidget(zAlignMax, 2, 3); alignLayout->addWidget(this->dataPtr->reverseXBox, 0, 4, 1, 3); alignLayout->addWidget(this->dataPtr->reverseYBox, 1, 4, 1, 3); alignLayout->addWidget(this->dataPtr->reverseZBox, 2, 4, 1, 3); alignLayout->addWidget(this->dataPtr->alignCombo, 3, 0, 1, 5); alignLayout->setAlignment(xAlignLabel, Qt::AlignRight); alignLayout->setAlignment(yAlignLabel, Qt::AlignRight); alignLayout->setAlignment(zAlignLabel, Qt::AlignRight); // Align group widget ConfigChildWidget *alignWidget = new ConfigChildWidget(); alignWidget->setLayout(alignLayout); this->dataPtr->configWidget->AddConfigChildWidget("align", alignWidget); this->dataPtr->configWidget->SetWidgetReadOnly("align", true); QWidget *alignGroupWidget = this->dataPtr->configWidget->CreateGroupWidget("Align links", alignWidget, 0); // Joint pose widget ConfigChildWidget *jointPoseWidget = this->dataPtr->configWidget->CreatePoseWidget("joint_pose", 0); this->dataPtr->configWidget->AddConfigChildWidget("joint_pose", jointPoseWidget); this->dataPtr->configWidget->SetWidgetReadOnly("joint_pose", true); QWidget *jointPoseGroupWidget = this->dataPtr->configWidget->CreateGroupWidget( "Joint Pose", jointPoseWidget, 0); jointPoseGroupWidget->setToolTip( "Joint pose expressed in the child link frame"); // Relative pose widget ConfigChildWidget *relativePoseWidget = this->dataPtr->configWidget->CreatePoseWidget("relative_pose", 0); this->dataPtr->configWidget->AddConfigChildWidget("relative_pose", relativePoseWidget); this->dataPtr->configWidget->SetWidgetReadOnly("relative_pose", true); QWidget *relativePoseGroupWidget = this->dataPtr->configWidget->CreateGroupWidget( "Relative Pose", relativePoseWidget, 0); relativePoseGroupWidget->setToolTip( "Child link pose expressed in parent link frame"); // Connect pose widgets signal connect(this->dataPtr->configWidget, SIGNAL(PoseValueChanged(const QString, const ignition::math::Pose3d)), this, SLOT(OnPoseFromDialog(const QString, const ignition::math::Pose3d))); // Spacer QWidget *spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Config Widget layout QVBoxLayout *configLayout = new QVBoxLayout(); configLayout->setSpacing(0); configLayout->addWidget(typesGroupWidget); configLayout->addWidget(linksGroupWidget); configLayout->addWidget(axisGroupWidget); configLayout->addWidget(alignGroupWidget); configLayout->addWidget(jointPoseGroupWidget); configLayout->addWidget(relativePoseGroupWidget); configLayout->addWidget(spacer); this->dataPtr->configWidget->setLayout(configLayout); // Scroll area QScrollArea *scrollArea = new QScrollArea; scrollArea->setWidget(this->dataPtr->configWidget); scrollArea->setWidgetResizable(true); // General layout QVBoxLayout *generalLayout = new QVBoxLayout; generalLayout->setContentsMargins(0, 0, 0, 0); generalLayout->addWidget(scrollArea); // Reset all button QPushButton *resetAllButton = new QPushButton(tr("Reset")); resetAllButton->setObjectName("JointCreationResetButton"); resetAllButton->setToolTip("Reset"); connect(resetAllButton, SIGNAL(clicked()), this, SLOT(OnResetAll())); // Cancel button QPushButton *cancelButton = new QPushButton(tr("Cancel")); cancelButton->setObjectName("JointCreationCancelButton"); connect(cancelButton, SIGNAL(clicked()), this, SLOT(OnCancel())); // Create button this->dataPtr->createButton = new QPushButton(tr("Create")); this->dataPtr->createButton->setObjectName("JointCreationCreateButton"); this->dataPtr->createButton->setEnabled(false); connect(this->dataPtr->createButton, SIGNAL(clicked()), this, SLOT(OnCreate())); // Buttons layout QHBoxLayout *buttonsLayout = new QHBoxLayout; buttonsLayout->addWidget(resetAllButton); buttonsLayout->addWidget(cancelButton); buttonsLayout->addWidget(this->dataPtr->createButton); buttonsLayout->setAlignment(Qt::AlignRight); // Main layout QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(generalLayout); mainLayout->addLayout(buttonsLayout); this->setLayout(mainLayout); // Qt signal-slot connections connect(this, SIGNAL(rejected()), this, SLOT(OnCancel())); // Initialize variables this->dataPtr->validLinks = false; this->dataPtr->validAxis1 = true; this->dataPtr->validAxis2 = true; } ///////////////////////////////////////////////// JointCreationDialog::~JointCreationDialog() { } ///////////////////////////////////////////////// void JointCreationDialog::Open(const JointMaker::JointType _type) { if (!this->dataPtr->jointMaker) { gzerr << "Joint maker not found, can't open joint creation dialog." << std::endl; this->OnCancel(); } // Check joint type this->dataPtr->typeButtons->button(static_cast(_type)) ->setChecked(true); this->OnType(_type); // Clear links this->dataPtr->configWidget->ClearEnumWidget("parentCombo"); this->dataPtr->configWidget->ClearEnumWidget("childCombo"); // Add an empty option to each this->dataPtr->configWidget->AddItemEnumWidget("parentCombo", ""); this->dataPtr->configWidget->AddItemEnumWidget("childCombo", ""); // Fill with all existing links for (auto link : this->dataPtr->jointMaker->LinkList()) { this->dataPtr->configWidget->AddItemEnumWidget("parentCombo", link.second); this->dataPtr->configWidget->AddItemEnumWidget("childCombo", link.second); } // Reset fields this->OnResetAll(); // Reset enabled states this->dataPtr->createButton->setEnabled(false); this->dataPtr->swapButton->setEnabled(false); this->dataPtr->configWidget->SetWidgetReadOnly("childCombo", true); this->dataPtr->configWidget->SetWidgetReadOnly("axis1", true); this->dataPtr->configWidget->SetWidgetReadOnly("axis2", true); this->dataPtr->configWidget->SetWidgetReadOnly("align", true); this->dataPtr->configWidget->SetWidgetReadOnly("joint_pose", true); this->dataPtr->configWidget->SetWidgetReadOnly("relative_pose", true); this->dataPtr->parentLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("active", 1)); this->dataPtr->childLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->axis1Widget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->axis2Widget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); if (this->parentWidget()) this->move(this->parentWidget()->pos()); this->show(); QApplication::setOverrideCursor(QCursor(Qt::CrossCursor)); } ///////////////////////////////////////////////// void JointCreationDialog::OnLinkFromDialog() { std::string currentParent = this->dataPtr->configWidget->EnumWidgetValue("parentCombo"); std::string currentChild = this->dataPtr->configWidget->EnumWidgetValue("childCombo"); // Notify so 3D is updated if (currentParent != currentChild) { if (currentParent != "") this->dataPtr->jointMaker->SetParentLink(currentParent); if (currentChild != "") this->dataPtr->jointMaker->SetChildLink(currentChild); } if (currentParent != "") this->OnParentImpl(currentParent); if (currentChild != "") this->OnChildImpl(currentChild); this->UncheckAllAlign(); } ///////////////////////////////////////////////// void JointCreationDialog::OnPoseFromDialog(const QString &_name, const ignition::math::Pose3d &_pose) { if (_name == "relative_pose") this->dataPtr->jointMaker->SetLinksRelativePose(_pose, false); if (_name == "joint_pose") this->dataPtr->jointMaker->SetJointPose(_pose); } ///////////////////////////////////////////////// void JointCreationDialog::OnVector3dFromDialog(const QString &_name, const ignition::math::Vector3d &_value) { if (_value == ignition::math::Vector3d::Zero) { if (_name == "axis1") { this->dataPtr->axis1Widget->setStyleSheet( ConfigWidget::StyleSheet("warning", 1)); this->dataPtr->validAxis1 = false; } else if (_name == "axis2") { this->dataPtr->axis2Widget->setStyleSheet( ConfigWidget::StyleSheet("warning", 1)); this->dataPtr->validAxis2 = false; } } else { if (_name == "axis1") { this->dataPtr->axis1Widget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->validAxis1 = true; } else if (_name == "axis2") { this->dataPtr->axis2Widget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->validAxis2 = true; } } if (this->CheckValid()) this->dataPtr->jointMaker->SetAxis(_name.toStdString(), _value); } ///////////////////////////////////////////////// void JointCreationDialog::SetParent(const std::string &_linkName) { if (_linkName.empty()) { gzerr << "Empty link name for parent" << std::endl; return; } std::string unscopedName = _linkName; size_t idx = _linkName.find("::"); if (idx != std::string::npos) unscopedName = _linkName.substr(idx+2); this->dataPtr->configWidget->blockSignals(true); if (!this->dataPtr->configWidget->SetEnumWidgetValue("parentCombo", unscopedName)) { gzerr << "Requested link [" << unscopedName << "] not found" << std::endl; return; } this->dataPtr->configWidget->blockSignals(false); this->OnParentImpl(_linkName); } ///////////////////////////////////////////////// void JointCreationDialog::SetChild(const std::string &_linkName) { if (_linkName.empty()) { gzerr << "Empty link name for child" << std::endl; return; } if (this->dataPtr->configWidget->WidgetReadOnly("childCombo")) { gzerr << "It's not possible to set child yet." << std::endl; return; } // Update child combo box std::string unscopedName = _linkName; size_t idx = _linkName.find("::"); if (idx != std::string::npos) unscopedName = _linkName.substr(idx+2); this->dataPtr->configWidget->blockSignals(true); if (!this->dataPtr->configWidget->SetEnumWidgetValue("childCombo", unscopedName)) { gzerr << "Requested link [" << unscopedName << "] not found" << std::endl; return; } this->dataPtr->configWidget->blockSignals(false); this->OnChildImpl(_linkName); } ///////////////////////////////////////////////// void JointCreationDialog::OnParentImpl(const std::string &_linkName) { if (_linkName.empty()) { gzerr << "Empty link name for parent" << std::endl; return; } // Remove empty option this->dataPtr->configWidget->RemoveItemEnumWidget("parentCombo", ""); // Check if links are valid this->CheckLinksValid(); // Enable child selection if (this->dataPtr->configWidget->WidgetReadOnly("childCombo")) { this->dataPtr->configWidget->SetWidgetReadOnly("childCombo", false); this->dataPtr->childLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("active", 1)); } } ///////////////////////////////////////////////// void JointCreationDialog::OnChildImpl(const std::string &_linkName) { if (_linkName.empty()) { gzerr << "Empty link name for child" << std::endl; return; } // Enable features this->dataPtr->createButton->setEnabled(true); this->dataPtr->swapButton->setEnabled(true); this->dataPtr->configWidget->SetWidgetReadOnly("axis1", false); this->dataPtr->configWidget->SetWidgetReadOnly("axis2", false); this->dataPtr->configWidget->SetWidgetReadOnly("joint_pose", false); this->dataPtr->configWidget->SetWidgetReadOnly("align", false); this->dataPtr->configWidget->SetWidgetReadOnly("relative_pose", false); // Remove empty option this->dataPtr->configWidget->RemoveItemEnumWidget("childCombo", ""); // Hide selection text this->dataPtr->selectionsText->setVisible(false); QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); // Check if links are valid this->CheckLinksValid(); } ///////////////////////////////////////////////// void JointCreationDialog::OnCancel() { this->close(); this->dataPtr->jointMaker->Stop(); } ///////////////////////////////////////////////// void JointCreationDialog::OnCreate() { this->accept(); this->dataPtr->jointMaker->FinalizeCreation(); } ///////////////////////////////////////////////// void JointCreationDialog::OnSwap() { // Get current values std::string currentParent = this->dataPtr->configWidget->EnumWidgetValue("parentCombo"); std::string currentChild = this->dataPtr->configWidget->EnumWidgetValue("childCombo"); // Choose new values this->dataPtr->configWidget->SetEnumWidgetValue("parentCombo", currentChild); this->dataPtr->configWidget->SetEnumWidgetValue("childCombo", currentParent); } ///////////////////////////////////////////////// void JointCreationDialog::UpdateRelativePose( const ignition::math::Pose3d &_pose) { this->dataPtr->configWidget->SetPoseWidgetValue("relative_pose", _pose); if (this->dataPtr->alignPending) { this->dataPtr->alignPending = false; return; } this->UncheckAllAlign(true); } ///////////////////////////////////////////////// void JointCreationDialog::OnResetAll() { this->dataPtr->jointMaker->SetLinksRelativePose(ignition::math::Pose3d::Zero, true); this->dataPtr->configWidget->SetVector3dWidgetValue("axis1", ignition::math::Vector3d::UnitX); this->dataPtr->configWidget->SetVector3dWidgetValue("axis2", ignition::math::Vector3d::UnitY); this->dataPtr->configWidget->SetPoseWidgetValue("joint_pose", ignition::math::Pose3d::Zero); this->OnPoseFromDialog("joint_pose", ignition::math::Pose3d::Zero); this->UncheckAllAlign(); } ///////////////////////////////////////////////// void JointCreationDialog::OnEnumChanged(const QString &_name, const QString &/*_value*/) { if (_name == "parentCombo" || _name == "childCombo") this->OnLinkFromDialog(); } ///////////////////////////////////////////////// void JointCreationDialog::CheckLinksValid() { std::string currentParent = this->dataPtr->configWidget->EnumWidgetValue("parentCombo"); std::string currentChild = this->dataPtr->configWidget->EnumWidgetValue("childCombo"); // Warning if parent is the same as the child and don't allow creation if (currentParent == currentChild) { this->dataPtr->parentLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("warning", 1)); this->dataPtr->childLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("warning", 1)); this->dataPtr->validLinks = false; } else { this->dataPtr->parentLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); if (!this->dataPtr->configWidget->WidgetReadOnly("childCombo")) { this->dataPtr->childLinkWidget->setStyleSheet( ConfigWidget::StyleSheet("normal", 1)); this->dataPtr->validLinks = true; } } // Disable align and relative_pose widgets if parent or child link // is a nested link // TODO: re-enable once pose of nested model links can be updated if (currentParent.find("::") != std::string::npos || currentChild.find("::") != std::string::npos) { this->dataPtr->configWidget->SetWidgetReadOnly("align", true); this->dataPtr->configWidget->SetWidgetReadOnly("relative_pose", true); gzwarn << "Pose updates for nested model links are not yet supported. " << "The Align and Relative Pose widgets will be disabled for now." << std::endl; } this->CheckValid(); } ///////////////////////////////////////////////// bool JointCreationDialog::CheckValid() { bool valid = this->dataPtr->validLinks && this->dataPtr->validAxis1 && this->dataPtr->validAxis2; if (this->dataPtr->createButton) this->dataPtr->createButton->setEnabled(valid); return valid; } ///////////////////////////////////////////////// void JointCreationDialog::OnAlign(const int _int) { // Button groups std::vector axes = {"x", "y", "z"}; std::vector configs = {"min", "center", "max"}; // Find out which axis was changed QButtonGroup *group = NULL; unsigned int g; QObject *senderObject = this->sender(); if (senderObject == this->dataPtr->reverseXBox) { g = 0; group = this->dataPtr->alignGroups[g]; } else if (senderObject == this->dataPtr->reverseYBox) { g = 1; group = this->dataPtr->alignGroups[g]; } else if (senderObject == this->dataPtr->reverseZBox) { g = 2; group = this->dataPtr->alignGroups[g]; } else { for (g = 0; g < this->dataPtr->alignGroups.size(); ++g) { group = this->dataPtr->alignGroups[g]; if (senderObject == group) { break; } } } // When changing target, reset everything if (senderObject == this->dataPtr->alignCombo) { this->dataPtr->jointMaker->SetLinksRelativePose( ignition::math::Pose3d::Zero, true); } // Reset only the axis which was changed else { this->dataPtr->jointMaker->SetLinksRelativePose( ignition::math::Pose3d::Zero, false, g); } // Uncheck other buttons in the same group if (group && qobject_cast(senderObject)) { for (int i = 0; i < group->buttons().size(); ++i) { if (i != _int) { group->buttons()[i]->setChecked(false); } } } // Reference link bool childToParent = this->dataPtr->alignCombo->currentIndex() == 0; // Direction std::vector reverse; reverse.push_back(this->dataPtr->reverseXBox->isChecked()); reverse.push_back(this->dataPtr->reverseYBox->isChecked()); reverse.push_back(this->dataPtr->reverseZBox->isChecked()); // Go through all groups and align for (g = 0; g < this->dataPtr->alignGroups.size(); ++g) { group = this->dataPtr->alignGroups[g]; // Align if there is a checked button int checked = group->checkedId(); if (checked >= 0 && checked <=2) { // Align this->dataPtr->jointMaker->AlignLinks(childToParent, axes[g], configs[checked], reverse[g]); } } this->dataPtr->alignPending = true; } ///////////////////////////////////////////////// void JointCreationDialog::UncheckAllAlign(const bool _blockSignals) { for (auto group : this->dataPtr->alignGroups) { for (auto button : group->buttons()) { bool blocked = button->signalsBlocked(); button->blockSignals(_blockSignals); button->setChecked(false); button->blockSignals(blocked); } } bool blocked = this->dataPtr->reverseXBox->signalsBlocked(); this->dataPtr->reverseXBox->blockSignals(_blockSignals); this->dataPtr->reverseXBox->setChecked(false); this->dataPtr->reverseXBox->blockSignals(blocked); blocked = this->dataPtr->reverseYBox->signalsBlocked(); this->dataPtr->reverseYBox->blockSignals(_blockSignals); this->dataPtr->reverseYBox->setChecked(false); this->dataPtr->reverseYBox->blockSignals(blocked); blocked = this->dataPtr->reverseZBox->signalsBlocked(); this->dataPtr->reverseZBox->blockSignals(_blockSignals); this->dataPtr->reverseZBox->setChecked(false); this->dataPtr->reverseZBox->blockSignals(blocked); } ///////////////////////////////////////////////// void JointCreationDialog::OnType(const int _typeInt) { auto type = static_cast(_typeInt); unsigned int axisCount = JointMaker::JointAxisCount(type); // Display correct number of axes for this type this->dataPtr->axis0Widget->setVisible(axisCount == 0); this->dataPtr->axis1Widget->setVisible(axisCount > 0); this->dataPtr->axis2Widget->setVisible(axisCount > 1); // Change parent icon color according to type common::Color matAmbient, matDiffuse, matSpecular, matEmissive; rendering::Material::GetMaterialAsColor( this->dataPtr->jointMaker->jointMaterials[type], matAmbient, matDiffuse, matSpecular, matEmissive); std::ostringstream sheet; sheet << "QLabel{background-color: rgb(" << (matAmbient[0] * 255) << ", " << (matAmbient[1] * 255) << ", " << (matAmbient[2] * 255) << "); }"; this->dataPtr->parentIcon->setStyleSheet(QString::fromStdString(sheet.str())); } ///////////////////////////////////////////////// void JointCreationDialog::keyPressEvent(QKeyEvent *_event) { if (_event->key() == Qt::Key_Enter) _event->accept(); else QDialog::keyPressEvent(_event); }