12#include <SimoxUtility/color/cmaps.h>
13#include <SimoxUtility/math/SoftMinMax.h>
16#include <RobotAPI/libraries/armem/aron/MemoryID.aron.generated.h>
31 splitter(new QSplitter(Qt::Orientation::Vertical)),
tree(new QTreeWidget(this))
35 QLayout* layout =
new QVBoxLayout();
36 this->setLayout(layout);
38 layout->setContentsMargins(margin, margin, margin, margin);
48 tree->setColumnCount(columns.size());
49 tree->setHeaderLabels(columns);
57 tree->setContextMenuPolicy(Qt::CustomContextMenu);
59 &QTreeWidget::customContextMenuRequested,
86 child->addWidget(dataView);
108 QTreeWidgetItem* item =
new QTreeWidgetItem({
"(No data.)"});
137 statusLabel->setText(QString::fromStdString(message));
141 std::optional<aron::Path>
144 QStringList qpath = item->data(
int(
Columns::KEY), Qt::UserRole).toStringList();
156 std::optional<MemoryID>
169 element =
data->navigateAbsolute(elementPath);
177 catch (
const armarx::LocalException& e)
183 std::stringstream couldNotParseMsg;
184 couldNotParseMsg <<
"Element " << elementPath.
toString()
185 <<
" could not be parsed as MemoryID.";
187 auto dictElement = std::dynamic_pointer_cast<aron::data::Dict>(element);
190 showErrorMessage(couldNotParseMsg.str() +
" (Failed to cast to DictNavigator.)");
196 arondto::MemoryID
dto;
197 dto.fromAron(dictElement);
211 DataView::makeActionResolveMemoryID(
const MemoryID&
id)
213 auto* action =
new QAction(
"Resolve memory ID");
215 if (not(
id.hasEntityName() and
id.isWellDefined()))
217 action->setDisabled(
true);
218 action->setText(action->text() +
" (incomplete Memory ID)");
234 QAction* action =
new QAction(
"Copy memory ID to clipboard");
240 const QString idStr = QString::fromStdString(
id.
str());
243 QClipboard* clipboard = QApplication::clipboard();
244 clipboard->setText(idStr);
245 QApplication::processEvents();
251 std::vector<QAction*>
252 DataView::makeActionsCopyDataToClipboard()
262 std::vector<QAction*>
263 DataView::makeActionsCopyDataToClipboard(
const aron::Path& path)
282 catch (
const aron::error::AronException& e)
287 return makeCopyActions(element, elementType);
289 catch (
const aron::error::AronException& e)
291 ARMARX_WARNING <<
"Could not convert Aron data to JSON: " << e.getReason();
296 std::vector<QAction*>
300 auto* easyJsonAction =
new QAction(
"Copy data to clipboard as easy JSON");
301 connect(easyJsonAction,
303 [
this, element, elementType]()
307 TreeTypedJSONConverter conv;
309 QClipboard* clipboard = QApplication::clipboard();
310 clipboard->setText(QString::fromStdString(conv.getJSON().dump(2)));
311 QApplication::processEvents();
313 catch (
const aron::error::AronException& e)
315 ARMARX_WARNING <<
"Could not convert Aron data to JSON: " << e.getReason();
319 auto* aronJsonAction =
new QAction(
"Copy data to clipboard as aron JSON");
320 connect(aronJsonAction,
326 nlohmann::json json =
329 QClipboard* clipboard = QApplication::clipboard();
330 clipboard->setText(QString::fromStdString(json.dump(2)));
331 QApplication::processEvents();
333 catch (
const aron::error::AronException& e)
335 ARMARX_WARNING <<
"Could not convert Aron data to JSON: " << e.getReason();
339 return {easyJsonAction, aronJsonAction};
345 QMenu* menu =
new QMenu(
this);
347 const QTreeWidgetItem* item =
tree->itemAt(pos);
355 auto actions = makeActionsCopyDataToClipboard();
356 for (
const auto& action :
actions)
360 menu->addAction(action);
373 QAction* viewAction =
new QAction(
"Show image");
374 menu->addAction(viewAction);
386 const std::vector<int> shape = imageData->getShape();
387 if (std::find(shape.begin(), shape.end(), 0) != shape.end())
389 viewAction->setText(viewAction->text() +
" (image is empty)");
390 viewAction->setEnabled(
false);
397 catch (
const armarx::LocalException&)
408 const std::string typeName = item->text(
int(
Columns::TYPE)).toStdString();
417 menu->addAction(action);
419 if (QAction* action = makeActionResolveMemoryID(
id.value()))
421 menu->addAction(action);
427 const std::optional<aron::Path> elementPath =
getElementPath(item);
430 auto actions = makeActionsCopyDataToClipboard(elementPath.value());
431 for (
const auto& action :
actions)
435 menu->addAction(action);
447 if (menu->actions().isEmpty())
491 const std::vector<int> shape =
aron.getShape();
493 ARMARX_CHECK_EQUAL(shape.at(2), 4) <<
"Expected Depth32 image to have 4 bytes per pixel.";
495 const int rows = shape.at(0);
496 const int cols = shape.at(1);
500 QImage image(cols, rows, QImage::Format::Format_RGB32);
501 const float*
data =
reinterpret_cast<float*
>(
aron.getData());
503 auto updateLimits = [](
float value,
Limits& limits)
507 limits.min = std::min(limits.min, value);
509 limits.max = std::max(limits.max, value);
516 const float* sourceRow =
data;
517 for (
int row = 0; row < rows; ++row)
519 for (
int col = 0; col < cols; ++col)
521 float value = sourceRow[col];
522 updateLimits(value, limits);
540 cmap.set_vlimits(softMin.getSoftMin(), softMax.getSoftMax());
545 const float* sourceRow =
data;
547 const int bytesPerLine = image.bytesPerLine();
548 uchar* targetRow = image.bits();
550 for (
int row = 0; row < rows; ++row)
552 for (
int col = 0; col < cols; ++col)
554 float value = sourceRow[col];
555 simox::Color color = value <= 0 ? simox::Color::white() :
cmap(value);
556 targetRow[col * 4 + 0] = color.b;
557 targetRow[col * 4 + 1] = color.g;
558 targetRow[col * 4 + 2] = color.r;
559 targetRow[col * 4 + 3] = color.a;
561 updateLimits(value, limits);
564 targetRow += bytesPerLine;
603 catch (
const armarx::LocalException&)
610 NDArray::PointerType imageData = NDArray::DynamicCast(element);
614 simox::meta::get_type_name(element));
618 const std::vector<int> shape = imageData->getShape();
619 if (shape.size() != 3)
622 NDArray::DimensionsToString(shape));
625 const int rows = shape.at(0);
626 const int cols = shape.at(1);
628 using aron::type::image::PixelType;
629 std::optional<PixelType> pixelType;
636 pixelType = (imageData->getType() ==
"5" ? PixelType::DEPTH32 : PixelType::RGB24);
642 bool clearLimitsHistory =
true;
643 std::optional<QImage> image;
646 switch (pixelType.value())
648 case PixelType::RGB24:
650 <<
"Expected Rgb24 image to have 3 bytes per pixel.";
651 image = QImage(imageData->getData(), cols, rows, QImage::Format::Format_RGB888);
654 case PixelType::DEPTH32:
655 image =
imageView->convertDepth32ToRGB32(*imageData);
656 clearLimitsHistory =
false;
662 QImage::Format format = QImage::Format_Invalid;
666 format = QImage::Format::Format_Grayscale8;
670 format = QImage::Format::Format_RGB888;
674 showErrorMessage(
"Expected 1 or 3 elements in last dimension, but got shape: " +
675 NDArray::DimensionsToString(shape));
678 image = QImage(imageData->getData(), cols, rows, format);
683 std::stringstream title;
684 title <<
"Image element '" <<
imageView->elementPath.toString()
686 imageView->setTitle(QString::fromStdString(title.str()));
687 imageView->view->setImage(image.value());
689 if (clearLimitsHistory)
698 setLayout(
new QHBoxLayout());
700 layout()->setContentsMargins(margin, margin, margin, margin);
703 QFont font = this->font();
704 font.setPointSizeF(font.pointSize() * 0.75);
709 layout()->addWidget(
view);
void setTag(const LogTag &tag)
void setColumns(int key, int value, int type)
void updateTree(QTreeWidgetItem *parent, const aron::data::DictPtr &data)
const size_t limitsHistoryMaxSize
In this context, n.
instance::ImageView * view
QImage convertDepth32ToRGB32(const aron::data::NDArray &aron)
simox::ColorMap cmap
Color map to visualize depth images.
std::deque< Limits > limitsHistory
History over first n extremal depth values used to calibrate the colormap.
void setUseTypeInfo(bool enable)
void memoryIdResolutionRequested(const MemoryID &id)
void addDataView(DataView *dataView)
QAction * makeActionCopyMemoryID(const MemoryID &id)
void actionsMenuRequested(const MemoryID &memoryID, QWidget *parent, const QPoint &pos, QMenu *menu)
virtual void updateData(const aron::data::DictPtr &data, aron::type::ObjectPtr aronType=nullptr)
virtual aron::data::DictPtr getData()=0
void showErrorMessage(const std::string &message)
static std::optional< aron::Path > getElementPath(const QTreeWidgetItem *item)
void updateImageView(const aron::data::DictPtr &data)
void showImageView(const aron::Path &elementPath)
aron::type::ObjectPtr currentAronType
virtual QMenu * buildActionsMenu(const QPoint &pos)
void setStatusLabel(QLabel *statusLabel)
QTreeWidgetItem * treeItemData
void useTypeInfoChanged(bool enable)
std::optional< MemoryID > getElementMemoryID(const aron::Path &elementPath)
virtual void prepareTreeContextMenu(const QPoint &pos)
A widget drawing an image in itself.
void updateTree(QTreeWidgetItem *parent, const aron::type::Dict &type, const aron::data::Dict &data)
std::string toString() const
static nlohmann::json ConvertToNlohmannJSON(const data::VariantPtr &)
static PointerType DynamicCast(const VariantPtr &n)
A base class for aron exceptions.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
aron::Path deserializePath(const QStringList &qpath)
std::string sanitizeTypeName(const std::string &typeName)
const std::string sanitizedMemoryIDTypeName
void fromAron(const arondto::MemoryID &dto, MemoryID &bo)
std::shared_ptr< Dict > DictPtr
std::shared_ptr< Variant > VariantPtr
void visitRecursive(RecursiveVisitorImplementation &v, typename RecursiveVisitorImplementation::Input &o)
std::shared_ptr< Object > ObjectPtr
std::shared_ptr< Variant > VariantPtr
void clearItem(QTreeWidgetItem *item)
Clear a tree widget item.
int toInt(const std::string &input)