ADTF  3.18.2
Source Code for Qt5 Substream JSON Display Plugin (BETA)
Location
./src/examples/src/adtf/filters/qt/substream_json_filter/
Namespace for entire ADTF SDK.
Build Environment
To see how to set up the build environment have a look at ADTF CMake Environment
This implementation shows:
  • how to extend the sec_substream_display
Header
/*
* This file depends on Qt which is licensed under LGPLv3.
* See ADTF_DIR/3rdparty/qt5 and doc/license for detailed information.
*/
#pragma once
#include <adtfqtsubstreamdisplaysdk/substream_display_intf.h>
#include <adtf_ui.h>
#include <memory>
#include <QDragEnterEvent>
#include <QLineEdit>
#include <QObject>
#include <QTextBrowser>
#include <QWidget>
using namespace ddl;
using namespace adtf::base;
using namespace adtf::filter;
using namespace adtf::mediadescription;
using namespace adtf::streaming;
using namespace adtf::ucom;
using namespace adtf::ui;
using namespace adtf::util;
using namespace adtf::substreamdisplay;
// IDragDestination
class IDragDestination
{
public:
virtual bool AcceptsSubstreamName(const QString& strSubstreamName) const = 0;
};
// cDroppableTextBrowser
class cDroppableTextBrowser : public QTextBrowser
{
Q_OBJECT
signals:
void dropped(const QString& strPath);
public:
cDroppableTextBrowser(const IDragDestination& oDragDestination);
private:
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
private:
const IDragDestination& m_oDragDestination;
};
// cQtSubstreamDisplayJsonFilter
class cQtSubstreamDisplayJsonFilter : public QObject, public adtf::ui::cQtUIFilter, public IDragDestination
{
Q_OBJECT
public:
ADTF_CLASS_ID_NAME(cQtSubstreamDisplayJsonFilter,
"qt_substream_json.ui_filter.adtf.cid",
"Qt5 Substream JSON Display (BETA)");
signals:
void newSubstreamSample(const QString& strSampleJson);
public:
cQtSubstreamDisplayJsonFilter();
tResult Init(tInitStage eStage) override;
bool AcceptsSubstreamName(const QString& strSubstreamName) const override;
private:
QWidget* CreateView() override;
void ReleaseView() override;
tResult OnTimer() override;
tResult AcceptType(ISampleReader* pReader, const iobject_ptr<const IStreamType>& pStreamType) override;
tResult ProcessInput(ISampleReader* pReader, const iobject_ptr<const ISample>& pSample) override;
void SetFilteredSubstreamName(const std::string& strFilteredSubstreamName);
std::string DecodeToJson(const cSampleDecoder& oDecoder, const iobject_ptr<const ISample>& pSample);
void UpdateUi();
private:
static constexpr auto DISPLAY_TYPE = "JSON Display";
static constexpr auto DISPLAY_BASE_NAME = "JSON Display";
std::recursive_mutex m_oMutex;
struct cSubstreamDef
{
uint32_t m_nId;
std::string m_strName;
object_ptr<const IStreamType> m_pStreamType;
};
std::vector<std::shared_ptr<cSubstreamDef>> m_vecSubstreamDefs;
std::optional<std::string> m_strFilteredSubstreamName;
std::optional<cSampleCodecFactory> m_oFilteredCodecFactory;
cPinReader* m_pReader = nullptr;
object_ptr<IStreamingRequest> m_pRequest;
interface_client<IDisplayServer> m_oClient;
std::shared_ptr<QWidget> m_pMainWidget;
QLineEdit* m_pSubstreamNameLineEdit = nullptr;
cDroppableTextBrowser* m_pSampleJsonTextBrowser = nullptr;
};
Copyright © Audi Electronics Venture GmbH.
#define REQUIRE_INTERFACE(_interface)
Macro usable with ADTF_CLASS_DEPENDENCIES() to require mandatory interfaces.
#define ADTF_CLASS_DEPENDENCIES(...)
Add interface ids (string literals,.
#define ADTF_CLASS_ID_NAME(_class, _strcid, _strclabel)
Common macro to enable correct treatment of class identifier AND Class Name by IClassInfo.
Definition: class_id.h:33
Helper class that wraps a streaming::ant::IBindingClient.
Definition: graph_object.h:67
Interface definition for the ADTF XSystem based on Qt.
Namespace for the ADTF Base SDK.
adtf::streaming::ant::cDynamicSampleReader cPinReader
use cSampleReader as cPinReader
Namespace for the ADTF Filter SDK.
Namespace for the ADTF Media Description SDK.
Namespace for the ADTF Streaming SDK.
Namespace for the ADTF uCOM3 SDK.
qt_ui_filter< adtf::filter::ant::cFilter > cQtUIFilter
UI Filter basic implementation which has static pins only and supports the IDataBinding.
Definition: qt_ui_filter.h:98
Namespace for the ADTF UI SDK.
alias namespace for the A_UTILS Library.
Implementation
/*
* This file depends on Qt which is licensed under LGPLv3.
* See ADTF_DIR/3rdparty/qt5 and doc/license for detailed information.
*/
#include "substream_json_filter.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QMimeData>
ADTF_PLUGIN("Qt5 Substream JSON Display Plugin (BETA)", cQtSubstreamDisplayJsonFilter)
namespace
{
// Can be used when direct initializing a structure is not possible e.g. when using std::make_shared.
// The arguments are forwarded to the direct initializer
template<typename T>
struct struct_initializer : public T
{
template<typename... Args>
struct_initializer(Args&&... args)
: T {std::forward<Args>(args)...}
{}
};
std::string substream_path_to_substream_name(const std::string& strPath)
{
// removing prefix because it doesn't belong to real substream path
auto lstPathTokens = QString::fromStdString(strPath).split(".", Qt::KeepEmptyParts);
if (lstPathTokens.size() >= 1)
{
lstPathTokens.removeFirst();
const auto strSubstreamName = lstPathTokens.join(".");
return strSubstreamName.toStdString();
}
return {};
}
} // namespace
// cDroppableTextBrowser
cDroppableTextBrowser::cDroppableTextBrowser(const IDragDestination& oDragDestination) :
m_oDragDestination(oDragDestination)
{}
void cDroppableTextBrowser::dragEnterEvent(QDragEnterEvent* event)
{
const QMimeData* pMimeData = event->mimeData();
if (pMimeData->hasText())
{
const auto strJson = pMimeData->text();
const auto oJsonDocument = QJsonDocument::fromJson(strJson.toUtf8());
const auto strPath = oJsonDocument.object()["path"].toString();
const auto strSubstreamName = QString::fromStdString(substream_path_to_substream_name(strPath.toStdString()));
if (m_oDragDestination.AcceptsSubstreamName(strSubstreamName))
{
event->acceptProposedAction();
}
}
}
void cDroppableTextBrowser::dragMoveEvent(QDragMoveEvent* event)
{
event->acceptProposedAction();
}
void cDroppableTextBrowser::dropEvent(QDropEvent* event)
{
const QMimeData* pMimeData = event->mimeData();
if (pMimeData->hasText())
{
const auto strJson = pMimeData->text();
const auto oJsonDocument = QJsonDocument::fromJson(strJson.toUtf8());
const auto strPath = oJsonDocument.object()["path"].toString();
emit dropped(strPath);
}
}
// cQtSubstreamDisplayJsonFilter
cQtSubstreamDisplayJsonFilter::cQtSubstreamDisplayJsonFilter()
{
setObjectName("Substream Display Json Filter");
// sets a short description for the component
SetDescription("Use this filter to visualize the sample data of a received input which match the substream name/ID.");
// set help link to jump to documentation from ADTF Configuration Editor
set_help_link(*this, "$(ADTF_DIR)/doc/adtf_html/page_substream_json_display.html#sec_substream_json_display");
m_oDisplayServer = CreateInterfaceClient<adtf::substreamdisplay::IDisplayServer>("display_server");
set_description(*this, "display_server", "display server object from Qt5 Substream Display.");
m_pReader = CreateInputPin("input", stream_meta_type_substreams());
set_description(*this, "input", "Incoming data of substreams.");
}
tResult cQtSubstreamDisplayJsonFilter::Init(tInitStage eStage)
{
const auto fnGetAdtfMainWindow = []()
{
QWidget* pAdtfMainWindow = nullptr;
const auto lstTopLevelWidgets = qApp->topLevelWidgets();
for (const auto pTopLevelWidget : lstTopLevelWidgets)
{
const auto strClassName = QString(pTopLevelWidget->metaObject()->className());
if (strClassName == "cMainADTFWindow")
{
pAdtfMainWindow = pTopLevelWidget;
break;
}
}
return pAdtfMainWindow;
};
//
RETURN_IF_FAILED(cQtUIFilter::Init(eStage));
if (eStage == StagePostConnect)
{
if (m_oDisplayServer.IsValid())
{
const auto pDisplayHandler = m_oDisplayServer->GetServer()->RegisterDisplaySingleton(DISPLAY_TYPE, DISPLAY_BASE_NAME, {} /*every metatype*/);
connect(pDisplayHandler, &cDisplayHandler::showInDisplay, [this, &fnGetAdtfMainWindow](const QString& strPath)
{
auto strSubstreamName = ISubstreamItemData::GetPathComponents(strPath).strSubStreamName;
if (AcceptsSubstreamName(strSubstreamName))
{
SetFilteredSubstreamName(strSubstreamName.toStdString());
UpdateUi();
}
else
{
const auto pAdtfMainWindow = fnGetAdtfMainWindow();
QMessageBox::warning(pAdtfMainWindow,
tr("Showing substream"),
QString(tr("The substream / component '%1' cannot be shown in the display '%2'.")
.arg(strPath)
.arg(DISPLAY_BASE_NAME)));
}
});
QObject::connect(pDisplayHandler, &cDisplayHandler::closeDisplay, []()
{
// nothing todo because singleton
});
QObject::connect(m_pSampleJsonTextBrowser, &cDroppableTextBrowser::dropped, [this](const QString& strPath)
{
const auto strName = substream_path_to_substream_name(strPath.toStdString());
SetFilteredSubstreamName(strName);
UpdateUi();
});
}
}
}
bool cQtSubstreamDisplayJsonFilter::AcceptsSubstreamName(const QString& strSubstreamName) const
{
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[&strSubstreamName](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_strName == strSubstreamName.toStdString(); });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto oSubstreamDef = *itSubstreamDef;
try
{
const cSampleCodecFactory oCodecFactory(oSubstreamDef->m_pStreamType);
if (IS_OK(oCodecFactory.IsValid()))
{
return true;
}
}
// cSampleCodecFactory's constructor throws exception when stream type (regular: md_struct) can not be processed
catch (...)
{}
}
return false;
}
QWidget* cQtSubstreamDisplayJsonFilter::CreateView()
{
m_pMainWidget = std::make_shared<QWidget>();
const auto pMainLayout = new QVBoxLayout;
m_pMainWidget->setLayout(pMainLayout);
pMainLayout->setMargin(0);
pMainLayout->setSpacing(3);
const auto pHeadLayout = new QHBoxLayout;
pMainLayout->addLayout(pHeadLayout);
pHeadLayout->addWidget(new QLabel(tr("Substream Name:")));
m_pSubstreamNameLineEdit = new QLineEdit;
m_pSubstreamNameLineEdit->setObjectName("json_filter_substream_name_lineedit");
pHeadLayout->addWidget(m_pSubstreamNameLineEdit);
m_pSubstreamNameLineEdit->setSizePolicy(QSizePolicy::MinimumExpanding, m_pSubstreamNameLineEdit->sizePolicy().verticalPolicy());
m_pSubstreamNameLineEdit->setReadOnly(true);
m_pSubstreamNameLineEdit->setStyleSheet("QLineEdit { background-color: lightgray; }");
pMainLayout->addWidget(new QLabel(tr("Substream Content (JSON)")));
m_pSampleJsonTextBrowser = new cDroppableTextBrowser(*this);
m_pSampleJsonTextBrowser->setObjectName("json_filter_sample_json_text_browser");
pMainLayout->addWidget(m_pSampleJsonTextBrowser);
connect(this, &cQtSubstreamDisplayJsonFilter::newSubstreamSample, this,
[this](QString strSampleJson)
{ m_pSampleJsonTextBrowser->setText(strSampleJson); },
Qt::QueuedConnection);
return m_pMainWidget.get();
}
void cQtSubstreamDisplayJsonFilter::ReleaseView()
{
m_pMainWidget.reset();
}
tResult cQtSubstreamDisplayJsonFilter::OnTimer()
{
}
tResult cQtSubstreamDisplayJsonFilter::AcceptType(ISampleReader* pReader, const iobject_ptr<const IStreamType>& pStreamType)
{
RETURN_IF_FAILED(cFilter::AcceptType(pReader, pStreamType));
stream_meta_type_substreams::ListSubStreams(*pStreamType.Get(), [&](const char* strName, uint32_t nSubStreamId)
{
auto pSubStreamType = stream_meta_type_substreams::GetSubStreamType(*pStreamType.Get(), strName);
const auto pSubstreamDef = std::make_shared<struct_initializer<cSubstreamDef>>(nSubStreamId, strName, pSubStreamType);
m_vecSubstreamDefs.push_back(pSubstreamDef);
});
}
tResult cQtSubstreamDisplayJsonFilter::ProcessInput(ISampleReader* pReader, const iobject_ptr<const ISample>& pSample)
{
Q_UNUSED(pReader);
std::lock_guard<std::recursive_mutex> oLock(m_oMutex);
const auto nSubstreamId = get_sample_substream_id(pSample);
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[nSubstreamId](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_nId == nSubstreamId; });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto pSubstreamDef = *itSubstreamDef;
if (m_strFilteredSubstreamName.has_value() && pSubstreamDef->m_strName == *m_strFilteredSubstreamName)
{
if (m_oFilteredCodecFactory.has_value() && IS_OK(m_oFilteredCodecFactory->IsValid()))
{
const auto oDecoder = m_oFilteredCodecFactory->MakeDecoderFor(pSample);
const auto strJson = DecodeToJson(oDecoder, pSample);
emit newSubstreamSample(QString::fromStdString(strJson));
}
else
{
emit newSubstreamSample({});
}
}
}
}
void cQtSubstreamDisplayJsonFilter::SetFilteredSubstreamName(const std::string& strFilteredSubstreamName)
{
m_strFilteredSubstreamName = strFilteredSubstreamName;
m_oFilteredCodecFactory.reset();
if (m_pSampleJsonTextBrowser != nullptr)
{
m_pSampleJsonTextBrowser->clear();
}
const auto itSubstreamDef = std::find_if(std::begin(m_vecSubstreamDefs), std::end(m_vecSubstreamDefs),
[&strFilteredSubstreamName](const std::shared_ptr<cSubstreamDef>& pSubstreamDef)
{ return pSubstreamDef->m_strName == strFilteredSubstreamName; });
if (itSubstreamDef != std::end(m_vecSubstreamDefs))
{
const auto oSubstreamDef = *itSubstreamDef;
try
{
const cSampleCodecFactory oCodecFactory(oSubstreamDef->m_pStreamType);
if (IS_OK(oCodecFactory.IsValid()))
{
m_strFilteredSubstreamName = strFilteredSubstreamName;
m_oFilteredCodecFactory = std::move(oCodecFactory);
}
m_pReader->RequestSamples(m_pRequest, oSubstreamDef->m_nId);
}
// cSampleCodecFactory's constructor throws exception when stream type (regular: md_struct) can not be processed
catch(...)
{}
}
UpdateUi();
}
std::string cQtSubstreamDisplayJsonFilter::DecodeToJson(const cSampleDecoder& oDecoder, const iobject_ptr<const ISample>& pSample)
{
adtf::util::cString strJSONObject = adtf::util::cString::Format("{\n \"timestamp\": %" PRIi64 ",\n \"data\":\n {", pSample->GetTime());
size_t nElementIndex = 0;
for_each_leaf_element(oDecoder.GetElements(),
[&strJSONObject, &nElementIndex](const auto& oElement)
{
strJSONObject.Append(adtf::util::cString::Format("%s\n \"%s\": %s",
nElementIndex > 0 ? "," : "",
oElement.getFullName().c_str(),
oElement.getStringValue().c_str()));
++nElementIndex;
});
strJSONObject.Append("\n }\n}");
return strJSONObject.GetPtr();
}
void cQtSubstreamDisplayJsonFilter::UpdateUi()
{
m_pSubstreamNameLineEdit->clear();
if (m_strFilteredSubstreamName.has_value() && m_pSubstreamNameLineEdit != nullptr)
{
m_pSubstreamNameLineEdit->setText(QString::fromStdString(*m_strFilteredSubstreamName));
}
}
#define ADTF_PLUGIN(__plugin_identifier,...)
The ADTF Plugin Macro will add the code of a adtf::ucom::ant::IPlugin implementation.
Definition: adtf_plugin.h:22
#define RETURN_IF_FAILED(s)
Return if expression is failed, which requires the calling function's return type to be tResult.
#define RETURN_NOERROR
Return status ERR_NOERROR, which requires the calling function's return type to be tResult.
string_base< cStackString > cString
cString implementation for a stack string which works on stack if string is lower than A_UTILS_DEFAUL...
Definition: string.h:2778
void for_each_leaf_element(ElementsType &oElements, const element_callback< ElementsType > &fnCallback)
Iterates ALL leaf elements within ALL array elements.
void set_description(base::ant::IConfiguration &oConfig, const char *strDescription)
Sets description information.
void set_help_link(base::ant::IConfiguration &oConfig, const char *strUrl)
Set the link to the corresponding help/documentation.
uint32_t get_sample_substream_id(const ant::ISample &oSample)
Copyright © Audi Electronics Venture GmbH.