NJointControllerClassesWidget.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * \package RobotAPI::gui-plugins::RobotUnitPlugin
17 * \author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * \date 2017
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <filesystem>
26
27#include <QDir>
28#include <QGridLayout>
29#include <QSortFilterProxyModel>
30
34
35namespace armarx
36{
38 RobotUnitWidgetTemplateBase("NJointControllerClassesWidget", parent)
39 {
40 connect(ui->pushButtonLoadLib, SIGNAL(released()), this, SLOT(loadLibClicked()));
41 connect(ui->comboBoxPackage,
42 SIGNAL(currentIndexChanged(QString)),
43 this,
44 SLOT(packageEditChanged()));
45 connect(ui->comboBoxPackage,
46 SIGNAL(editTextChanged(QString)),
47 this,
48 SLOT(packageEditChanged()));
49 ui->comboBoxPackage->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
50 ui->comboBoxPackage->setFixedWidth(300);
51
52 //get package hints
53 {
54 using namespace std::filesystem;
55 std::string homeDir = QDir::homePath().toStdString();
56 path p = path{homeDir} / ".cmake" / "packages";
57 if (is_directory(p))
58 {
59 for (const path& entry : directory_iterator(p))
60 {
61 const std::string pkg = entry.filename().string();
62 if (CMakePackageFinder{pkg, "", true}.packageFound())
63 {
64 ui->comboBoxPackage->addItem(QString::fromStdString(pkg));
65 }
66 }
67 // for sorting you need the following 4 lines
68 QSortFilterProxyModel* proxy = new QSortFilterProxyModel(ui->comboBoxPackage);
69 proxy->setSourceModel(ui->comboBoxPackage->model());
70 // combo's current model must be reparented,
71 // otherwise QComboBox::setModel() will delete it
72 ui->comboBoxPackage->model()->setParent(proxy);
73 ui->comboBoxPackage->setModel(proxy);
74 ui->comboBoxPackage->model()->sort(0);
75 }
76 }
77
78 ui->comboBoxPackage->setEditText("");
79 packageEditChanged();
80
81 ui->treeWidget->setColumnCount(2);
82
83 QTreeWidgetItem* head = ui->treeWidget->headerItem();
84 head->setText(0, "Class");
85 head->setText(1, "");
86 head->setToolTip(0, "Controller class name");
87 ui->treeWidget->header()->setResizeMode(0, QHeaderView::Fixed);
88 ui->treeWidget->header()->setResizeMode(1, QHeaderView::Fixed);
89 }
90
95
96 void
98 {
100 std::unique_lock<std::recursive_timed_mutex> guard{mutex};
101 if (!robotUnit)
102 {
103 return;
104 }
105 ru = robotUnit;
106 guard.unlock();
107 auto data = ru->getNJointControllerClassDescription(name);
108 guard.lock();
109 nJointControllerClassDescriptions[data.className] = std::move(data);
110 if (doMetaCall)
111 {
112 QMetaObject::invokeMethod(this, "updateContent", Qt::QueuedConnection);
113 }
114 }
115
116 void
118 bool force)
119 {
120 const auto oldName = getDefaultName();
121 if (oldName == createdName || force)
122 {
123 ++defaultControllerName;
124 const auto newName = getDefaultName();
125 for (auto& pair : entries)
126 {
127 pair.second->updateDefaultName(oldName, newName);
128 }
129 }
130 }
131
132 QString
134 {
135 return QString::number(defaultControllerName);
136 }
137
138 void
140 {
141 ui->lineEditLibrary->setText(settings->value("classLoadLineEditLib", "").toString());
142 ui->comboBoxPackage->lineEdit()->setText(
143 settings->value("classLoadComboBoxPkg", "").toString());
144 ui->comboBoxLibrary->lineEdit()->setText(
145 settings->value("classLoadComboBoxLib", "").toString());
146 }
147
148 void
150 {
151 settings->setValue("classLoadLineEditLib", ui->lineEditLibrary->text());
152 settings->setValue("classLoadComboBoxPkg", ui->comboBoxPackage->currentText());
153 settings->setValue("classLoadComboBoxLib", ui->comboBoxLibrary->currentText());
154 }
155
156 void
158 {
159 entries.clear();
160 nJointControllerClassDescriptions.clear();
161 }
162
163 void
165 {
166 for (const auto& pair : nJointControllerClassDescriptions)
167 {
168 add(pair.second);
169 }
170 nJointControllerClassDescriptions.clear();
171 }
172
173 void
175 {
176 auto temp = robotUnit->getNJointControllerClassDescriptions();
177 {
178 std::unique_lock<std::recursive_timed_mutex> guard{mutex};
179 for (NJointControllerClassDescription& ds : temp)
180 {
181 nJointControllerClassDescriptions[ds.className] = std::move(ds);
182 }
183 }
184 }
185
186 bool
188 {
189 if (nJointControllerClassDescriptions.empty())
190 {
191 return true;
192 }
193 add(nJointControllerClassDescriptions.begin()->second);
194 nJointControllerClassDescriptions.erase(nJointControllerClassDescriptions.begin());
195 return false;
196 }
197
198 void
199 NJointControllerClassesWidget::filterUpdated()
200 {
201 for (auto& entry : entries)
202 {
203 NJointControllerClassesWidgetEntry* item = entry.second;
204 if (!filterNameActive->isChecked() && !filterRemoteCreationActive->isChecked())
205 {
206 item->setVisible(true);
207 return;
208 }
209 const bool combineOr = (filterCombination->currentText() == "Or");
210 bool showName = !combineOr; //init to neutral element
211 bool showRemote = !combineOr; //init to neutral element
212
213 if (filterNameActive->isChecked())
214 {
215 showName = (item->matchName(filterName->text()) != filterNameInverted->isChecked());
216 }
217 if (filterRemoteCreationActive->isChecked())
218 {
219 if (item->hasRemoteCreation())
220 {
221 showRemote = (filterRemoteCreation->currentText() != "Without");
222 }
223 else
224 {
225 showRemote = (filterRemoteCreation->currentText() != "With");
226 }
227 }
228 if (combineOr)
229 {
230 item->setVisible(showName || showRemote);
231 }
232 else
233 {
234 item->setVisible(showName && showRemote);
235 }
236 }
237 }
238
239 void
240 NJointControllerClassesWidget::add(const NJointControllerClassDescription& desc)
241 {
242 std::unique_lock<std::recursive_timed_mutex> guard{mutex};
243 if (entries.count(desc.className))
244 {
245 return;
246 }
247 entries[desc.className] =
248 new NJointControllerClassesWidgetEntry(*this, *(ui->treeWidget), desc, robotUnit);
249 }
250
251 void
252 NJointControllerClassesWidget::addFilter()
253 {
254 QTreeWidgetItem* filterHeader = new QTreeWidgetItem;
255 ui->treeWidget->addTopLevelItem(filterHeader);
256 filterHeader->setText(0, "Filter");
257
258 QTreeWidgetItem* filter = new QTreeWidgetItem;
259 filterHeader->addChild(filter);
260 filter->setFirstColumnSpanned(true);
261 QWidget* w = new QWidget;
262 QGridLayout* l = new QGridLayout;
263 w->setLayout(l);
264 l->setContentsMargins(0, 0, 0, 0);
265 l->addItem(new QSpacerItem{0, 0, QSizePolicy::MinimumExpanding}, 0, 3);
266 l->addWidget(new QLabel{"Combine filters with"}, 0, 0, 1, 1);
267 filterCombination = new QComboBox;
268 filterCombination->addItem("Or");
269 filterCombination->addItem("And");
270 l->addWidget(filterCombination, 0, 1, 1, 1);
271
272 filterNameActive = new QCheckBox;
273 filterNameActive->setText("Filter by name");
274 l->addWidget(filterNameActive, 1, 0, 1, 1);
275
276 filterName = new QLineEdit;
277 l->addWidget(filterName, 1, 1, 1, 1);
278
279 filterNameInverted = new QCheckBox;
280 filterNameInverted->setText("Invert filter");
281 l->addWidget(filterNameInverted, 1, 2, 1, 1);
282 connect(filterNameActive, SIGNAL(toggled(bool)), filterName, SLOT(setEnabled(bool)));
283 connect(
284 filterNameActive, SIGNAL(toggled(bool)), filterNameInverted, SLOT(setEnabled(bool)));
285 filterName->setEnabled(false);
286 filterNameInverted->setEnabled(false);
287
288 filterRemoteCreationActive = new QCheckBox;
289 filterRemoteCreationActive->setText("Filter by remote creation capabilities");
290 l->addWidget(filterRemoteCreationActive, 2, 0, 1, 1);
291 filterRemoteCreation = new QComboBox;
292 filterRemoteCreation->addItem("Both");
293 filterRemoteCreation->addItem("With");
294 filterRemoteCreation->addItem("Without");
295 l->addWidget(filterRemoteCreation, 2, 1, 1, 1);
296 connect(filterRemoteCreationActive,
297 SIGNAL(toggled(bool)),
298 filterRemoteCreation,
299 SLOT(setEnabled(bool)));
300 filterRemoteCreationActive->setEnabled(false);
301
302 connect(
303 filterCombination, SIGNAL(currentIndexChanged(QString)), this, SLOT(filterUpdated()));
304 connect(filterNameActive, SIGNAL(clicked(bool)), this, SLOT(filterUpdated()));
305 connect(filterRemoteCreationActive, SIGNAL(clicked(bool)), this, SLOT(filterUpdated()));
306 connect(filterNameInverted, SIGNAL(clicked(bool)), this, SLOT(filterUpdated()));
307 connect(filterName, SIGNAL(textChanged(QString)), this, SLOT(filterUpdated()));
308 connect(
309 filterRemoteCreation, SIGNAL(currentIndexChanged(int)), this, SLOT(filterUpdated()));
310
311 filterNameActive->setChecked(false);
312 filterRemoteCreationActive->setChecked(false);
313
314 ui->treeWidget->setItemWidget(filter, 0, w);
315 }
316
319 QTreeWidget& treeWidget,
320 const NJointControllerClassDescription& desc,
322 QObject{&parent},
323 className{desc.className},
324 classNameQStr{QString::fromStdString(className)},
326 parent{&parent}
327 {
328 header = new QTreeWidgetItem{{QString::fromStdString(desc.className)}};
329 treeWidget.addTopLevelItem(header);
330 treeWidget.resizeColumnToContents(0);
331 QWidget* headerW = new QWidget;
332 headerW->setLayout(new QHBoxLayout);
333 headerW->layout()->setContentsMargins(0, 0, 0, 0);
334 headerW->layout()->addItem(new QSpacerItem{0, 0, QSizePolicy::MinimumExpanding});
335 if (desc.configDescription)
336 {
337 //add textfiel + button
338 {
339 nameEdit = new QLineEdit;
340 nameEdit->setText(parent.getDefaultName());
341 nameEdit->setToolTip("The instance name for the created controller instance");
342 headerW->layout()->addWidget(new QLabel{"Controller name"});
343 headerW->layout()->addWidget(nameEdit);
344 QPushButton* button = new QPushButton{"Create"};
345 headerW->layout()->addWidget(button);
346 button->setToolTip("Create a new controller instance of this class");
347 connect(button,
348 &QPushButton::clicked,
349 this,
350 &NJointControllerClassesWidgetEntry::createCtrl);
351 connect(nameEdit,
352 &QLineEdit::returnPressed,
353 this,
354 &NJointControllerClassesWidgetEntry::createCtrl);
355 }
356 //descr
357 {
358 QTreeWidgetItem* child = new QTreeWidgetItem;
359 header->addChild(child);
360 child->setFirstColumnSpanned(true);
361 QWidget* compressWid = new QWidget;
362 QHBoxLayout* compressLay = new QHBoxLayout;
363 compressWid->setLayout(compressLay);
364 compressLay->setContentsMargins(0, 0, 0, 0);
365 creator = WidgetDescription::makeDescribedWidget(desc.configDescription);
366 creator->layout()->setContentsMargins(0, 0, 0, 0);
367 compressLay->addWidget(creator);
368 compressLay->addItem(new QSpacerItem{0, 0, QSizePolicy::MinimumExpanding});
369 treeWidget.setItemWidget(child, 0, compressWid);
370 }
371 }
372 else
373 {
374 //dummy to force the same height for all lines
375 QLineEdit* dummy = new QLineEdit;
376 dummy->setFixedWidth(0);
377 dummy->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
378 headerW->layout()->addWidget(dummy);
379
380 headerW->layout()->addWidget(new QLabel{"No remote creation allowed."});
381 }
382 treeWidget.setItemWidget(header, 1, headerW);
383 }
384
385 bool
387 {
388 return classNameQStr.contains(name, Qt::CaseInsensitive);
389 }
390
391 bool
396
397 void
399 const QString& newName)
400 {
401 if (nameEdit && nameEdit->text() == oldName)
402 {
403 nameEdit->setText(newName);
404 }
405 }
406
407 void
409 {
410 header->setHidden(!vis);
411 }
412
413 void
414 NJointControllerClassesWidgetEntry::createCtrl()
415 {
416 const auto instanceName = nameEdit->text().toStdString();
417 const auto variants = creator->getVariants();
418 if (variants.empty())
419 {
420 ARMARX_INFO << "creating " << instanceName << " of class " << className
421 << " with no parameters\n";
422 }
423 else
424 {
425 std::stringstream ss;
426 ss << "creating " << instanceName << " of class " << className << " with parameters:\n";
427 for (const auto& pair : variants)
428 {
429 if (pair.second)
430 {
431
432 if (pair.second->data)
433 {
434 ss << " '" << pair.first << "' of type " << pair.second->data->ice_id()
435 << "\n";
436 }
437 else
438 {
439 ss << " '" << pair.first << "' nullptr data \n";
440 }
441 }
442 else
443 {
444 ss << " '" << pair.first << "' nullptr\n";
445 }
446 }
447 ARMARX_INFO << ss.str();
448 }
449 robotUnit->createNJointControllerFromVariantConfig(className, instanceName, variants);
450 parent->updateDefaultNameOnControllerCreated(nameEdit->text());
451 }
452
453 void
454 NJointControllerClassesWidget::packageEditChanged()
455 {
456 auto package = ui->comboBoxPackage->currentText();
457 if (package.isEmpty())
458 {
459 selectLibMode = SelectLibsMode::LineEdit;
460 ui->lineEditLibrary->setVisible(true);
461 ui->comboBoxLibrary->setVisible(false);
462 ui->pushButtonLoadLib->setEnabled(true);
463 ui->labelPackageFound->setPixmap(QPixmap(":/icons/Blank.svg").scaled(16, 16));
464 ui->labelPackageFound->setToolTip("");
465 }
466 else
467 {
468 libShortNameToFileName.clear();
469 ui->comboBoxLibrary->clear();
470
471 selectLibMode = SelectLibsMode::ComboBox;
472 ui->lineEditLibrary->setVisible(false);
473 ui->comboBoxLibrary->setVisible(true);
474 CMakePackageFinder pFinder(package.toStdString());
475 if (pFinder.packageFound())
476 {
477 ui->pushButtonLoadLib->setEnabled(true);
478 ui->labelPackageFound->setPixmap(QPixmap(":/icons/user-online.svg").scaled(16, 16));
479 ui->labelPackageFound->setToolTip("Found Package");
480 int libidx = -1;
481 for (const std::string& lib : Split(pFinder.getLibs(), ",; ", true))
482 {
483 if (lib.empty())
484 {
485 return;
486 }
487 const auto libPrefix = lib.find("lib");
488 const auto libSubstrStart = libPrefix == lib.npos ? 0 : libPrefix + 3;
489 const auto libSuffix = lib.find(".");
490 const auto libSubstrEnd = libSuffix - libSubstrStart;
491 std::string shortName = lib.substr(libSubstrStart, libSubstrEnd);
492 libShortNameToFileName[shortName] = lib;
493 ui->comboBoxLibrary->addItem(QString::fromStdString(shortName));
494 if (libidx == -1 &&
495 (lib.find("Controller") != lib.npos || lib.find("controller") != lib.npos))
496 {
497 libidx = libShortNameToFileName.size() - 1;
498 }
499 ui->comboBoxLibrary->setCurrentIndex(libidx);
500 }
501 }
502 else
503 {
504 ui->pushButtonLoadLib->setEnabled(false);
505 ui->labelPackageFound->setPixmap(
506 QPixmap(":/icons/dialog-cancel-5.svg").scaled(16, 16));
507 ui->labelPackageFound->setToolTip("Cannot find Package");
508 }
509 }
510 }
511
512 void
513 NJointControllerClassesWidget::loadLibClicked()
514 {
515 ARMARX_WARNING << "The functionality of dynamically loading libraries during runtime is no "
516 "longer supported. Use"
517 "the component properties to load additional libraries.";
518 return;
519 // if (!robotUnit)
520 // {
521 // return;
522 // }
523
524 // switch (selectLibMode)
525 // {
526 // case SelectLibsMode::LineEdit:
527 // {
528 // auto lib = ui->lineEditLibrary->text().toStdString();
529 // if (lib.empty())
530 // {
531 // return;
532 // }
533 //// ARMARX_INFO << "requesting to load lib " << lib
534 //// << " -> " << robotUnit->loadLibFromPath(lib);
535 // }
536 // break;
537 //
538 // case SelectLibsMode::ComboBox:
539 // {
540 // auto package = ui->comboBoxPackage->currentText().toStdString();
541 // auto lib = ui->comboBoxLibrary->currentText().toStdString();
542 // if (lib.empty())
543 // {
544 // return;
545 // }
546 // if (libShortNameToFileName.count(lib))
547 // {
548 // lib = libShortNameToFileName.at(lib);
549 // }
550 //// ARMARX_INFO << "requesting to load lib " << lib << " from package " << package
551 //// << " -> " << robotUnit->loadLibFromPackage(package, lib);
552 // }
553 // break;
554 // default:
555 // ARMARX_WARNING << "Load lib for given SelectLibsMode nyi";
556 // break;
557 // }
558 }
559
560} // namespace armarx
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
bool packageFound() const
Returns whether or not this package was found with cmake.
WidgetDescription::DescribedWidgetBase * creator
void updateDefaultName(const QString &oldName, const QString &newName)
NJointControllerClassesWidgetEntry(NJointControllerClassesWidget &parent, QTreeWidget &treeWidget, const NJointControllerClassDescription &desc, RobotUnitInterfacePrx robotUnit)
virtual void updateDefaultNameOnControllerCreated(QString createdName, bool force=false)
virtual void nJointControllerClassAdded(std::string name)
RobotUnitInterfacePrx robotUnit
std::recursive_timed_mutex mutex
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
::IceInternal::ProxyHandle<::IceProxy::armarx::RobotUnitInterface > RobotUnitInterfacePrx