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 <QTimer>
28 #include <QMenu>
29 
31 
34 
36 
37 
38 namespace armarx
39 {
40 
41  static const int OBJECTS_COLUMN_ATTACHMENT = 6;
42 
44  {
45  widget.setupUi(getWidget());
46 
47  widget.objectsTree->clear();
48  widget.objectsTree->sortItems(0, Qt::SortOrder::AscendingOrder);
49  widget.objectsTree->setContextMenuPolicy(Qt::CustomContextMenu);
50 
51  widget.requestTree->clear();
52  widget.requestTree->sortItems(0, Qt::SortOrder::AscendingOrder);
53 
54 
55  using This = ObjectPoseGuiWidgetController;
56 
57  connect(widget.updateButton, &QPushButton::pressed, this, &This::updateTab);
58  connect(widget.tabWidget, &QTabWidget::currentChanged, this, &This::updateTab);
59 
60  connect(widget.objectsTree, &QTreeWidget::customContextMenuRequested,
61  this, &This::prepareObjectContextMenu);
62 
63  connect(widget.requestButton, &QPushButton::pressed, this, &This::requestSelectedObjects);
64 
65  QTimer* timer = new QTimer(this);
66  timer->setInterval(500);
67  connect(timer, &QTimer::timeout, this, &This::updateTab);
68  connect(widget.autoUpdateCheckBox, &QCheckBox::toggled, this, [timer](bool checked)
69  {
70  if (checked)
71  {
72  timer->start();
73  }
74  else
75  {
76  timer->stop();
77  }
78  });
79  }
80 
81 
82  ObjectPoseGuiWidgetController::~ObjectPoseGuiWidgetController()
83  {
84  }
85 
86 
87  void ObjectPoseGuiWidgetController::loadSettings(QSettings* settings)
88  {
89  (void) settings;
90  }
91 
92  void ObjectPoseGuiWidgetController::saveSettings(QSettings* settings)
93  {
94  (void) settings;
95  }
96 
97  QString ObjectPoseGuiWidgetController::GetWidgetName()
98  {
99  return "MemoryX.ObjectPoseGui";
100  }
101 
102  static const std::string CONFIG_KEY_OBJECT_POSE_OBSERVER = "ObjectPoseStorage";
103 
104  QPointer<QDialog> ObjectPoseGuiWidgetController::getConfigDialog(QWidget* parent)
105  {
106  if (!configDialog)
107  {
108  configDialog = new SimpleConfigDialog(parent);
109  configDialog->addProxyFinder<armarx::objpose::ObjectPoseStorageInterfacePrx>({CONFIG_KEY_OBJECT_POSE_OBSERVER, "Object pose observer.", "*"});
110  }
111  return qobject_cast<QDialog*>(configDialog);
112  }
113 
114  void ObjectPoseGuiWidgetController::configured()
115  {
116  if (configDialog)
117  {
118  ObjectPoseStorageName = configDialog->getProxyName(CONFIG_KEY_OBJECT_POSE_OBSERVER);
119  }
120  }
121 
122  void ObjectPoseGuiWidgetController::onInitComponent()
123  {
124  if (!ObjectPoseStorageName.empty())
125  {
126  usingProxy(ObjectPoseStorageName);
127  }
128  }
129 
130  void ObjectPoseGuiWidgetController::onConnectComponent()
131  {
132  if (!ObjectPoseStorageName.empty())
133  {
134  getProxy(ObjectPoseStorage, ObjectPoseStorageName);
135  }
136 
137  this->attachableFrames = ObjectPoseStorage->getAttachableFrames();
138  std::sort(attachableFrames.begin(), attachableFrames.end(), [](const auto & lhs, const auto & rhs)
139  {
140  return lhs.agent < rhs.agent;
141  });
142  for (objpose::AgentFrames& frames : attachableFrames)
143  {
144  std::sort(frames.frames.begin(), frames.frames.end());
145  }
146  }
147 
148  void ObjectPoseGuiWidgetController::onDisconnectComponent()
149  {
150  ObjectPoseStorage = nullptr;
151  }
152 
153  void ObjectPoseGuiWidgetController::updateTab()
154  {
155  if (widget.tabWidget->currentWidget() == widget.tabObjects)
156  {
157  updateObjectsTab();
158  }
159  else if (widget.tabWidget->currentWidget() == widget.tabRequest)
160  {
161  updateRequestTab();
162  }
163  }
164 
165 
166 
167  void ObjectPoseGuiWidgetController::updateObjectsTab()
168  {
169  if (!ObjectPoseStorage)
170  {
171  // Probably disconnected.
172  ARMARX_VERBOSE << "No object pose observer.";
173  return;
174  }
175 
176  IceUtil::Time start = IceUtil::Time::now();
177  ARMARX_VERBOSE << "Getting object poses...";
178  const objpose::data::ObjectPoseSeq objectPosesIce = ObjectPoseStorage->getObjectPoses();
179  ARMARX_VERBOSE << "Got " << objectPosesIce.size() << " object poses. "
180  << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.)";
181 
182  const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPosesIce);
183 
184  std::map<std::string, objpose::ObjectPoseSeq> objectPosesByProvider;
185  for (const auto& pose : objectPoses)
186  {
187  objectPosesByProvider[pose.providerName].push_back(pose);
188  }
189 
190  start = IceUtil::Time::now();
191 
192  QTreeWidget* tree = widget.objectsTree;
193 
194  MapTreeWidgetBuilder builder(objectPosesByProvider);
195  builder.setMakeItemFn([](const std::string & provider, const objpose::ObjectPoseSeq&)
196  {
197  QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(provider)});
198  return item;
199  });
200  builder.setUpdateItemFn([](const std::string&, const objpose::ObjectPoseSeq & objectPoses, QTreeWidgetItem * item)
201  {
202  bool expand = item->childCount() == 0;
203 
205  builder.setNameFn([](const objpose::ObjectPose & pose)
206  {
207  return pose.objectID.str();
208  });
209  builder.setMakeItemFn([](const objpose::ObjectPose&)
210  {
211  QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{});
212  return item;
213  });
214  builder.setUpdateItemFn([](const objpose::ObjectPose & pose, QTreeWidgetItem * item)
215  {
216  int col = 0;
217  item->setText(col++, QString::fromStdString(pose.objectID.str()));
218  item->setText(col++, QString::fromStdString(pose.providerName));
219  item->setText(col++, QString::fromStdString(objpose::ObjectTypeNames.to_name(pose.objectType)));
220 
221  {
222  std::stringstream ss;
223  if (pose.localOOBB)
224  {
225  static const Eigen::IOFormat iof(5, 0, "", " x ", "", "", "", "");
226  ss << pose.localOOBB->dimensions().format(iof);
227  }
228  else
229  {
230  ss << "None";
231  }
232  item->setText(col++, QString::fromStdString(ss.str()));
233  }
234  item->setText(col++, QString::number(double(pose.confidence), 'g', 2));
235  item->setText(col++, QString::fromStdString(pose.timestamp.toDateTimeString()));
236 
237  {
238  std::stringstream ss;
239  if (pose.attachment)
240  {
241  ss << pose.attachment->frameName << " (" << pose.attachment->agentName << ")";
242  }
243  item->setText(col++, QString::fromStdString(ss.str()));
244  }
245 
246  return true;
247  });
248  builder.updateTreeWithContainer(item, objectPoses);
249 
250  if (expand)
251  {
252  item->setExpanded(true);
253  }
254 
255  return true;
256  });
257  builder.updateTree(tree, objectPosesByProvider);
258 
259  ARMARX_VERBOSE << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
260  }
261 
262 
263  void ObjectPoseGuiWidgetController::updateRequestTab()
264  {
265  if (!ObjectPoseStorage)
266  {
267  // Probably disconnected.
268  ARMARX_VERBOSE << "No object pose observer.";
269  return;
270  }
271 
272  IceUtil::Time start = IceUtil::Time::now();
273  objpose::ProviderInfoMap availableProvidersInfo = ObjectPoseStorage->getAvailableProvidersInfo();
274  ARMARX_VERBOSE << "Got infos of " << availableProvidersInfo.size() << " object pose providers. "
275  << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.)";
276 
277 
278  // Restructure data.
279  std::map<std::string, std::set<std::pair<std::string, std::string>>> data;
280  for (const auto& [providerName, info] : availableProvidersInfo)
281  {
282  for (const auto& id : info.supportedObjects)
283  {
284  data[id.dataset].insert(std::make_pair(id.className, providerName));
285  }
286  }
287 
288  start = IceUtil::Time::now();
289 
290  QTreeWidget* tree = widget.requestTree;
291 
292  MapTreeWidgetBuilder builder(data);
293  builder.setMakeItemFn([](const std::string & dataset, const auto&)
294  {
295  QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(dataset)});
296  return item;
297  });
298  builder.setUpdateItemFn([tree](const std::string & dataset, const auto & datasetData, QTreeWidgetItem * datasetItem)
299  {
300  (void) dataset;
301 
303  builder.setCompareFn([](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem * item)
304  {
305  auto rhs = std::make_pair(item->text(0).toStdString(), item->text(1).toStdString());
306  if (lhs < rhs)
307  {
308  return -1;
309  }
310  return lhs == rhs ? 0 : 1;
311  });
312  builder.setMakeItemFn([](const std::pair<std::string, std::string>& element)
313  {
314  QTreeWidgetItem* item = new QTreeWidgetItem({ QString::fromStdString(element.first), QString::fromStdString(element.second)});
315  return item;
316  });
317  builder.setUpdateItemFn([tree](const std::pair<std::string, std::string>& element, QTreeWidgetItem * item)
318  {
319  (void) element;
320  if (!tree->itemWidget(item, 2))
321  {
322  QCheckBox* requestCheckBox = new QCheckBox();
323  tree->setItemWidget(item, 2, requestCheckBox);
324  }
325  return true;
326  });
327  builder.updateTreeWithContainer(datasetItem, datasetData);
328 
329  return true;
330  });
331  builder.updateTree(tree, data);
332 
333  ARMARX_VERBOSE << "Gui update took " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
334  }
335 
336  void ObjectPoseGuiWidgetController::prepareObjectContextMenu(const QPoint& pos)
337  {
338  QTreeWidget* tree = widget.objectsTree;
339  QTreeWidgetItem* item = tree->itemAt(pos);
340 
341  if (item == nullptr || item->parent() == nullptr)
342  {
343  // No item or top level item => no context menu.
344  return;
345  }
346 
347  QString providerName = item->parent()->text(0);
348  QString objectID = item->text(0);
349 
350  QMenu* attachMenu = new QMenu("Attach to robot node", tree);
351  for (const objpose::AgentFrames& agentFrames : attachableFrames)
352  {
353  QMenu* agentMenu = new QMenu(QString::fromStdString(agentFrames.agent), tree);
354 
355  for (const std::string& frame : agentFrames.frames)
356  {
357  QAction* attachAgentAction = new QAction(QString::fromStdString(frame), tree);
358  // attachAgentAction->setStatusTip(tr("Attach object rigidly to a robot node"));
359  connect(attachAgentAction, &QAction::triggered,
360  [ =, this ]()
361  {
362  this->attachObjectToRobotNode(providerName, objectID, agentFrames.agent, frame);
363  });
364  agentMenu->addAction(attachAgentAction);
365  }
366  attachMenu->addMenu(agentMenu);
367  }
368 
369  QAction* detachAction = new QAction(tr("Detach from to robot node"), tree);
370  detachAction->setEnabled(!item->text(OBJECTS_COLUMN_ATTACHMENT).isEmpty());
371  connect(detachAction, &QAction::triggered, [ =, this ]()
372  {
373  this->detachObjectFromRobotNode(providerName, objectID);
374  });
375 
376  QMenu menu(tree);
377  menu.addMenu(attachMenu);
378  menu.addAction(detachAction);
379 
380  menu.exec(tree->mapToGlobal(pos));
381  }
382 
383  void ObjectPoseGuiWidgetController::attachObjectToRobotNode(
384  QString providerName, QString objectID,
385  const std::string& agentName, const std::string& frameName)
386  {
387  ARMARX_VERBOSE << "Attaching " << objectID << " by '" << providerName << "' to robot node '"
388  << frameName << "' of agent '" << agentName << "'.";
389 
390  objpose::AttachObjectToRobotNodeInput input;
391  input.providerName = providerName.toStdString();
392  input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
393  input.agentName = agentName;
394  input.frameName = frameName;
395 
396  try
397  {
398  objpose::AttachObjectToRobotNodeOutput output = ObjectPoseStorage->attachObjectToRobotNode(input);
399  ARMARX_VERBOSE << "Success of attaching: " << output.success;
400  }
401  catch (const IceUtil::Exception& e)
402  {
403  ARMARX_WARNING << "Failed to attach object '" << input.objectID << "' to robot node '"
404  << input.frameName << "' of agent '" << input.agentName << "'."
405  << "\nReason: " << e.what();
406  }
407  }
408 
409  void ObjectPoseGuiWidgetController::detachObjectFromRobotNode(QString providerName, QString objectID)
410  {
411  ARMARX_VERBOSE << "Detaching " << objectID << " by '" << providerName << "' from robot node.";
412 
413  objpose::DetachObjectFromRobotNodeInput input;
414  input.providerName = providerName.toStdString();
415  input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
416 
417  try
418  {
419  objpose::DetachObjectFromRobotNodeOutput output = ObjectPoseStorage->detachObjectFromRobotNode(input);
420  ARMARX_VERBOSE << "Was attached: " << output.wasAttached;
421  }
422  catch (const IceUtil::Exception& e)
423  {
424  ARMARX_WARNING << "Failed to detach object '" << input.objectID << "' from a robot node."
425  << "\nReason: " << e.what();
426  }
427  }
428 
429  void ObjectPoseGuiWidgetController::requestSelectedObjects()
430  {
431  std::map<std::string, objpose::observer::RequestObjectsInput> requestsPerProvider;
432 
433  QTreeWidget* tree = widget.requestTree;
434  for (int i = 0; i < tree->topLevelItemCount(); ++i)
435  {
436  QTreeWidgetItem* datasetItem = tree->topLevelItem(i);
437  for (int j = 0; j < datasetItem->childCount(); ++j)
438  {
439  QTreeWidgetItem* classItem = datasetItem->child(j);
440  QCheckBox* selected = dynamic_cast<QCheckBox*>(tree->itemWidget(classItem, 2));
441  ARMARX_CHECK_NOT_NULL(selected);
442  if (selected->isChecked())
443  {
444  std::string providerName = classItem->text(1).toStdString();
445  objpose::observer::RequestObjectsInput& requests = requestsPerProvider[providerName];
446  data::ObjectID& id = requests.request.objectIDs.emplace_back();
447  id.dataset = datasetItem->text(0).toStdString();
448  id.className = classItem->text(0).toStdString();
449  }
450  }
451  }
452 
453  long timeoutMS = -1;
454  if (!widget.requestInfiniteCheckBox->isChecked())
455  {
456  timeoutMS = long(widget.requestTimeoutSpinBox->value() * 1000);
457  }
458 
459  for (auto& [providerName, request] : requestsPerProvider)
460  {
461  request.provider = providerName;
462  request.request.relativeTimeoutMS = timeoutMS;
463 
464  ARMARX_INFO << "Requesting " << request.request.objectIDs.size() << " objects for "
465  << request.request.relativeTimeoutMS << " ms.";
466  objpose::observer::RequestObjectsOutput output = ObjectPoseStorage->requestObjects(request);
467  int successful = 0;
468  for (const auto& [id, result] : output.results)
469  {
470  successful += int(!result.providerName.empty() && result.result.success);
471  }
472  ARMARX_INFO << successful << " of " << request.request.objectIDs.size() << " object request successful.";
473  }
474  }
475 
476 }
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
ice_conversions.h
armarx::ObjectID
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition: ObjectID.h:11
armarx::objpose::ObjectPoseSeq
std::vector< ObjectPose > ObjectPoseSeq
Definition: forward_declarations.h:20
armarx::objpose::ObjectPose::objectType
ObjectType objectType
Known or unknown object.
Definition: ObjectPose.h:63
armarx::armem::attachment::ObjectID
armem::MemoryID ObjectID
Definition: types.h:37
armarx::MapTreeWidgetBuilder::setUpdateItemFn
void setUpdateItemFn(UpdateItemFn updateItemFn)
Definition: TreeWidgetBuilder.h:179
ARMARX_CHECK_NOT_NULL
#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...
Definition: ExpressionException.h:204
armarx::objpose::ObjectPose::localOOBB
std::optional< simox::OrientedBoxf > localOOBB
Object bounding box in object's local coordinate frame.
Definition: ObjectPose.h:104
armarx::MapTreeWidgetBuilder
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a m...
Definition: TreeWidgetBuilder.h:124
armarx::objpose::ObjectPose::providerName
std::string providerName
Name of the providing component.
Definition: ObjectPose.h:61
armarx::MapTreeWidgetBuilder::updateTree
void updateTree(ParentT *tree, const MapT &elements)
Definition: TreeWidgetBuilder.h:195
armarx::TreeWidgetBuilder::setNameFn
void setNameFn(NameFn nameFn, int nameColumn=0)
Definition: TreeWidgetBuilder.h:49
TreeWidgetBuilder.h
armarx::ObjectPoseGuiWidgetController::ObjectPoseGuiWidgetController
ObjectPoseGuiWidgetController()
Controller Constructor.
Definition: ObjectPoseGuiWidgetController.cpp:43
ObjectPoseGuiWidgetController.h
armarx::toIce
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
Definition: ice_conversions_boost_templates.h:15
armarx::TreeWidgetBuilder::updateTreeWithContainer
void updateTreeWithContainer(ParentT *parent, const ContainerT &elements)
Update the tree with the iterable container.
Definition: TreeWidgetBuilder.h:312
armarx::aron::input
ReaderT::InputType & input
Definition: rw.h:16
armarx::TreeWidgetBuilder
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a s...
Definition: DataTreeBuilderBase.h:10
ObjectPose.h
armarx::TreeWidgetBuilder::setUpdateItemFn
void setUpdateItemFn(UpdateItemFn updateItemFn)
Definition: TreeWidgetBuilder.h:57
armarx::TreeWidgetBuilder::setMakeItemFn
void setMakeItemFn(MakeItemFn makeItemFn)
Definition: TreeWidgetBuilder.h:53
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::objpose::ObjectTypeNames
const simox::meta::EnumNames< objpose::ObjectType > ObjectTypeNames
Definition: ice_conversions.cpp:87
armarx::fromIce
void fromIce(const std::map< IceKeyT, IceValueT > &iceMap, boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
Definition: ice_conversions_boost_templates.h:26
armarx::MapTreeWidgetBuilder::setMakeItemFn
void setMakeItemFn(MakeItemFn makeItemFn)
Definition: TreeWidgetBuilder.h:171
armarx::objpose::ObjectPose::objectID
armarx::ObjectID objectID
The object ID, i.e. dataset, class name and instance name.
Definition: ObjectPose.h:58
TimeUtil.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::objpose::ObjectPose::attachment
std::optional< ObjectAttachmentInfo > attachment
Attachment information.
Definition: ObjectPose.h:95
armarx::ArmarXWidgetController::getWidget
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
Definition: ArmarXWidgetController.cpp:54
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::objpose::ObjectPose::confidence
float confidence
Confidence in [0, 1] (1 = full, 0 = none).
Definition: ObjectPose.h:98
armarx::TreeWidgetBuilder::setCompareFn
void setCompareFn(CompareFn compareFn)
Definition: TreeWidgetBuilder.h:45
armarx::core::time::DateTime::toDateTimeString
std::string toDateTimeString() const
Definition: DateTime.cpp:81
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::objpose::ObjectPose
An object pose as stored by the ObjectPoseStorage.
Definition: ObjectPose.h:36
armarx::ObjectID::str
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition: ObjectID.cpp:55
armarx::objpose::ObjectPose::timestamp
DateTime timestamp
Source timestamp.
Definition: ObjectPose.h:100
armarx::SimpleConfigDialog
A config-dialog containing one (or multiple) proxy finders.
Definition: SimpleConfigDialog.h:84