ObjectPoseGuiWidgetController.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::ObjectPoseGuiWidgetController
17 * \author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
18 * \date 2020
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <string>
26
27#include <QMenu>
28#include <QTimer>
29
31
35
36namespace armarx
37{
38
39 static const int OBJECTS_COLUMN_ATTACHMENT = 6;
40
42 {
43 widget.setupUi(getWidget());
44
45 widget.objectsTree->clear();
46 widget.objectsTree->sortItems(0, Qt::SortOrder::AscendingOrder);
47 widget.objectsTree->setContextMenuPolicy(Qt::CustomContextMenu);
48
49 widget.requestTree->clear();
50 widget.requestTree->sortItems(0, Qt::SortOrder::AscendingOrder);
51
52
54
55 connect(widget.updateButton, &QPushButton::pressed, this, &This::updateTab);
56 connect(widget.tabWidget, &QTabWidget::currentChanged, this, &This::updateTab);
57 connect(widget.detchAllButton,
58 &QPushButton::pressed,
59 [this]() {
60 this->detachAllObjectsFromRobotNodes(
61 widget.detchAllCommitAttachedCheckBox->isChecked());
62 });
63
64 connect(widget.objectsTree,
65 &QTreeWidget::customContextMenuRequested,
66 this,
67 &This::prepareObjectContextMenu);
68
69 connect(widget.requestButton, &QPushButton::pressed, this, &This::requestSelectedObjects);
70
71 QTimer* timer = new QTimer(this);
72 timer->setInterval(500);
73 connect(timer, &QTimer::timeout, this, &This::updateTab);
74 connect(widget.autoUpdateCheckBox,
75 &QCheckBox::toggled,
76 this,
77 [timer](bool checked)
78 {
79 if (checked)
80 {
81 timer->start();
82 }
83 else
84 {
85 timer->stop();
86 }
87 });
88 }
89
93
94 void
96 {
97 (void)settings;
98 }
99
100 void
102 {
103 (void)settings;
104 }
105
106 QString
108 {
109 return "ArMem.ObjectPoseGui";
110 }
111
112 static const std::string CONFIG_KEY_OBJECT_POSE_STORAGE = "ObjectPoseStorage";
113
114 QPointer<QDialog>
116 {
117 if (!configDialog)
118 {
119 configDialog = new SimpleConfigDialog(parent);
120 configDialog->addProxyFinder<armarx::objpose::ObjectPoseStorageInterfacePrx>(
121 {CONFIG_KEY_OBJECT_POSE_STORAGE, "Object pose storage", "Object*"});
122 }
123 return qobject_cast<QDialog*>(configDialog);
124 }
125
126 void
128 {
129 if (configDialog)
130 {
131 objectPoseStorageName = configDialog->getProxyName(CONFIG_KEY_OBJECT_POSE_STORAGE);
132 if (objectPoseStorageName.empty())
133 {
134 objectPoseStorageName = "ObjectMemory";
135 }
136 }
137 }
138
139 void
141 {
142 if (!objectPoseStorageName.empty())
143 {
144 usingProxy(objectPoseStorageName);
145 }
146 }
147
148 void
150 {
151 if (!objectPoseStorageName.empty())
152 {
153 getProxy(ObjectPoseStorage, objectPoseStorageName);
154 }
155
156 this->attachableFrames = ObjectPoseStorage->getAttachableFrames();
157 std::sort(attachableFrames.begin(),
158 attachableFrames.end(),
159 [](const auto& lhs, const auto& rhs) { return lhs.agent < rhs.agent; });
160 for (objpose::AgentFrames& frames : attachableFrames)
161 {
162 std::sort(frames.frames.begin(), frames.frames.end());
163 }
164
165 // Update once.
166 updateTab();
167 }
168
169 void
171 {
172 ObjectPoseStorage = nullptr;
173 }
174
175 void
177 {
178 if (widget.tabWidget->currentWidget() == widget.tabObjects)
179 {
181 }
182 else if (widget.tabWidget->currentWidget() == widget.tabRequest)
183 {
185 }
186 }
187
188 void
190 {
191 if (!ObjectPoseStorage)
192 {
193 // Probably disconnected.
194 ARMARX_VERBOSE << "No object pose storage.";
195 return;
196 }
197
198 IceUtil::Time start = IceUtil::Time::now();
199 ARMARX_VERBOSE << "Getting object poses...";
200 const objpose::data::ObjectPoseSeq objectPosesIce = ObjectPoseStorage->getObjectPoses();
201 ARMARX_VERBOSE << "Got " << objectPosesIce.size() << " object poses. "
202 << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble()
203 << " ms.)";
204
205 const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPosesIce);
206
207 std::map<std::string, objpose::ObjectPoseSeq> objectPosesByProvider;
208 for (const auto& pose : objectPoses)
209 {
210 objectPosesByProvider[pose.providerName].push_back(pose);
211 }
212
213 start = IceUtil::Time::now();
214
215 QTreeWidget* tree = widget.objectsTree;
216
217 MapTreeWidgetBuilder builder(objectPosesByProvider);
218 builder.setMakeItemFn(
219 [](const std::string& provider, const objpose::ObjectPoseSeq&)
220 {
221 QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(provider)});
222 return item;
223 });
224 builder.setUpdateItemFn(
225 [](const std::string&, const objpose::ObjectPoseSeq& objectPoses, QTreeWidgetItem* item)
226 {
227 bool expand = item->childCount() == 0;
228
230 builder.setNameFn([](const objpose::ObjectPose& pose)
231 { return pose.objectID.str(); });
232 builder.setMakeItemFn(
233 [](const objpose::ObjectPose&)
234 {
235 QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{});
236 return item;
237 });
238 builder.setUpdateItemFn(
239 [](const objpose::ObjectPose& pose, QTreeWidgetItem* item)
240 {
241 int col = 0;
242 item->setText(col++, QString::fromStdString(pose.objectID.str()));
243 item->setText(col++, QString::fromStdString(pose.providerName));
244 item->setText(col++,
245 QString::fromStdString(
246 objpose::ObjectTypeNames.to_name(pose.objectType)));
247
248 {
249 std::stringstream ss;
250 if (pose.localOOBB)
251 {
252 static const Eigen::IOFormat iof(5, 0, "", " x ", "", "", "", "");
253 ss << pose.localOOBB->dimensions().format(iof);
254 }
255 else
256 {
257 ss << "None";
258 }
259 item->setText(col++, QString::fromStdString(ss.str()));
260 }
261 item->setText(col++, QString::number(double(pose.confidence), 'g', 2));
262 item->setText(col++,
263 QString::fromStdString(pose.timestamp.toDateTimeString()));
264
265 {
266 std::stringstream ss;
267 if (pose.attachment)
268 {
269 ss << pose.attachment->frameName << " ("
270 << pose.attachment->agentName << ")";
271 }
272 item->setText(col++, QString::fromStdString(ss.str()));
273 }
274
275 return true;
276 });
277 builder.updateTreeWithContainer(item, objectPoses);
278
279 if (expand)
280 {
281 item->setExpanded(true);
282 }
283
284 return true;
285 });
286 builder.updateTree(tree, objectPosesByProvider);
287
288 ARMARX_VERBOSE << "Gui update took "
289 << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
290 }
291
292 void
294 {
295 if (!ObjectPoseStorage)
296 {
297 // Probably disconnected.
298 ARMARX_VERBOSE << "No object pose storage.";
299 return;
300 }
301
302 IceUtil::Time start = IceUtil::Time::now();
303 objpose::ProviderInfoMap availableProvidersInfo =
304 ObjectPoseStorage->getAvailableProvidersInfo();
305 ARMARX_VERBOSE << "Got infos of " << availableProvidersInfo.size()
306 << " object pose providers. "
307 << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble()
308 << " ms.)";
309
310
311 // Restructure data.
312 std::map<std::string, std::set<std::pair<std::string, std::string>>> data;
313 for (const auto& [providerName, info] : availableProvidersInfo)
314 {
315 for (const auto& id : info.supportedObjects)
316 {
317 data[id.dataset].insert(std::make_pair(id.className, providerName));
318 }
319 }
320
321 start = IceUtil::Time::now();
322
323 QTreeWidget* tree = widget.requestTree;
324
325 MapTreeWidgetBuilder builder(data);
326 builder.setMakeItemFn(
327 [](const std::string& dataset, const auto&)
328 {
329 QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(dataset)});
330 return item;
331 });
332 builder.setUpdateItemFn(
333 [tree](
334 const std::string& dataset, const auto& datasetData, QTreeWidgetItem* datasetItem)
335 {
336 (void)dataset;
337
339 builder.setCompareFn(
340 [](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem* item)
341 {
342 auto rhs = std::make_pair(item->text(0).toStdString(),
343 item->text(1).toStdString());
344 if (lhs < rhs)
345 {
346 return -1;
347 }
348 return lhs == rhs ? 0 : 1;
349 });
350 builder.setMakeItemFn(
351 [](const std::pair<std::string, std::string>& element)
352 {
353 QTreeWidgetItem* item =
354 new QTreeWidgetItem({QString::fromStdString(element.first),
355 QString::fromStdString(element.second)});
356 return item;
357 });
358 builder.setUpdateItemFn(
359 [tree](const std::pair<std::string, std::string>& element,
360 QTreeWidgetItem* item)
361 {
362 (void)element;
363 if (!tree->itemWidget(item, 2))
364 {
365 QCheckBox* requestCheckBox = new QCheckBox();
366 tree->setItemWidget(item, 2, requestCheckBox);
367 }
368 return true;
369 });
370 builder.updateTreeWithContainer(datasetItem, datasetData);
371
372 return true;
373 });
374 builder.updateTree(tree, data);
375
376 ARMARX_VERBOSE << "Gui update took "
377 << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
378 }
379
380 void
382 {
383 QTreeWidget* tree = widget.objectsTree;
384 QTreeWidgetItem* item = tree->itemAt(pos);
385
386 if (item == nullptr || item->parent() == nullptr)
387 {
388 // No item or top level item => no context menu.
389 return;
390 }
391
392 QString providerName = item->parent()->text(0);
393 QString objectID = item->text(0);
394
395 QMenu* attachMenu = new QMenu("Attach to robot node", tree);
396 for (const objpose::AgentFrames& agentFrames : attachableFrames)
397 {
398 QMenu* agentMenu = new QMenu(QString::fromStdString(agentFrames.agent), tree);
399
400 for (const std::string& frame : agentFrames.frames)
401 {
402 QAction* attachAgentAction = new QAction(QString::fromStdString(frame), tree);
403 // attachAgentAction->setStatusTip(tr("Attach object rigidly to a robot node"));
404 connect(attachAgentAction,
405 &QAction::triggered,
406 [=, this]() {
408 providerName, objectID, agentFrames.agent, frame);
409 });
410 agentMenu->addAction(attachAgentAction);
411 }
412 attachMenu->addMenu(agentMenu);
413 }
414
415 QAction* detachAction = new QAction(tr("Detach from to robot node"), tree);
416 detachAction->setEnabled(!item->text(OBJECTS_COLUMN_ATTACHMENT).isEmpty());
417 connect(detachAction,
418 &QAction::triggered,
419 [=, this]() { this->detachObjectFromRobotNode(providerName, objectID); });
420
421 QMenu menu(tree);
422 menu.addMenu(attachMenu);
423 menu.addAction(detachAction);
424
425 menu.exec(tree->mapToGlobal(pos));
426 }
427
428 void
430 QString objectID,
431 const std::string& agentName,
432 const std::string& frameName)
433 {
434 ARMARX_VERBOSE << "Attaching " << objectID << " by '" << providerName << "' to robot node '"
435 << frameName << "' of agent '" << agentName << "'.";
436
437 objpose::AttachObjectToRobotNodeInput input;
438 input.providerName = providerName.toStdString();
439 input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
440 input.agentName = agentName;
441 input.frameName = frameName;
442
443 try
444 {
445 objpose::AttachObjectToRobotNodeOutput output =
446 ObjectPoseStorage->attachObjectToRobotNode(input);
447 ARMARX_VERBOSE << "Success of attaching: " << output.success;
448 }
449 catch (const IceUtil::Exception& e)
450 {
451 ARMARX_WARNING << "Failed to attach object '" << input.objectID << "' to robot node '"
452 << input.frameName << "' of agent '" << input.agentName << "'."
453 << "\nReason: " << e.what();
454 }
455 }
456
457 void
458 ObjectPoseGuiWidgetController::detachObjectFromRobotNode(QString providerName, QString objectID)
459 {
460 ARMARX_VERBOSE << "Detaching " << objectID << " by '" << providerName
461 << "' from robot node.";
462
463 objpose::DetachObjectFromRobotNodeInput input;
464 input.providerName = providerName.toStdString();
465 input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
466
467 try
468 {
469 objpose::DetachObjectFromRobotNodeOutput output =
470 ObjectPoseStorage->detachObjectFromRobotNode(input);
471 ARMARX_VERBOSE << "Was attached: " << output.wasAttached;
472 }
473 catch (const IceUtil::Exception& e)
474 {
475 ARMARX_WARNING << "Failed to detach object '" << input.objectID
476 << "' from a robot node."
477 << "\nReason: " << e.what();
478 }
479 }
480
481 void
483 {
484
485 objpose::DetachAllObjectsFromRobotNodesInput input;
486 input.commitAttachedPose = commitAttachedPose;
487
488 try
489 {
490 objpose::DetachAllObjectsFromRobotNodesOutput output =
491 ObjectPoseStorage->detachAllObjectsFromRobotNodes(input);
492 ARMARX_VERBOSE << "Detached " << output.numDetached << " objects from robot nodes.";
493 }
494 catch (const IceUtil::Exception& e)
495 {
496 ARMARX_WARNING << "Failed to detach all objects from robot nodes.";
497 }
498 }
499
500 void
502 {
503 std::map<std::string, objpose::observer::RequestObjectsInput> requestsPerProvider;
504
505 QTreeWidget* tree = widget.requestTree;
506 for (int i = 0; i < tree->topLevelItemCount(); ++i)
507 {
508 QTreeWidgetItem* datasetItem = tree->topLevelItem(i);
509 for (int j = 0; j < datasetItem->childCount(); ++j)
510 {
511 QTreeWidgetItem* classItem = datasetItem->child(j);
512 QCheckBox* selected = dynamic_cast<QCheckBox*>(tree->itemWidget(classItem, 2));
513 ARMARX_CHECK_NOT_NULL(selected);
514 if (selected->isChecked())
515 {
516 std::string providerName = classItem->text(1).toStdString();
517 objpose::observer::RequestObjectsInput& requests =
518 requestsPerProvider[providerName];
519 data::ObjectID& id = requests.request.objectIDs.emplace_back();
520 id.dataset = datasetItem->text(0).toStdString();
521 id.className = classItem->text(0).toStdString();
522 }
523 }
524 }
525
526 long timeoutMS = -1;
527 if (!widget.requestInfiniteCheckBox->isChecked())
528 {
529 timeoutMS = long(widget.requestTimeoutSpinBox->value() * 1000);
530 }
531
532 for (auto& [providerName, request] : requestsPerProvider)
533 {
534 request.provider = providerName;
535 request.request.relativeTimeoutMS = timeoutMS;
536
537 ARMARX_INFO << "Requesting " << request.request.objectIDs.size() << " objects for "
538 << request.request.relativeTimeoutMS << " ms.";
539 objpose::observer::RequestObjectsOutput output =
540 ObjectPoseStorage->requestObjects(request);
541 int successful = 0;
542 for (const auto& [id, result] : output.results)
543 {
544 successful += int(!result.providerName.empty() && result.result.success);
545 }
546 ARMARX_INFO << successful << " of " << request.request.objectIDs.size()
547 << " object request successful.";
548 }
549 }
550
551} // namespace armarx
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition ObjectID.h:11
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition ObjectID.cpp:60
void updateTab()
Update the currently opened tab.
void loadSettings(QSettings *settings) override
static QString GetWidgetName()
Returns the Widget name displayed in the ArmarXGui to create an instance of this class.
void detachObjectFromRobotNode(QString providerName, QString objectID)
void detachAllObjectsFromRobotNodes(bool commitAttachedPose)
void attachObjectToRobotNode(QString providerName, QString objectID, const std::string &agentName, const std::string &frameName)
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
virtual ~ObjectPoseGuiWidgetController() override
Controller destructor.
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
A config-dialog containing one (or multiple) proxy finders.
std::string toDateTimeString() const
Definition DateTime.cpp:75
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#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
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
std::vector< ObjectPose > ObjectPoseSeq
void fromIce(const Box &box, simox::OrientedBox< float > &oobb)
const simox::meta::EnumNames< objpose::ObjectType > ObjectTypeNames
This file offers overloads of toIce() and fromIce() functions for STL container types.
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a m...
void setUpdateItemFn(UpdateItemFn updateItemFn)
void updateTree(ParentT *tree, const MapT &elements)
void setMakeItemFn(MakeItemFn makeItemFn)
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a s...
void setNameFn(NameFn nameFn, int nameColumn=0)
void setUpdateItemFn(UpdateItemFn updateItemFn)
void setMakeItemFn(MakeItemFn makeItemFn)
void updateTreeWithContainer(ParentT *parent, const ContainerT &elements)
Update the tree with the iterable container.
void setCompareFn(CompareFn compareFn)
An object pose as stored by the ObjectPoseStorage.
Definition ObjectPose.h:34
float confidence
Confidence in [0, 1] (1 = full, 0 = none).
Definition ObjectPose.h:96
armarx::ObjectID objectID
The object ID, i.e. dataset, class name and instance name.
Definition ObjectPose.h:56
std::optional< ObjectAttachmentInfo > attachment
Attachment information.
Definition ObjectPose.h:93
std::string providerName
Name of the providing component.
Definition ObjectPose.h:59
DateTime timestamp
Source timestamp.
Definition ObjectPose.h:98
ObjectType objectType
Known or unknown object.
Definition ObjectPose.h:61
std::optional< simox::OrientedBoxf > localOOBB
Object bounding box in object's local coordinate frame.
Definition ObjectPose.h:102